Skip to content

Commit 2e66aca

Browse files
sapiersapier
sapier
authored and
sapier
committedNov 29, 2013
Fix modstore/favourites hang by adding asynchronous lua job support
1 parent b08d755 commit 2e66aca

27 files changed

+2271
-559
lines changed
 

‎builtin/async_env.lua

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
engine.log("info","Initializing Asynchronous environment")
2+
3+
dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua")
4+
5+
function engine.job_processor(serialized_function, serialized_data)
6+
7+
local fct = marshal.decode(serialized_function)
8+
local params = marshal.decode(serialized_data)
9+
local retval = marshal.encode(nil)
10+
11+
if fct ~= nil and type(fct) == "function" then
12+
local result = fct(params)
13+
retval = marshal.encode(result)
14+
else
15+
engine.log("error","ASYNC WORKER: unable to deserialize function")
16+
end
17+
18+
return retval,retval:len()
19+
end

‎builtin/async_event.lua

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
local tbl = engine or minetest
2+
3+
tbl.async_jobs = {}
4+
5+
if engine ~= nil then
6+
function tbl.async_event_handler(jobid, serialized_retval)
7+
local retval = nil
8+
if serialized_retval ~= "ERROR" then
9+
retval= marshal.decode(serialized_retval)
10+
else
11+
tbl.log("error","Error fetching async result")
12+
end
13+
14+
assert(type(tbl.async_jobs[jobid]) == "function")
15+
tbl.async_jobs[jobid](retval)
16+
tbl.async_jobs[jobid] = nil
17+
end
18+
else
19+
20+
minetest.register_globalstep(
21+
function(dtime)
22+
local list = tbl.get_finished_jobs()
23+
24+
for i=1,#list,1 do
25+
local retval = marshal.decode(list[i].retval)
26+
27+
assert(type(tbl.async_jobs[jobid]) == "function")
28+
tbl.async_jobs[list[i].jobid](retval)
29+
tbl.async_jobs[list[i].jobid] = nil
30+
end
31+
end)
32+
end
33+
34+
function tbl.handle_async(fct, parameters, callback)
35+
36+
--serialize fct
37+
local serialized_fct = marshal.encode(fct)
38+
39+
assert(marshal.decode(serialized_fct) ~= nil)
40+
41+
--serialize parameters
42+
local serialized_params = marshal.encode(parameters)
43+
44+
if serialized_fct == nil or
45+
serialized_params == nil or
46+
serialized_fct:len() == 0 or
47+
serialized_params:len() == 0 then
48+
return false
49+
end
50+
51+
local jobid = tbl.do_async_callback( serialized_fct,
52+
serialized_fct:len(),
53+
serialized_params,
54+
serialized_params:len())
55+
56+
tbl.async_jobs[jobid] = callback
57+
58+
return true
59+
end

‎builtin/mainmenu.lua

+181-150
Large diffs are not rendered by default.

‎builtin/modmgr.lua

+166-166
Large diffs are not rendered by default.

‎builtin/modstore.lua

+238-107
Large diffs are not rendered by default.

‎doc/menu_lua_api.txt

+37-20
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ engine.close()
3333
Filesystem:
3434
engine.get_scriptdir()
3535
^ returns directory of script
36-
engine.get_modpath()
36+
engine.get_modpath() (possible in async calls)
3737
^ returns path to global modpath
38-
engine.get_modstore_details(modid)
38+
engine.get_modstore_details(modid) (possible in async calls)
3939
^ modid numeric id of mod in modstore
4040
^ returns {
4141
id = <numeric id of mod in modstore>,
@@ -47,27 +47,29 @@ engine.get_modstore_details(modid)
4747
license = <short description of license>,
4848
rating = <float value of current rating>
4949
}
50-
engine.get_modstore_list()
50+
engine.get_modstore_list() (possible in async calls)
5151
^ returns {
5252
[1] = {
5353
id = <numeric id of mod in modstore>,
5454
title = <human readable title>,
5555
basename = <basename for mod>
5656
}
5757
}
58-
engine.get_gamepath()
58+
engine.get_gamepath() (possible in async calls)
5959
^ returns path to global gamepath
60-
engine.get_dirlist(path,onlydirs)
60+
engine.get_texturepath() (possible in async calls)
61+
^ returns path to default textures
62+
engine.get_dirlist(path,onlydirs) (possible in async calls)
6163
^ path to get subdirs from
6264
^ onlydirs should result contain only dirs?
6365
^ returns list of folders within path
64-
engine.create_dir(absolute_path)
66+
engine.create_dir(absolute_path) (possible in async calls)
6567
^ absolute_path to directory to create (needs to be absolute)
6668
^ returns true/false
67-
engine.delete_dir(absolute_path)
69+
engine.delete_dir(absolute_path) (possible in async calls)
6870
^ absolute_path to directory to delete (needs to be absolute)
6971
^ returns true/false
70-
engine.copy_dir(source,destination,keep_soure)
72+
engine.copy_dir(source,destination,keep_soure) (possible in async calls)
7173
^ source folder
7274
^ destination folder
7375
^ keep_source DEFAULT true --> if set to false source is deleted after copying
@@ -76,11 +78,11 @@ engine.extract_zip(zipfile,destination) [unzip within path required]
7678
^ zipfile to extract
7779
^ destination folder to extract to
7880
^ returns true/false
79-
engine.download_file(url,target)
81+
engine.download_file(url,target) (possible in async calls)
8082
^ url to download
8183
^ target to store to
8284
^ returns true/false
83-
engine.get_version()
85+
engine.get_version() (possible in async calls)
8486
^ returns current minetest version
8587
engine.sound_play(spec, looped) -> handle
8688
^ spec = SimpleSoundSpec (see lua-api.txt)
@@ -105,10 +107,10 @@ engine.get_game(index)
105107
DEPRECATED:
106108
addon_mods_paths = {[1] = <path>,},
107109
}
108-
engine.get_games() -> table of all games in upper format
110+
engine.get_games() -> table of all games in upper format (possible in async calls)
109111

110112
Favorites:
111-
engine.get_favorites(location) -> list of favorites
113+
engine.get_favorites(location) -> list of favorites (possible in async calls)
112114
^ location: "local" or "online"
113115
^ returns {
114116
[1] = {
@@ -128,21 +130,21 @@ engine.get_favorites(location) -> list of favorites
128130
engine.delete_favorite(id, location) -> success
129131

130132
Logging:
131-
engine.debug(line)
133+
engine.debug(line) (possible in async calls)
132134
^ Always printed to stderr and logfile (print() is redirected here)
133-
engine.log(line)
134-
engine.log(loglevel, line)
135+
engine.log(line) (possible in async calls)
136+
engine.log(loglevel, line) (possible in async calls)
135137
^ loglevel one of "error", "action", "info", "verbose"
136138

137139
Settings:
138140
engine.setting_set(name, value)
139-
engine.setting_get(name) -> string or nil
141+
engine.setting_get(name) -> string or nil (possible in async calls)
140142
engine.setting_setbool(name, value)
141-
engine.setting_getbool(name) -> bool or nil
143+
engine.setting_getbool(name) -> bool or nil (possible in async calls)
142144
engine.setting_save() -> nil, save all settings to config file
143145

144146
Worlds:
145-
engine.get_worlds() -> list of worlds
147+
engine.get_worlds() -> list of worlds (possible in async calls)
146148
^ returns {
147149
[1] = {
148150
path = <full path to world>,
@@ -174,17 +176,32 @@ engine.gettext(string) -> string
174176
fgettext(string, ...) -> string
175177
^ call engine.gettext(string), replace "$1"..."$9" with the given
176178
^ extra arguments, call engine.formspec_escape and return the result
177-
engine.parse_json(string[, nullvalue]) -> something
179+
engine.parse_json(string[, nullvalue]) -> something (possible in async calls)
178180
^ see minetest.parse_json (lua_api.txt)
179181
dump(obj, dumped={})
180182
^ Return object serialized as a string
181183
string:split(separator)
182184
^ eg. string:split("a,b", ",") == {"a","b"}
183185
string:trim()
184186
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
185-
minetest.is_yes(arg)
187+
minetest.is_yes(arg) (possible in async calls)
186188
^ returns whether arg can be interpreted as yes
187189

190+
Async:
191+
engine.handle_async(async_job,parameters,finished)
192+
^ execute a function asynchronously
193+
^ async_job is a function receiving one parameter and returning one parameter
194+
^ parameters parameter table passed to async_job
195+
^ finished function to be called once async_job has finished
196+
^ the result of async_job is passed to this function
197+
198+
Limitations of Async operations
199+
-No access to global lua variables, don't even try
200+
-Limited set of available functions
201+
e.g. No access to functions modifying menu like engine.start,engine.close,
202+
engine.file_open_dialog
203+
204+
188205
Class reference
189206
----------------
190207
Settings: see lua_api.txt

‎src/guiEngine.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ void GUIEngine::run()
286286
cloudPostProcess();
287287
else
288288
sleep_ms(25);
289+
290+
m_script->Step();
289291
}
290292
}
291293

@@ -576,3 +578,9 @@ void GUIEngine::stopSound(s32 handle)
576578
{
577579
m_sound_manager->stopSound(handle);
578580
}
581+
582+
/******************************************************************************/
583+
unsigned int GUIEngine::DoAsync(std::string serialized_fct,
584+
std::string serialized_params) {
585+
return m_script->DoAsync(serialized_fct,serialized_params);
586+
}

‎src/guiEngine.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ class GUIEngine {
166166
return m_scriptdir;
167167
}
168168

169+
/** pass async callback to scriptengine **/
170+
unsigned int DoAsync(std::string serialized_fct,std::string serialized_params);
171+
169172
private:
170173

171174
/** find and run the main menu script */
@@ -244,7 +247,7 @@ class GUIEngine {
244247
* @param url url to download
245248
* @param target file to store to
246249
*/
247-
bool downloadFile(std::string url,std::string target);
250+
static bool downloadFile(std::string url,std::string target);
248251

249252
/** array containing pointers to current specified texture layers */
250253
video::ITexture* m_textures[TEX_LAYER_MAX];

‎src/jthread/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ if( UNIX )
22
set(JTHREAD_SRCS
33
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jmutex.cpp
44
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jthread.cpp
5+
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jsemaphore.cpp
56
PARENT_SCOPE)
67
else( UNIX )
78
set(JTHREAD_SRCS
89
${CMAKE_CURRENT_SOURCE_DIR}/win32/jmutex.cpp
910
${CMAKE_CURRENT_SOURCE_DIR}/win32/jthread.cpp
11+
${CMAKE_CURRENT_SOURCE_DIR}/win32/jsemaphore.cpp
1012
PARENT_SCOPE)
1113
endif( UNIX )

‎src/jthread/jsemaphore.h

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*/
19+
20+
#ifndef JSEMAPHORE_H_
21+
#define JSEMAPHORE_H_
22+
23+
#if defined(WIN32)
24+
#include <windows.h>
25+
#define MAX_SEMAPHORE_COUNT 1024
26+
#else
27+
#include <pthread.h>
28+
#include <semaphore.h>
29+
#endif
30+
31+
class JSemaphore {
32+
public:
33+
JSemaphore();
34+
~JSemaphore();
35+
JSemaphore(int initval);
36+
37+
void Post();
38+
void Wait();
39+
40+
int GetValue();
41+
42+
private:
43+
#if defined(WIN32)
44+
HANDLE m_hSemaphore;
45+
#else
46+
sem_t m_semaphore;
47+
#endif
48+
};
49+
50+
#endif /* JSEMAPHORE_H_ */

‎src/jthread/jthread.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class JThread
4343
JThread();
4444
virtual ~JThread();
4545
int Start();
46+
void Stop();
4647
int Kill();
4748
virtual void *Thread() = 0;
4849
bool IsRunning();
@@ -63,12 +64,12 @@ class JThread
6364
HANDLE threadhandle;
6465
#else // pthread type threads
6566
static void *TheThread(void *param);
66-
67+
6768
pthread_t threadid;
6869
#endif // WIN32
6970
void *retval;
7071
bool running;
71-
72+
7273
JMutex runningmutex;
7374
JMutex continuemutex,continuemutex2;
7475
bool mutexinit;

‎src/jthread/pthread/jsemaphore.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*/
19+
#include "jthread/jsemaphore.h"
20+
21+
JSemaphore::JSemaphore() {
22+
sem_init(&m_semaphore,0,0);
23+
}
24+
25+
JSemaphore::~JSemaphore() {
26+
sem_destroy(&m_semaphore);
27+
}
28+
29+
JSemaphore::JSemaphore(int initval) {
30+
sem_init(&m_semaphore,0,initval);
31+
}
32+
33+
void JSemaphore::Post() {
34+
sem_post(&m_semaphore);
35+
}
36+
37+
void JSemaphore::Wait() {
38+
sem_wait(&m_semaphore);
39+
}
40+
41+
int JSemaphore::GetValue() {
42+
43+
int retval = 0;
44+
sem_getvalue(&m_semaphore,&retval);
45+
46+
return retval;
47+
}
48+

‎src/jthread/pthread/jthread.cpp

+24-18
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ JThread::~JThread()
4242
Kill();
4343
}
4444

45+
void JThread::Stop() {
46+
runningmutex.Lock();
47+
running = false;
48+
runningmutex.Unlock();
49+
}
50+
4551
int JThread::Start()
4652
{
4753
int status;
@@ -65,35 +71,35 @@ int JThread::Start()
6571
}
6672
mutexinit = true;
6773
}
68-
74+
6975
runningmutex.Lock();
7076
if (running)
7177
{
7278
runningmutex.Unlock();
7379
return ERR_JTHREAD_ALREADYRUNNING;
7480
}
7581
runningmutex.Unlock();
76-
82+
7783
pthread_attr_t attr;
7884
pthread_attr_init(&attr);
7985
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
80-
86+
8187
continuemutex.Lock();
82-
status = pthread_create(&threadid,&attr,TheThread,this);
88+
status = pthread_create(&threadid,&attr,TheThread,this);
8389
pthread_attr_destroy(&attr);
8490
if (status != 0)
8591
{
8692
continuemutex.Unlock();
8793
return ERR_JTHREAD_CANTSTARTTHREAD;
8894
}
89-
95+
9096
/* Wait until 'running' is set */
91-
92-
runningmutex.Lock();
97+
98+
runningmutex.Lock();
9399
while (!running)
94100
{
95101
runningmutex.Unlock();
96-
102+
97103
struct timespec req,rem;
98104

99105
req.tv_sec = 0;
@@ -103,17 +109,17 @@ int JThread::Start()
103109
runningmutex.Lock();
104110
}
105111
runningmutex.Unlock();
106-
112+
107113
continuemutex.Unlock();
108-
114+
109115
continuemutex2.Lock();
110116
continuemutex2.Unlock();
111117
return 0;
112118
}
113119

114120
int JThread::Kill()
115121
{
116-
runningmutex.Lock();
122+
runningmutex.Lock();
117123
if (!running)
118124
{
119125
runningmutex.Unlock();
@@ -128,8 +134,8 @@ int JThread::Kill()
128134
bool JThread::IsRunning()
129135
{
130136
bool r;
131-
132-
runningmutex.Lock();
137+
138+
runningmutex.Lock();
133139
r = running;
134140
runningmutex.Unlock();
135141
return r;
@@ -138,7 +144,7 @@ bool JThread::IsRunning()
138144
void *JThread::GetReturnValue()
139145
{
140146
void *val;
141-
147+
142148
runningmutex.Lock();
143149
if (running)
144150
val = NULL;
@@ -157,17 +163,17 @@ void *JThread::TheThread(void *param)
157163
{
158164
JThread *jthread;
159165
void *ret;
160-
166+
161167
jthread = (JThread *)param;
162-
168+
163169
jthread->continuemutex2.Lock();
164170
jthread->runningmutex.Lock();
165171
jthread->running = true;
166172
jthread->runningmutex.Unlock();
167-
173+
168174
jthread->continuemutex.Lock();
169175
jthread->continuemutex.Unlock();
170-
176+
171177
ret = jthread->Thread();
172178

173179
jthread->runningmutex.Lock();

‎src/jthread/win32/jsemaphore.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*/
19+
#include "jthread/jsemaphore.h"
20+
21+
JSemaphore::JSemaphore() {
22+
m_hSemaphore = CreateSemaphore(
23+
0,
24+
0,
25+
MAX_SEMAPHORE_COUNT,
26+
0);
27+
}
28+
29+
JSemaphore::~JSemaphore() {
30+
CloseHandle(&m_hSemaphore);
31+
}
32+
33+
JSemaphore::JSemaphore(int initval) {
34+
m_hSemaphore = CreateSemaphore(
35+
0,
36+
initval,
37+
MAX_SEMAPHORE_COUNT,
38+
0);
39+
}
40+
41+
void JSemaphore::Post() {
42+
ReleaseSemaphore(
43+
m_hSemaphore,
44+
1,
45+
0);
46+
}
47+
48+
void JSemaphore::Wait() {
49+
WaitForSingleObject(
50+
m_hSemaphore,
51+
INFINITE);
52+
}
53+
54+
int JSemaphore::GetValue() {
55+
56+
long int retval = 0;
57+
ReleaseSemaphore(
58+
m_hSemaphore,
59+
0,
60+
&retval);
61+
62+
return retval;
63+
}
64+

‎src/jthread/win32/jthread.cpp

+22-16
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ JThread::~JThread()
4343
Kill();
4444
}
4545

46+
void JThread::Stop() {
47+
runningmutex.Lock();
48+
running = false;
49+
runningmutex.Unlock();
50+
}
51+
4652
int JThread::Start()
4753
{
4854
if (!mutexinit)
@@ -63,15 +69,15 @@ int JThread::Start()
6369
return ERR_JTHREAD_CANTINITMUTEX;
6470
} mutexinit = true;
6571
}
66-
72+
6773
runningmutex.Lock();
6874
if (running)
6975
{
7076
runningmutex.Unlock();
7177
return ERR_JTHREAD_ALREADYRUNNING;
7278
}
7379
runningmutex.Unlock();
74-
80+
7581
continuemutex.Lock();
7682
#ifndef _WIN32_WCE
7783
threadhandle = (HANDLE)_beginthreadex(NULL,0,TheThread,this,0,&threadid);
@@ -83,29 +89,29 @@ int JThread::Start()
8389
continuemutex.Unlock();
8490
return ERR_JTHREAD_CANTSTARTTHREAD;
8591
}
86-
92+
8793
/* Wait until 'running' is set */
8894

89-
runningmutex.Lock();
95+
runningmutex.Lock();
9096
while (!running)
9197
{
9298
runningmutex.Unlock();
9399
Sleep(1);
94100
runningmutex.Lock();
95101
}
96102
runningmutex.Unlock();
97-
103+
98104
continuemutex.Unlock();
99-
105+
100106
continuemutex2.Lock();
101107
continuemutex2.Unlock();
102-
108+
103109
return 0;
104110
}
105111

106112
int JThread::Kill()
107113
{
108-
runningmutex.Lock();
114+
runningmutex.Lock();
109115
if (!running)
110116
{
111117
runningmutex.Unlock();
@@ -121,8 +127,8 @@ int JThread::Kill()
121127
bool JThread::IsRunning()
122128
{
123129
bool r;
124-
125-
runningmutex.Lock();
130+
131+
runningmutex.Lock();
126132
r = running;
127133
runningmutex.Unlock();
128134
return r;
@@ -131,7 +137,7 @@ bool JThread::IsRunning()
131137
void *JThread::GetReturnValue()
132138
{
133139
void *val;
134-
140+
135141
runningmutex.Lock();
136142
if (running)
137143
val = NULL;
@@ -156,23 +162,23 @@ DWORD WINAPI JThread::TheThread(void *param)
156162
void *ret;
157163

158164
jthread = (JThread *)param;
159-
165+
160166
jthread->continuemutex2.Lock();
161167
jthread->runningmutex.Lock();
162168
jthread->running = true;
163169
jthread->runningmutex.Unlock();
164-
170+
165171
jthread->continuemutex.Lock();
166172
jthread->continuemutex.Unlock();
167-
173+
168174
ret = jthread->Thread();
169-
175+
170176
jthread->runningmutex.Lock();
171177
jthread->running = false;
172178
jthread->retval = ret;
173179
CloseHandle(jthread->threadhandle);
174180
jthread->runningmutex.Unlock();
175-
return 0;
181+
return 0;
176182
}
177183

178184
void JThread::ThreadStarted()

‎src/main.cpp

+60-51
Large diffs are not rendered by default.

‎src/script/lua_api/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ set(common_SCRIPT_LUA_API_SRCS
1616
${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp
1717
${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp
1818
${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp
19+
${CMAKE_CURRENT_SOURCE_DIR}/l_async_events.cpp
20+
${CMAKE_CURRENT_SOURCE_DIR}/marshall.c
1921
PARENT_SCOPE)
2022

2123
# Used by client only

‎src/script/lua_api/l_async_events.cpp

+396
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2013 sapier, <sapier AT gmx DOT net>
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*/
19+
20+
extern "C" {
21+
#include "lua.h"
22+
#include "lauxlib.h"
23+
#include "lualib.h"
24+
int luaopen_marshal(lua_State *L);
25+
}
26+
#include <stdio.h>
27+
28+
#include "l_async_events.h"
29+
#include "log.h"
30+
#include "filesys.h"
31+
#include "porting.h"
32+
33+
//TODO replace by ShadowNinja version not yet merged to master
34+
static int script_error_handler(lua_State *L) {
35+
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
36+
if (!lua_istable(L, -1)) {
37+
lua_pop(L, 1);
38+
return 1;
39+
}
40+
lua_getfield(L, -1, "traceback");
41+
if (!lua_isfunction(L, -1)) {
42+
lua_pop(L, 2);
43+
return 1;
44+
}
45+
lua_pushvalue(L, 1);
46+
lua_pushinteger(L, 2);
47+
lua_call(L, 2, 1);
48+
return 1;
49+
}
50+
51+
/******************************************************************************/
52+
static void scriptError(const char *fmt, ...)
53+
{
54+
va_list argp;
55+
va_start(argp, fmt);
56+
char buf[10000];
57+
vsnprintf(buf, 10000, fmt, argp);
58+
va_end(argp);
59+
errorstream<<"ERROR: "<<buf;
60+
fprintf(stderr,"ERROR: %s\n",buf);
61+
}
62+
63+
/******************************************************************************/
64+
AsyncEngine::AsyncEngine() :
65+
m_initDone(false),
66+
m_JobIdCounter(0)
67+
{
68+
assert(m_JobQueueMutex.Init() == 0);
69+
assert(m_ResultQueueMutex.Init() == 0);
70+
}
71+
72+
/******************************************************************************/
73+
AsyncEngine::~AsyncEngine()
74+
{
75+
/** request all threads to stop **/
76+
for (std::vector<AsyncWorkerThread*>::iterator i= m_WorkerThreads.begin();
77+
i != m_WorkerThreads.end();i++) {
78+
(*i)->Stop();
79+
}
80+
81+
82+
/** wakeup all threads **/
83+
for (std::vector<AsyncWorkerThread*>::iterator i= m_WorkerThreads.begin();
84+
i != m_WorkerThreads.end();i++) {
85+
m_JobQueueCounter.Post();
86+
}
87+
88+
/** wait for threads to finish **/
89+
for (std::vector<AsyncWorkerThread*>::iterator i= m_WorkerThreads.begin();
90+
i != m_WorkerThreads.end();i++) {
91+
(*i)->Wait();
92+
}
93+
94+
/** force kill all threads **/
95+
for (std::vector<AsyncWorkerThread*>::iterator i= m_WorkerThreads.begin();
96+
i != m_WorkerThreads.end();i++) {
97+
(*i)->Kill();
98+
delete *i;
99+
}
100+
101+
m_JobQueueMutex.Lock();
102+
m_JobQueue.clear();
103+
m_JobQueueMutex.Unlock();
104+
m_WorkerThreads.clear();
105+
}
106+
107+
/******************************************************************************/
108+
bool AsyncEngine::registerFunction(const char* name, lua_CFunction fct) {
109+
110+
if (m_initDone) return false;
111+
m_FunctionList[name] = fct;
112+
return true;
113+
}
114+
115+
/******************************************************************************/
116+
void AsyncEngine::Initialize(unsigned int numengines) {
117+
m_initDone = true;
118+
119+
for (unsigned int i=0; i < numengines; i ++) {
120+
121+
AsyncWorkerThread* toadd = new AsyncWorkerThread(this,i);
122+
m_WorkerThreads.push_back(toadd);
123+
toadd->Start();
124+
}
125+
}
126+
127+
/******************************************************************************/
128+
unsigned int AsyncEngine::doAsyncJob(std::string fct, std::string params) {
129+
130+
m_JobQueueMutex.Lock();
131+
LuaJobInfo toadd;
132+
toadd.JobId = m_JobIdCounter++;
133+
toadd.serializedFunction = fct;
134+
toadd.serializedParams = params;
135+
136+
m_JobQueue.push_back(toadd);
137+
138+
m_JobQueueCounter.Post();
139+
140+
m_JobQueueMutex.Unlock();
141+
142+
return toadd.JobId;
143+
}
144+
145+
/******************************************************************************/
146+
LuaJobInfo AsyncEngine::getJob() {
147+
148+
m_JobQueueCounter.Wait();
149+
m_JobQueueMutex.Lock();
150+
151+
LuaJobInfo retval;
152+
153+
if (m_JobQueue.size() != 0) {
154+
retval = m_JobQueue.front();
155+
m_JobQueue.erase((m_JobQueue.begin()));
156+
}
157+
m_JobQueueMutex.Unlock();
158+
159+
return retval;
160+
}
161+
162+
/******************************************************************************/
163+
void AsyncEngine::putJobResult(LuaJobInfo result) {
164+
m_ResultQueueMutex.Lock();
165+
m_ResultQueue.push_back(result);
166+
m_ResultQueueMutex.Unlock();
167+
}
168+
169+
/******************************************************************************/
170+
void AsyncEngine::Step(lua_State *L) {
171+
m_ResultQueueMutex.Lock();
172+
while(!m_ResultQueue.empty()) {
173+
174+
LuaJobInfo jobdone = m_ResultQueue.front();
175+
m_ResultQueue.erase(m_ResultQueue.begin());
176+
lua_getglobal(L, "engine");
177+
178+
lua_getfield(L, -1, "async_event_handler");
179+
180+
if(lua_isnil(L, -1))
181+
assert("Someone managed to destroy a async callback in engine!" == 0);
182+
183+
luaL_checktype(L, -1, LUA_TFUNCTION);
184+
185+
lua_pushinteger(L, jobdone.JobId);
186+
lua_pushlstring(L, jobdone.serializedResult.c_str(),
187+
jobdone.serializedResult.length());
188+
189+
if(lua_pcall(L, 2, 0, 0)) {
190+
scriptError("Async ENGINE step: %s", lua_tostring(L, -1));
191+
}
192+
193+
lua_pop(L,1);
194+
}
195+
m_ResultQueueMutex.Unlock();
196+
}
197+
198+
/******************************************************************************/
199+
void AsyncEngine::PushFinishedJobs(lua_State* L) {
200+
//Result Table
201+
m_ResultQueueMutex.Lock();
202+
203+
unsigned int index=1;
204+
lua_newtable(L);
205+
int top = lua_gettop(L);
206+
207+
while(!m_ResultQueue.empty()) {
208+
209+
LuaJobInfo jobdone = m_ResultQueue.front();
210+
m_ResultQueue.erase(m_ResultQueue.begin());
211+
212+
lua_pushnumber(L,index);
213+
214+
lua_newtable(L);
215+
int top_lvl2 = lua_gettop(L);
216+
217+
lua_pushstring(L,"jobid");
218+
lua_pushnumber(L,jobdone.JobId);
219+
lua_settable(L, top_lvl2);
220+
221+
lua_pushstring(L,"retval");
222+
lua_pushstring(L, jobdone.serializedResult.c_str());
223+
lua_settable(L, top_lvl2);
224+
225+
lua_settable(L, top);
226+
index++;
227+
}
228+
229+
m_ResultQueueMutex.Unlock();
230+
231+
}
232+
/******************************************************************************/
233+
void AsyncEngine::PrepareEnvironment(lua_State* L, int top) {
234+
for(std::map<std::string,lua_CFunction>::iterator i = m_FunctionList.begin();
235+
i != m_FunctionList.end(); i++) {
236+
237+
lua_pushstring(L,i->first.c_str());
238+
lua_pushcfunction(L,i->second);
239+
lua_settable(L, top);
240+
241+
}
242+
}
243+
244+
/******************************************************************************/
245+
int async_worker_ErrorHandler(lua_State *L) {
246+
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
247+
if (!lua_istable(L, -1)) {
248+
lua_pop(L, 1);
249+
return 1;
250+
}
251+
lua_getfield(L, -1, "traceback");
252+
if (!lua_isfunction(L, -1)) {
253+
lua_pop(L, 2);
254+
return 1;
255+
}
256+
lua_pushvalue(L, 1);
257+
lua_pushinteger(L, 2);
258+
lua_call(L, 2, 1);
259+
return 1;
260+
}
261+
262+
/******************************************************************************/
263+
AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobdispatcher,
264+
unsigned int numthreadnumber) :
265+
m_JobDispatcher(jobdispatcher),
266+
m_luaerrorhandler(-1),
267+
m_threadnum(numthreadnumber)
268+
{
269+
// create luastack
270+
m_LuaStack = luaL_newstate();
271+
272+
// load basic lua modules
273+
luaL_openlibs(m_LuaStack);
274+
275+
// load serialization functions
276+
luaopen_marshal(m_LuaStack);
277+
}
278+
279+
/******************************************************************************/
280+
AsyncWorkerThread::~AsyncWorkerThread() {
281+
282+
assert(IsRunning() == false);
283+
lua_close(m_LuaStack);
284+
}
285+
286+
/******************************************************************************/
287+
void* AsyncWorkerThread::worker_thread_main() {
288+
289+
//register thread for error logging
290+
char number[21];
291+
snprintf(number,sizeof(number),"%d",m_threadnum);
292+
log_register_thread(std::string("AsyncWorkerThread_") + number);
293+
294+
/** prepare job lua environment **/
295+
lua_newtable(m_LuaStack);
296+
lua_setglobal(m_LuaStack, "engine");
297+
298+
lua_getglobal(m_LuaStack, "engine");
299+
int top = lua_gettop(m_LuaStack);
300+
301+
lua_pushstring(m_LuaStack, DIR_DELIM);
302+
lua_setglobal(m_LuaStack, "DIR_DELIM");
303+
304+
lua_pushstring(m_LuaStack,
305+
std::string(porting::path_share + DIR_DELIM + "builtin").c_str());
306+
lua_setglobal(m_LuaStack, "SCRIPTDIR");
307+
308+
309+
m_JobDispatcher->PrepareEnvironment(m_LuaStack,top);
310+
311+
std::string asyncscript =
312+
porting::path_share + DIR_DELIM + "builtin"
313+
+ DIR_DELIM + "async_env.lua";
314+
315+
lua_pushcfunction(m_LuaStack, async_worker_ErrorHandler);
316+
m_luaerrorhandler = lua_gettop(m_LuaStack);
317+
318+
if(!runScript(asyncscript)) {
319+
infostream
320+
<< "AsyncWorkderThread::worker_thread_main execution of async base environment failed!"
321+
<< std::endl;
322+
assert("no future with broken builtin async environment scripts" == 0);
323+
}
324+
/** main loop **/
325+
while(IsRunning()) {
326+
//wait for job
327+
LuaJobInfo toprocess = m_JobDispatcher->getJob();
328+
329+
if (!IsRunning()) { continue; }
330+
331+
//first push error handler
332+
lua_pushcfunction(m_LuaStack, script_error_handler);
333+
int errorhandler = lua_gettop(m_LuaStack);
334+
335+
lua_getglobal(m_LuaStack, "engine");
336+
if(lua_isnil(m_LuaStack, -1))
337+
assert("unable to find engine within async environment" == 0);
338+
339+
lua_getfield(m_LuaStack, -1, "job_processor");
340+
if(lua_isnil(m_LuaStack, -1))
341+
assert("Someone managed to destroy a async worker engine!" == 0);
342+
343+
luaL_checktype(m_LuaStack, -1, LUA_TFUNCTION);
344+
345+
//call it
346+
lua_pushlstring(m_LuaStack,
347+
toprocess.serializedFunction.c_str(),
348+
toprocess.serializedFunction.length());
349+
lua_pushlstring(m_LuaStack,
350+
toprocess.serializedParams.c_str(),
351+
toprocess.serializedParams.length());
352+
353+
if (!IsRunning()) { continue; }
354+
if(lua_pcall(m_LuaStack, 2, 2, errorhandler)) {
355+
scriptError("Async WORKER thread: %s\n", lua_tostring(m_LuaStack, -1));
356+
toprocess.serializedResult="ERROR";
357+
}
358+
else {
359+
//fetch result
360+
const char *retval = lua_tostring(m_LuaStack, -2);
361+
unsigned int lenght = lua_tointeger(m_LuaStack,-1);
362+
toprocess.serializedResult = std::string(retval,lenght);
363+
}
364+
365+
if (!IsRunning()) { continue; }
366+
//put job result
367+
m_JobDispatcher->putJobResult(toprocess);
368+
}
369+
log_deregister_thread();
370+
return 0;
371+
}
372+
373+
/******************************************************************************/
374+
bool AsyncWorkerThread::runScript(std::string script) {
375+
376+
int ret = luaL_loadfile(m_LuaStack, script.c_str()) ||
377+
lua_pcall(m_LuaStack, 0, 0, m_luaerrorhandler);
378+
if(ret){
379+
errorstream<<"==== ERROR FROM LUA WHILE INITIALIZING ASYNC ENVIRONMENT ====="<<std::endl;
380+
errorstream<<"Failed to load and run script from "<<std::endl;
381+
errorstream<<script<<":"<<std::endl;
382+
errorstream<<std::endl;
383+
errorstream<<lua_tostring(m_LuaStack, -1)<<std::endl;
384+
errorstream<<std::endl;
385+
errorstream<<"=================== END OF ERROR FROM LUA ===================="<<std::endl;
386+
lua_pop(m_LuaStack, 1); // Pop error message from stack
387+
lua_pop(m_LuaStack, 1); // Pop the error handler from stack
388+
return false;
389+
}
390+
return true;
391+
}
392+
393+
/******************************************************************************/
394+
void* AsyncWorkerThread::worker_thread_wrapper(void* thread) {
395+
return ((AsyncWorkerThread*) thread)->worker_thread_main();
396+
}

‎src/script/lua_api/l_async_events.h

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
Minetest
3+
Copyright (C) 2013 sapier, <sapier AT gmx DOT net>
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*/
19+
20+
#ifndef C_ASYNC_EVENTS_H_
21+
#define C_ASYNC_EVENTS_H_
22+
23+
#include <vector>
24+
#include <map>
25+
#include <unistd.h>
26+
27+
/******************************************************************************/
28+
/* Includes */
29+
/******************************************************************************/
30+
#include "jthread/jthread.h"
31+
#include "jthread/jmutex.h"
32+
#include "jthread/jsemaphore.h"
33+
#include "debug.h"
34+
#include "lua.h"
35+
36+
/******************************************************************************/
37+
/* Typedefs and macros */
38+
/******************************************************************************/
39+
#define MAINMENU_NUMBER_OF_ASYNC_THREADS 4
40+
41+
/******************************************************************************/
42+
/* forward declarations */
43+
/******************************************************************************/
44+
class AsyncEngine;
45+
46+
/******************************************************************************/
47+
/* declarations */
48+
/******************************************************************************/
49+
50+
/** a struct to encapsulate data required to queue a job **/
51+
struct LuaJobInfo {
52+
/** function to be called in async environment **/
53+
std::string serializedFunction;
54+
/** parameter table to be passed to function **/
55+
std::string serializedParams;
56+
/** result of function call **/
57+
std::string serializedResult;
58+
/** jobid used to identify a job and match it to callback **/
59+
unsigned int JobId;
60+
};
61+
62+
/** class encapsulating a asynchronous working environment **/
63+
class AsyncWorkerThread : public JThread {
64+
public:
65+
/**
66+
* default constructor
67+
* @param pointer to job dispatcher
68+
*/
69+
AsyncWorkerThread(AsyncEngine* jobdispatcher, unsigned int threadnumber);
70+
71+
/**
72+
* default destructor
73+
*/
74+
virtual ~AsyncWorkerThread();
75+
76+
/**
77+
* thread function
78+
*/
79+
void* Thread() {
80+
ThreadStarted();
81+
return worker_thread_wrapper(this);
82+
}
83+
84+
/**
85+
* wait for thread to stop
86+
*/
87+
void Wait() {
88+
while(IsRunning()) {
89+
sleep(1);
90+
}
91+
}
92+
93+
private:
94+
/**
95+
* helper function to run a lua script
96+
* @param path of script
97+
*/
98+
bool runScript(std::string script);
99+
100+
/**
101+
* main function of thread
102+
*/
103+
void* worker_thread_main();
104+
105+
/**
106+
* static wrapper for thread creation
107+
* @param this pointer to the thread to be created
108+
*/
109+
static void* worker_thread_wrapper(void* thread);
110+
111+
/**
112+
* pointer to job dispatcher
113+
*/
114+
AsyncEngine* m_JobDispatcher;
115+
116+
/**
117+
* the lua stack to run at
118+
*/
119+
lua_State* m_LuaStack;
120+
121+
/**
122+
* lua internal stack number of error handler
123+
*/
124+
int m_luaerrorhandler;
125+
126+
/**
127+
* thread number used for debug output
128+
*/
129+
unsigned int m_threadnum;
130+
131+
};
132+
133+
/** asynchornous thread and job management **/
134+
class AsyncEngine {
135+
friend AsyncWorkerThread;
136+
public:
137+
/**
138+
* default constructor
139+
*/
140+
AsyncEngine();
141+
/**
142+
* default destructor
143+
*/
144+
~AsyncEngine();
145+
146+
/**
147+
* register function to be used within engines
148+
* @param name function name to be used within lua environment
149+
* @param fct c-function to be called
150+
*/
151+
bool registerFunction(const char* name, lua_CFunction fct);
152+
153+
/**
154+
* create async engine tasks and lock function registration
155+
* @param numengines number of async threads to be started
156+
*/
157+
void Initialize(unsigned int numengines);
158+
159+
/**
160+
* queue/run a async job
161+
* @param fct serialized lua function
162+
* @param params serialized parameters
163+
* @return jobid the job is queued
164+
*/
165+
unsigned int doAsyncJob(std::string fct, std::string params);
166+
167+
/**
168+
* engine step to process finished jobs
169+
* the engine step is one way to pass events back, PushFinishedJobs another
170+
* @param L the lua environment to do the step in
171+
*/
172+
void Step(lua_State *L);
173+
174+
175+
void PushFinishedJobs(lua_State* L);
176+
177+
protected:
178+
/**
179+
* Get a Job from queue to be processed
180+
* this function blocks until a job is ready
181+
* @return a job to be processed
182+
*/
183+
LuaJobInfo getJob();
184+
185+
/**
186+
* put a Job result back to result queue
187+
* @param result result of completed job
188+
*/
189+
void putJobResult(LuaJobInfo result);
190+
191+
/**
192+
* initialize environment with current registred functions
193+
* this function adds all functions registred by registerFunction to the
194+
* passed lua stack
195+
* @param L lua stack to initialize
196+
* @param top stack position
197+
*/
198+
void PrepareEnvironment(lua_State* L, int top);
199+
200+
private:
201+
202+
/** variable locking the engine against further modification **/
203+
bool m_initDone;
204+
205+
/** internal store for registred functions **/
206+
std::map<std::string,lua_CFunction> m_FunctionList;
207+
208+
/** internal counter to create job id's **/
209+
unsigned int m_JobIdCounter;
210+
211+
/** mutex to protect job queue **/
212+
JMutex m_JobQueueMutex;
213+
/** job queue **/
214+
std::vector<LuaJobInfo> m_JobQueue;
215+
216+
/** mutext to protect result queue **/
217+
JMutex m_ResultQueueMutex;
218+
/** result queue **/
219+
std::vector<LuaJobInfo> m_ResultQueue;
220+
221+
/** list of current worker threads **/
222+
std::vector<AsyncWorkerThread*> m_WorkerThreads;
223+
224+
/** counter semaphore for job dispatching **/
225+
JSemaphore m_JobQueueCounter;
226+
};
227+
228+
#endif /* C_ASYNC_EVENTS_H_ */

‎src/script/lua_api/l_internal.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3030
#include "common/c_internal.h"
3131

3232
#define luamethod(class, name) {#name, class::l_##name}
33-
#define API_FCT(name) registerFunction(L,#name,l_##name,top)
33+
#define API_FCT(name) registerFunction(L, #name, l_##name,top)
34+
#define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name)
3435

3536
#if (defined(WIN32) || defined(_WIN32_WCE))
3637
#define NO_MAP_LOCK_REQUIRED
3738
#else
3839
#include "main.h"
3940
#include "profiler.h"
40-
#define NO_MAP_LOCK_REQUIRED ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD)
41+
#define NO_MAP_LOCK_REQUIRED \
42+
ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD)
4143
#endif
4244

4345
#endif /* L_INTERNAL_H_ */

‎src/script/lua_api/l_mainmenu.cpp

+47-25
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2020
#include "lua_api/l_mainmenu.h"
2121
#include "lua_api/l_internal.h"
2222
#include "common/c_content.h"
23+
#include "lua_api/l_async_events.h"
2324
#include "guiEngine.h"
2425
#include "guiMainMenu.h"
2526
#include "guiKeyChangeMenu.h"
@@ -200,9 +201,6 @@ int ModApiMainMenu::l_get_textlist_index(lua_State *L)
200201
/******************************************************************************/
201202
int ModApiMainMenu::l_get_worlds(lua_State *L)
202203
{
203-
GUIEngine* engine = getGuiEngine(L);
204-
assert(engine != 0);
205-
206204
std::vector<WorldSpec> worlds = getAvailableWorlds();
207205

208206
lua_newtable(L);
@@ -237,9 +235,6 @@ int ModApiMainMenu::l_get_worlds(lua_State *L)
237235
/******************************************************************************/
238236
int ModApiMainMenu::l_get_games(lua_State *L)
239237
{
240-
GUIEngine* engine = getGuiEngine(L);
241-
assert(engine != 0);
242-
243238
std::vector<SubgameSpec> games = getAvailableGames();
244239

245240
lua_newtable(L);
@@ -365,9 +360,6 @@ int ModApiMainMenu::l_get_modstore_details(lua_State *L)
365360
/******************************************************************************/
366361
int ModApiMainMenu::l_get_modstore_list(lua_State *L)
367362
{
368-
GUIEngine* engine = getGuiEngine(L);
369-
assert(engine != 0);
370-
371363
std::string listtype = "local";
372364

373365
if (!lua_isnone(L,1)) {
@@ -421,9 +413,6 @@ int ModApiMainMenu::l_get_modstore_list(lua_State *L)
421413
/******************************************************************************/
422414
int ModApiMainMenu::l_get_favorites(lua_State *L)
423415
{
424-
GUIEngine* engine = getGuiEngine(L);
425-
assert(engine != 0);
426-
427416
std::string listtype = "local";
428417

429418
if (!lua_isnone(L,1)) {
@@ -545,9 +534,6 @@ int ModApiMainMenu::l_get_favorites(lua_State *L)
545534
/******************************************************************************/
546535
int ModApiMainMenu::l_delete_favorite(lua_State *L)
547536
{
548-
GUIEngine* engine = getGuiEngine(L);
549-
assert(engine != 0);
550-
551537
std::vector<ServerListSpec> servers;
552538

553539
std::string listtype = "local";
@@ -599,9 +585,6 @@ int ModApiMainMenu::l_show_keys_menu(lua_State *L)
599585
/******************************************************************************/
600586
int ModApiMainMenu::l_create_world(lua_State *L)
601587
{
602-
GUIEngine* engine = getGuiEngine(L);
603-
assert(engine != 0);
604-
605588
const char *name = luaL_checkstring(L, 1);
606589
int gameidx = luaL_checkinteger(L,2) -1;
607590

@@ -632,9 +615,6 @@ int ModApiMainMenu::l_create_world(lua_State *L)
632615
/******************************************************************************/
633616
int ModApiMainMenu::l_delete_world(lua_State *L)
634617
{
635-
GUIEngine* engine = getGuiEngine(L);
636-
assert(engine != 0);
637-
638618
int worldidx = luaL_checkinteger(L,1) -1;
639619

640620
std::vector<WorldSpec> worlds = getAvailableWorlds();
@@ -962,17 +942,14 @@ int ModApiMainMenu::l_sound_stop(lua_State *L)
962942
/******************************************************************************/
963943
int ModApiMainMenu::l_download_file(lua_State *L)
964944
{
965-
GUIEngine* engine = getGuiEngine(L);
966-
assert(engine != 0);
967-
968945
const char *url = luaL_checkstring(L, 1);
969946
const char *target = luaL_checkstring(L, 2);
970947

971948
//check path
972949
std::string absolute_destination = fs::RemoveRelativePathComponents(target);
973950

974951
if (ModApiMainMenu::isMinetestPath(absolute_destination)) {
975-
if (engine->downloadFile(url,absolute_destination)) {
952+
if (GUIEngine::downloadFile(url,absolute_destination)) {
976953
lua_pushboolean(L,true);
977954
return 1;
978955
}
@@ -990,6 +967,28 @@ int ModApiMainMenu::l_gettext(lua_State *L)
990967
return 1;
991968
}
992969

970+
/******************************************************************************/
971+
int ModApiMainMenu::l_do_async_callback(lua_State *L)
972+
{
973+
GUIEngine* engine = getGuiEngine(L);
974+
975+
const char* serialized_fct_raw = luaL_checkstring(L, 1);
976+
unsigned int lenght_fct = luaL_checkint(L, 2);
977+
978+
const char* serialized_params_raw = luaL_checkstring(L, 3);
979+
unsigned int lenght_params = luaL_checkint(L, 4);
980+
981+
assert(serialized_fct_raw != 0);
982+
assert(serialized_params_raw != 0);
983+
984+
std::string serialized_fct = std::string(serialized_fct_raw,lenght_fct);
985+
std::string serialized_params = std::string(serialized_params_raw,lenght_params);
986+
987+
lua_pushinteger(L,engine->DoAsync(serialized_fct,serialized_params));
988+
989+
return 1;
990+
}
991+
993992
/******************************************************************************/
994993
void ModApiMainMenu::Initialize(lua_State *L, int top)
995994
{
@@ -1024,4 +1023,27 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
10241023
API_FCT(sound_play);
10251024
API_FCT(sound_stop);
10261025
API_FCT(gettext);
1026+
API_FCT(do_async_callback);
1027+
}
1028+
1029+
/******************************************************************************/
1030+
void ModApiMainMenu::InitializeAsync(AsyncEngine& engine)
1031+
{
1032+
1033+
ASYNC_API_FCT(get_worlds);
1034+
ASYNC_API_FCT(get_games);
1035+
ASYNC_API_FCT(get_favorites);
1036+
ASYNC_API_FCT(get_modpath);
1037+
ASYNC_API_FCT(get_gamepath);
1038+
ASYNC_API_FCT(get_texturepath);
1039+
ASYNC_API_FCT(get_dirlist);
1040+
ASYNC_API_FCT(create_dir);
1041+
ASYNC_API_FCT(delete_dir);
1042+
ASYNC_API_FCT(copy_dir);
1043+
//ASYNC_API_FCT(extract_zip); //TODO remove dependency to GuiEngine
1044+
ASYNC_API_FCT(get_version);
1045+
ASYNC_API_FCT(download_file);
1046+
ASYNC_API_FCT(get_modstore_details);
1047+
ASYNC_API_FCT(get_modstore_list);
1048+
//ASYNC_API_FCT(gettext); (gettext lib isn't threadsafe)
10271049
}

‎src/script/lua_api/l_mainmenu.h

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2222

2323
#include "lua_api/l_base.h"
2424

25+
class AsyncEngine;
26+
2527
/** Implementation of lua api support for mainmenu */
2628
class ModApiMainMenu : public ModApiBase {
2729

@@ -125,6 +127,8 @@ class ModApiMainMenu : public ModApiBase {
125127

126128
static int l_download_file(lua_State *L);
127129

130+
// async
131+
static int l_do_async_callback(lua_State *L);
128132

129133
public:
130134
/**
@@ -134,6 +138,8 @@ class ModApiMainMenu : public ModApiBase {
134138
*/
135139
static void Initialize(lua_State *L, int top);
136140

141+
static void InitializeAsync(AsyncEngine& engine);
142+
137143
};
138144

139145
#endif /* L_MAINMENU_H_ */

‎src/script/lua_api/l_util.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2121
#include "lua_api/l_internal.h"
2222
#include "common/c_converter.h"
2323
#include "common/c_content.h"
24+
#include "lua_api/l_async_events.h"
2425
#include "debug.h"
2526
#include "log.h"
2627
#include "tool.h"
@@ -257,3 +258,18 @@ void ModApiUtil::Initialize(lua_State *L, int top)
257258
API_FCT(is_yes);
258259
}
259260

261+
void ModApiUtil::InitializeAsync(AsyncEngine& engine)
262+
{
263+
ASYNC_API_FCT(debug);
264+
ASYNC_API_FCT(log);
265+
266+
//ASYNC_API_FCT(setting_set);
267+
ASYNC_API_FCT(setting_get);
268+
//ASYNC_API_FCT(setting_setbool);
269+
ASYNC_API_FCT(setting_getbool);
270+
//ASYNC_API_FCT(setting_save);
271+
272+
ASYNC_API_FCT(parse_json);
273+
274+
ASYNC_API_FCT(is_yes);
275+
}

‎src/script/lua_api/l_util.h

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2222

2323
#include "lua_api/l_base.h"
2424

25+
class AsyncEngine;
26+
2527
class ModApiUtil : public ModApiBase {
2628
private:
2729
/*
@@ -77,6 +79,8 @@ class ModApiUtil : public ModApiBase {
7779
public:
7880
static void Initialize(lua_State *L, int top);
7981

82+
static void InitializeAsync(AsyncEngine& engine);
83+
8084
};
8185

8286
#endif /* L_UTIL_H_ */

‎src/script/lua_api/marshall.c

+551
Large diffs are not rendered by default.

‎src/script/scripting_mainmenu.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2828

2929
extern "C" {
3030
#include "lualib.h"
31+
int luaopen_marshal(lua_State *L);
3132
}
32-
33+
/******************************************************************************/
3334
MainMenuScripting::MainMenuScripting(GUIEngine* guiengine)
3435
{
3536
setGuiEngine(guiengine);
3637

3738
//TODO add security
3839

3940
luaL_openlibs(getStack());
41+
luaopen_marshal(getStack());
4042

4143
SCRIPTAPI_PRECHECKHEADER
4244

@@ -58,6 +60,7 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine)
5860
infostream << "SCRIPTAPI: initialized mainmenu modules" << std::endl;
5961
}
6062

63+
/******************************************************************************/
6164
void MainMenuScripting::InitializeModApi(lua_State *L, int top)
6265
{
6366
// Initialize mod api modules
@@ -66,4 +69,23 @@ void MainMenuScripting::InitializeModApi(lua_State *L, int top)
6669

6770
// Register reference classes (userdata)
6871
LuaSettings::Register(L);
72+
73+
// Register functions to async environment
74+
ModApiMainMenu::InitializeAsync(m_AsyncEngine);
75+
ModApiUtil::InitializeAsync(m_AsyncEngine);
76+
77+
// Initialize async environment
78+
//TODO possibly make number of async threads configurable
79+
m_AsyncEngine.Initialize(MAINMENU_NUMBER_OF_ASYNC_THREADS);
80+
}
81+
82+
/******************************************************************************/
83+
void MainMenuScripting::Step() {
84+
m_AsyncEngine.Step(getStack());
85+
}
86+
87+
/******************************************************************************/
88+
unsigned int MainMenuScripting::DoAsync(std::string serialized_fct,
89+
std::string serialized_params) {
90+
return m_AsyncEngine.doAsyncJob(serialized_fct,serialized_params);
6991
}

‎src/script/scripting_mainmenu.h

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2222

2323
#include "cpp_api/s_base.h"
2424
#include "cpp_api/s_mainmenu.h"
25+
#include "lua_api/l_async_events.h"
2526

2627
/*****************************************************************************/
2728
/* Scripting <-> Main Menu Interface */
@@ -37,8 +38,16 @@ class MainMenuScripting
3738
// use ScriptApiBase::loadMod() or ScriptApiBase::loadScript()
3839
// to load scripts
3940

41+
/* global step handler to pass back async events */
42+
void Step();
43+
44+
/* pass async events from engine to async threads */
45+
unsigned int DoAsync(std::string serialized_fct,
46+
std::string serialized_params);
4047
private:
4148
void InitializeModApi(lua_State *L, int top);
49+
50+
AsyncEngine m_AsyncEngine;
4251
};
4352

4453

0 commit comments

Comments
 (0)
Please sign in to comment.