aboutsummaryrefslogtreecommitdiff
path: root/doc/menu_lua_api.txt
blob: 90b2d01fd99211726fd88d34b37d288ac4c68b50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
Minetest Lua Mainmenu API Reference 0.4.6
========================================

Introduction
-------------
The main menu is defined as a formspec by Lua in builtin/mainmenu.lua
Description of formspec language to show your menu is in lua_api.txt

Callbacks
---------
engine.buttonhandler(fields): called when a button is pressed.
^ fields = {name1 = value1, name2 = value2, ...}
engine.event_handler(event)
^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter"

Gamedata
--------
The "gamedata" table is read when calling engine.start(). It should contain:
{
	playername     = <name>,
	password       = <password>,
	address        = <IP/adress>,
	port           = <port>,
	selected_world = <index>, -- 0 for client mode
	singleplayer   = <true/false>,
}

Functions
---------
engine.start()
engine.close()

Filesystem:
engine.get_scriptdir()
^ returns directory of script
engine.get_modpath() (possible in async calls)
^ returns path to global modpath
engine.get_modstore_details(modid) (possible in async calls)
^ modid numeric id of mod in modstore
^ returns {
	id			= <numeric id of mod in modstore>,
	title		= <human readable title>,
	basename	= <basename for mod>,
	description = <description of mod>,
	author		= <author of mod>,
	download_url= <best match download url>,
	license		= <short description of license>,
	rating		= <float value of current rating>
}
engine.get_modstore_list() (possible in async calls)
^ returns {
	[1] = {
		id		 = <numeric id of mod in modstore>,
		title	 = <human readable title>,
		basename = <basename for mod>
	}
}
engine.get_gamepath() (possible in async calls)
^ returns path to global gamepath
engine.get_texturepath() (possible in async calls)
^ returns path to default textures
engine.get_dirlist(path,onlydirs) (possible in async calls)
^ path to get subdirs from
^ onlydirs should result contain only dirs?
^ returns list of folders within path
engine.create_dir(absolute_path) (possible in async calls)
^ absolute_path to directory to create (needs to be absolute)
^ returns true/false
engine.delete_dir(absolute_path) (possible in async calls)
^ absolute_path to directory to delete (needs to be absolute)
^ returns true/false
engine.copy_dir(source,destination,keep_soure) (possible in async calls)
^ source folder
^ destination folder
^ keep_source DEFAULT true --> if set to false source is deleted after copying
^ returns true/false
engine.extract_zip(zipfile,destination) [unzip within path required]
^ zipfile to extract
^ destination folder to extract to
^ returns true/false
engine.download_file(url,target) (possible in async calls)
^ url to download
^ target to store to
^ returns true/false
engine.get_version() (possible in async calls)
^ returns current minetest version
engine.sound_play(spec, looped) -> handle
^ spec = SimpleSoundSpec (see lua-api.txt)
^ looped = bool
engine.sound_stop(handle)

GUI:
engine.update_formspec(formspec)
- engine.set_background(type, texturepath)
^ type: "background", "overlay", "header" or "footer"
engine.set_clouds(<true/false>)
engine.set_topleft_text(text)

Games:
engine.get_game(index)
^ returns {
	id               = <id>,
	path             = <full path to game>,
	gamemods_path    = <path>,
	name             = <name of game>,
	menuicon_path    = <full path to menuicon>,
	DEPRECATED:
	addon_mods_paths = {[1] = <path>,},
}
engine.get_games() -> table of all games in upper format (possible in async calls)

Favorites:
engine.get_favorites(location) -> list of favorites (possible in async calls)
^ location: "local" or "online"
^ returns {
	[1] = {
	clients       = <number of clients/nil>,
	clients_max   = <maximum number of clients/nil>,
	version       = <server version/nil>,
	password      = <true/nil>,
	creative      = <true/nil>,
	damage        = <true/nil>,
	pvp           = <true/nil>,
	description   = <server description/nil>,
	name          = <server name/nil>,
	address       = <address of server/nil>,
	port          = <port>
	},
}
engine.delete_favorite(id, location) -> success

Logging:
engine.debug(line) (possible in async calls)
^ Always printed to stderr and logfile (print() is redirected here)
engine.log(line) (possible in async calls)
engine.log(loglevel, line) (possible in async calls)
^ loglevel one of "error", "action", "info", "verbose"

Settings:
engine.setting_set(name, value)
engine.setting_get(name) -> string or nil (possible in async calls)
engine.setting_setbool(name, value)
engine.setting_getbool(name) -> bool or nil (possible in async calls)
engine.setting_save() -> nil, save all settings to config file

Worlds:
engine.get_worlds() -> list of worlds (possible in async calls)
^ returns {
	[1] = {
	path   = <full path to world>,
	name   = <name of world>,
	gameid = <gameid of world>,
	},
}
engine.create_world(worldname, gameid)
engine.delete_world(index)


UI:
engine.get_textlist_index(textlistname) -> index
engine.show_keys_menu()
engine.file_open_dialog(formname,caption)
^ shows a file open dialog
^ formname is base name of dialog response returned in fields
^     -if dialog was accepted "_accepted" 
^^       will be added to fieldname containing the path
^     -if dialog was canceled "_cancelled" 
^        will be added to fieldname value is set to formname itself
^ returns nil or selected file/folder

Helpers:
engine.formspec_escape(string) -> string
^ escapes characters [ ] \ , ; that can not be used in formspecs
engine.gettext(string) -> string
^ look up the translation of a string in the gettext message catalog
fgettext(string, ...) -> string
^ call engine.gettext(string), replace "$1"..."$9" with the given
^ extra arguments, call engine.formspec_escape and return the result
engine.parse_json(string[, nullvalue]) -> something (possible in async calls)
^ see minetest.parse_json (lua_api.txt)
dump(obj, dumped={})
^ Return object serialized as a string
string:split(separator)
^ eg. string:split("a,b", ",") == {"a","b"}
string:trim()
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
minetest.is_yes(arg) (possible in async calls)
^ returns whether arg can be interpreted as yes

Async:
engine.handle_async(async_job,parameters,finished)
^ execute a function asynchronously
^ async_job is a function receiving one parameter and returning one parameter
^ parameters parameter table passed to async_job
^ finished function to be called once async_job has finished 
^    the result of async_job is passed to this function

Limitations of Async operations
 -No access to global lua variables, don't even try
 -Limited set of available functions
	e.g. No access to functions modifying menu like engine.start,engine.close,
	engine.file_open_dialog
			

Class reference
----------------
Settings: see lua_api.txt
n426'>426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "threading/thread.h"
#include "threading/mutex_auto_lock.h"
#include "log.h"
#include "porting.h"

#define UNUSED(expr) do { (void)(expr); } while (0)

#if USE_CPP11_THREADS
	#include <chrono>
	#include <system_error>
#elif USE_WIN_THREADS
	#ifndef _WIN32_WCE
		#include <process.h>
	#endif
#elif USE_POSIX_THREADS
	#include <time.h>
	#include <assert.h>
	#include <stdlib.h>
	#include <unistd.h>
	#include <sys/time.h>

	#if defined(__FreeBSD__) || defined(__APPLE__)
		#include <sys/types.h>
		#include <sys/sysctl.h>
	#elif defined(_GNU_SOURCE)
		#include <sys/sysinfo.h>
	#endif
#endif


// for setName
#if defined(__linux__)
	#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
	#include <pthread_np.h>
#elif defined(_MSC_VER)
	struct THREADNAME_INFO {
		DWORD dwType;     // Must be 0x1000
		LPCSTR szName;    // Pointer to name (in user addr space)
		DWORD dwThreadID; // Thread ID (-1=caller thread)
		DWORD dwFlags;    // Reserved for future use, must be zero
	};
#endif

// for bindToProcessor
#if __FreeBSD_version >= 702106
	typedef cpuset_t cpu_set_t;
#elif defined(__linux__)
	#include <sched.h>
#elif defined(__sun) || defined(sun)
	#include <sys/types.h>
	#include <sys/processor.h>
	#include <sys/procset.h>
#elif defined(_AIX)
	#include <sys/processor.h>
	#include <sys/thread.h>
#elif defined(__APPLE__)
	#include <mach/mach_init.h>
	#include <mach/thread_act.h>
#endif


Thread::Thread(const std::string &name) :
	m_name(name),
	m_retval(NULL),
	m_joinable(false),
	m_request_stop(false),
	m_running(false)
{
#ifdef _AIX
	m_kernel_thread_id = -1;
#endif
}


Thread::~Thread()
{
	kill();

	// Make sure start finished mutex is unlocked before it's destroyed
	m_start_finished_mutex.try_lock();
	m_start_finished_mutex.unlock();

}


bool Thread::start()
{
	MutexAutoLock lock(m_mutex);

	if (m_running)
		return false;

	m_request_stop = false;

	// The mutex may already be locked if the thread is being restarted
	m_start_finished_mutex.try_lock();

#if USE_CPP11_THREADS

	try {
		m_thread_obj = new std::thread(threadProc, this);
	} catch (const std::system_error &e) {
		return false;
	}

#elif USE_WIN_THREADS

	m_thread_handle = CreateThread(NULL, 0, threadProc, this, 0, &m_thread_id);
	if (!m_thread_handle)
		return false;

#elif USE_POSIX_THREADS

	int status = pthread_create(&m_thread_handle, NULL, threadProc, this);
	if (status)
		return false;

#endif

	// Allow spawned thread to continue
	m_start_finished_mutex.unlock();

	while (!m_running)
		sleep_ms(1);

	m_joinable = true;

	return true;
}


bool Thread::stop()
{
	m_request_stop = true;
	return true;
}


bool Thread::wait()
{
	MutexAutoLock lock(m_mutex);

	if (!m_joinable)
		return false;

#if USE_CPP11_THREADS

	m_thread_obj->join();

	delete m_thread_obj;
	m_thread_obj = NULL;

#elif USE_WIN_THREADS

	int ret = WaitForSingleObject(m_thread_handle, INFINITE);
	assert(ret == WAIT_OBJECT_0);
	UNUSED(ret);

	CloseHandle(m_thread_handle);
	m_thread_handle = NULL;
	m_thread_id = -1;

#elif USE_POSIX_THREADS

	int ret = pthread_join(m_thread_handle, NULL);
	assert(ret == 0);
	UNUSED(ret);

#endif

	assert(m_running == false);
	m_joinable = false;
	return true;
}


bool Thread::kill()
{
	if (!m_running) {
		wait();
		return false;
	}

	m_running = false;

#if USE_WIN_THREADS
	TerminateThread(m_thread_handle, 0);
	CloseHandle(m_thread_handle);
#else
	// We need to pthread_kill instead on Android since NDKv5's pthread
	// implementation is incomplete.
# ifdef __ANDROID__
	pthread_kill(getThreadHandle(), SIGKILL);
# else
	pthread_cancel(getThreadHandle());
# endif
	wait();
#endif

	m_retval       = NULL;
	m_joinable     = false;
	m_request_stop = false;

	return true;
}


bool Thread::getReturnValue(void **ret)
{
	if (m_running)
		return false;

	*ret = m_retval;
	return true;
}


#if USE_CPP11_THREADS || USE_POSIX_THREADS
void *Thread::threadProc(void *param)
#elif defined(_WIN32_WCE)
DWORD Thread::threadProc(LPVOID param)
#elif defined(_WIN32)
DWORD WINAPI Thread::threadProc(LPVOID param)
#endif
{
	Thread *thr = (Thread *)param;

#ifdef _AIX
	thr->m_kernel_thread_id = thread_self();
#endif

	thr->setName(thr->m_name);

	g_logger.registerThread(thr->m_name);
	thr->m_running = true;

	// Wait for the thread that started this one to finish initializing the
	// thread handle so that getThreadId/getThreadHandle will work.
	thr->m_start_finished_mutex.lock();

	thr->m_retval = thr->run();

	thr->m_running = false;
	g_logger.deregisterThread();

	// 0 is returned here to avoid an unnecessary ifdef clause
	return 0;
}


void Thread::setName(const std::string &name)
{
#if defined(__linux__)

	// It would be cleaner to do this with pthread_setname_np,
	// which was added to glibc in version 2.12, but some major
	// distributions are still runing 2.11 and previous versions.
	prctl(PR_SET_NAME, name.c_str());

#elif defined(__FreeBSD__) || defined(__OpenBSD__)

	pthread_set_name_np(pthread_self(), name.c_str());

#elif defined(__NetBSD__)

	pthread_setname_np(pthread_self(), name.c_str());

#elif defined(__APPLE__)

	pthread_setname_np(name.c_str());

#elif defined(_MSC_VER)

	// Windows itself doesn't support thread names,
	// but the MSVC debugger does...
	THREADNAME_INFO info;

	info.dwType = 0x1000;
	info.szName = name.c_str();
	info.dwThreadID = -1;
	info.dwFlags = 0;

	__try {
		RaiseException(0x406D1388, 0,
			sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
	} __except (EXCEPTION_CONTINUE_EXECUTION) {
	}

#elif defined(_WIN32) || defined(__GNU__)

	// These platforms are known to not support thread names.
	// Silently ignore the request.

#else
	#warning "Unrecognized platform, thread names will not be available."
#endif
}


unsigned int Thread::getNumberOfProcessors()
{
#if USE_CPP11_THREADS

	return std::thread::hardware_concurrency();

#elif USE_WIN_THREADS

	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);
	return sysinfo.dwNumberOfProcessors;

#elif defined(_SC_NPROCESSORS_ONLN)

	return sysconf(_SC_NPROCESSORS_ONLN);

#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
	defined(__DragonFly__) || defined(__APPLE__)

	unsigned int num_cpus = 1;
	size_t len = sizeof(num_cpus);

	int mib[2];
	mib[0] = CTL_HW;
	mib[1] = HW_NCPU;

	sysctl(mib, 2, &num_cpus, &len, NULL, 0);
	return num_cpus;

#elif defined(_GNU_SOURCE)

	return get_nprocs();

#elif defined(PTW32_VERSION) || defined(__hpux)

	return pthread_num_processors_np();

#else

	return 1;

#endif
}


bool Thread::bindToProcessor(unsigned int proc_number)
{
#if defined(__ANDROID__)

	return false;

#elif USE_WIN_THREADS

	return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);

#elif __FreeBSD_version >= 702106 || defined(__linux__)

	cpu_set_t cpuset;

	CPU_ZERO(&cpuset);
	CPU_SET(proc_number, &cpuset);

	return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;

#elif defined(__sun) || defined(sun)

	return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0

#elif defined(_AIX)

	return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;

#elif defined(__hpux) || defined(hpux)

	pthread_spu_t answer;

	return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
			&answer, proc_number, getThreadHandle()) == 0;

#elif defined(__APPLE__)

	struct thread_affinity_policy tapol;

	thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
	tapol.affinity_tag = proc_number + 1;
	return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
			(thread_policy_t)&tapol,
			THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;

#else

	return false;

#endif
}


bool Thread::setPriority(int prio)
{
#if USE_WIN_THREADS

	return SetThreadPriority(getThreadHandle(), prio);

#else

	struct sched_param sparam;
	int policy;

	if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
		return false;

	int min = sched_get_priority_min(policy);
	int max = sched_get_priority_max(policy);

	sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
	return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;

#endif
}