minetest/src/main.cpp

1964 lines
50 KiB
C++
Raw Normal View History

/*
2013-02-24 18:40:43 +01:00
Minetest
2013-02-24 19:38:45 +01:00
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef NDEBUG
/*#ifdef _WIN32
#pragma message ("Disabling unit tests")
#else
#warning "Disabling unit tests"
#endif*/
// Disable unit tests
#define ENABLE_TESTS 0
#else
// Enable unit tests
#define ENABLE_TESTS 1
#endif
#ifdef _MSC_VER
#ifndef SERVER // Dedicated server isn't linked with Irrlicht
#pragma comment(lib, "Irrlicht.lib")
// This would get rid of the console window
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
#pragma comment(lib, "zlibwapi.lib")
#pragma comment(lib, "Shell32.lib")
#endif
2011-10-12 12:53:38 +02:00
#include "irrlicht.h" // createDevice
#include "main.h"
#include "mainmenumanager.h"
#include <iostream>
#include <fstream>
#include <locale.h>
2012-06-17 03:00:31 +02:00
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
#include "test.h"
#include "clouds.h"
#include "server.h"
#include "constants.h"
#include "porting.h"
#include "gettime.h"
#include "filesys.h"
#include "config.h"
#include "version.h"
#include "guiMainMenu.h"
#include "game.h"
#include "keycode.h"
#include "tile.h"
#include "chat.h"
2011-10-12 12:53:38 +02:00
#include "defaultsettings.h"
#include "gettext.h"
2011-10-12 12:53:38 +02:00
#include "settings.h"
#include "profiler.h"
#include "log.h"
2011-12-03 02:23:14 +01:00
#include "mods.h"
2013-02-03 13:19:09 +01:00
#if USE_FREETYPE
#include "xCGUITTFont.h"
#endif
#include "util/string.h"
2012-03-11 13:54:23 +01:00
#include "subgame.h"
#include "quicktune.h"
#include "serverlist.h"
#include "httpfetch.h"
#include "guiEngine.h"
#include "mapsector.h"
2014-08-22 14:03:04 +02:00
#include "player.h"
#include "database-sqlite3.h"
#ifdef USE_LEVELDB
#include "database-leveldb.h"
#endif
2014-04-08 21:39:21 +02:00
#if USE_REDIS
#include "database-redis.h"
#endif
#ifdef HAVE_TOUCHSCREENGUI
#include "touchscreengui.h"
#endif
/*
Settings.
These are loaded from the config file.
*/
2011-10-12 12:53:38 +02:00
Settings main_settings;
Settings *g_settings = &main_settings;
std::string g_settings_path;
// Global profiler
2011-10-12 12:53:38 +02:00
Profiler main_profiler;
Profiler *g_profiler = &main_profiler;
// Menu clouds are created later
Clouds *g_menuclouds = 0;
irr::scene::ISceneManager *g_menucloudsmgr = 0;
/*
Debug streams
*/
// Connection
std::ostream *dout_con_ptr = &dummyout;
std::ostream *derr_con_ptr = &verbosestream;
// Server
std::ostream *dout_server_ptr = &infostream;
std::ostream *derr_server_ptr = &errorstream;
// Client
std::ostream *dout_client_ptr = &infostream;
std::ostream *derr_client_ptr = &errorstream;
#ifndef SERVER
/*
Random stuff
*/
/* mainmenumanager.h */
gui::IGUIEnvironment* guienv = NULL;
gui::IGUIStaticText *guiroot = NULL;
MainMenuManager g_menumgr;
bool noMenuActive()
{
return (g_menumgr.menuCount() == 0);
}
// Passed to menus to allow disconnecting and exiting
MainGameCallback *g_gamecallback = NULL;
#endif
/*
gettime.h implementation
*/
#ifdef SERVER
u32 getTimeMs()
{
/* Use imprecise system calls directly (from porting.h) */
return porting::getTime(PRECISION_MILLI);
}
u32 getTime(TimePrecision prec)
{
return porting::getTime(prec);
}
#else
// A small helper class
class TimeGetter
{
public:
virtual u32 getTime(TimePrecision prec) = 0;
};
// A precise irrlicht one
class IrrlichtTimeGetter: public TimeGetter
{
public:
IrrlichtTimeGetter(IrrlichtDevice *device):
m_device(device)
{}
u32 getTime(TimePrecision prec)
{
if (prec == PRECISION_MILLI) {
2014-06-01 00:55:23 +02:00
if (m_device == NULL)
return 0;
return m_device->getTimer()->getRealTime();
} else {
return porting::getTime(prec);
}
}
private:
IrrlichtDevice *m_device;
};
// Not so precise one which works without irrlicht
class SimpleTimeGetter: public TimeGetter
{
public:
u32 getTime(TimePrecision prec)
{
return porting::getTime(prec);
}
};
// A pointer to a global instance of the time getter
// TODO: why?
TimeGetter *g_timegetter = NULL;
u32 getTimeMs()
{
2014-06-01 00:55:23 +02:00
if (g_timegetter == NULL)
return 0;
return g_timegetter->getTime(PRECISION_MILLI);
}
u32 getTime(TimePrecision prec) {
if (g_timegetter == NULL)
return 0;
return g_timegetter->getTime(prec);
}
#endif
class StderrLogOutput: public ILogOutput
{
public:
/* line: Full line with timestamp, level and thread */
void printLog(const std::string &line)
{
2014-06-01 00:55:23 +02:00
std::cerr << line << std::endl;
}
} main_stderr_log_out;
class DstreamNoStderrLogOutput: public ILogOutput
{
public:
/* line: Full line with timestamp, level and thread */
void printLog(const std::string &line)
{
2014-06-01 00:55:23 +02:00
dstream_no_stderr << line << std::endl;
}
} main_dstream_no_stderr_log_out;
#ifndef SERVER
/*
Event handler for Irrlicht
NOTE: Everything possible should be moved out from here,
probably to InputHandler and the_game
*/
class MyEventReceiver : public IEventReceiver
{
public:
// This is the one method that we have to implement
virtual bool OnEvent(const SEvent& event)
{
/*
React to nothing here if a menu is active
*/
2014-06-01 00:55:23 +02:00
if (noMenuActive() == false) {
#ifdef HAVE_TOUCHSCREENGUI
if (m_touchscreengui != 0) {
m_touchscreengui->Toggle(false);
}
#endif
2013-08-19 11:26:51 +02:00
return g_menumgr.preprocessEvent(event);
}
// Remember whether each key is down or up
2014-06-01 00:55:23 +02:00
if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
if (event.KeyInput.PressedDown) {
keyIsDown.set(event.KeyInput);
keyWasDown.set(event.KeyInput);
} else {
keyIsDown.unset(event.KeyInput);
}
}
#ifdef HAVE_TOUCHSCREENGUI
// case of touchscreengui we have to handle different events
if ((m_touchscreengui != 0) &&
(event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
m_touchscreengui->translateEvent(event);
return true;
}
#endif
// handle mouse events
if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
2014-06-01 00:55:23 +02:00
if (noMenuActive() == false) {
left_active = false;
middle_active = false;
right_active = false;
2014-06-01 00:55:23 +02:00
} else {
left_active = event.MouseInput.isLeftPressed();
middle_active = event.MouseInput.isMiddlePressed();
right_active = event.MouseInput.isRightPressed();
2014-06-01 00:55:23 +02:00
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
leftclicked = true;
}
2014-06-01 00:55:23 +02:00
if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
rightclicked = true;
}
2014-06-01 00:55:23 +02:00
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
leftreleased = true;
}
2014-06-01 00:55:23 +02:00
if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
rightreleased = true;
}
2014-06-01 00:55:23 +02:00
if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
mouse_wheel += event.MouseInput.Wheel;
}
}
}
if(event.EventType == irr::EET_LOG_TEXT_EVENT) {
dstream<< std::string("Irrlicht log: ") + std::string(event.LogEvent.Text)<<std::endl;
return true;
}
2014-04-20 10:55:08 +02:00
/* always return false in order to continue processing events */
return false;
}
bool IsKeyDown(const KeyPress &keyCode) const
{
return keyIsDown[keyCode];
}
// Checks whether a key was down and resets the state
bool WasKeyDown(const KeyPress &keyCode)
{
bool b = keyWasDown[keyCode];
if (b)
keyWasDown.unset(keyCode);
return b;
}
s32 getMouseWheel()
{
s32 a = mouse_wheel;
mouse_wheel = 0;
return a;
}
void clearInput()
{
keyIsDown.clear();
keyWasDown.clear();
leftclicked = false;
rightclicked = false;
leftreleased = false;
rightreleased = false;
left_active = false;
middle_active = false;
right_active = false;
mouse_wheel = 0;
}
MyEventReceiver()
{
clearInput();
#ifdef HAVE_TOUCHSCREENGUI
m_touchscreengui = NULL;
#endif
}
bool leftclicked;
bool rightclicked;
bool leftreleased;
bool rightreleased;
bool left_active;
bool middle_active;
bool right_active;
s32 mouse_wheel;
#ifdef HAVE_TOUCHSCREENGUI
TouchScreenGUI* m_touchscreengui;
#endif
private:
// The current state of keys
KeyList keyIsDown;
// Whether a key has been pressed or not
KeyList keyWasDown;
};
/*
Separated input handler
*/
class RealInputHandler : public InputHandler
{
public:
RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
m_device(device),
m_receiver(receiver),
m_mousepos(0,0)
{
}
virtual bool isKeyDown(const KeyPress &keyCode)
{
return m_receiver->IsKeyDown(keyCode);
}
virtual bool wasKeyDown(const KeyPress &keyCode)
{
return m_receiver->WasKeyDown(keyCode);
}
virtual v2s32 getMousePos()
{
if (m_device->getCursorControl()) {
return m_device->getCursorControl()->getPosition();
}
else {
return m_mousepos;
}
}
virtual void setMousePos(s32 x, s32 y)
{
if (m_device->getCursorControl()) {
m_device->getCursorControl()->setPosition(x, y);
}
else {
m_mousepos = v2s32(x,y);
}
}
virtual bool getLeftState()
{
return m_receiver->left_active;
}
virtual bool getRightState()
{
return m_receiver->right_active;
}
virtual bool getLeftClicked()
{
return m_receiver->leftclicked;
}
virtual bool getRightClicked()
{
return m_receiver->rightclicked;
}
virtual void resetLeftClicked()
{
m_receiver->leftclicked = false;
}
virtual void resetRightClicked()
{
m_receiver->rightclicked = false;
}
virtual bool getLeftReleased()
{
return m_receiver->leftreleased;
}
virtual bool getRightReleased()
{
return m_receiver->rightreleased;
}
virtual void resetLeftReleased()
{
m_receiver->leftreleased = false;
}
virtual void resetRightReleased()
{
m_receiver->rightreleased = false;
}
virtual s32 getMouseWheel()
{
return m_receiver->getMouseWheel();
}
void clear()
{
m_receiver->clearInput();
}
private:
IrrlichtDevice *m_device;
MyEventReceiver *m_receiver;
v2s32 m_mousepos;
};
class RandomInputHandler : public InputHandler
{
public:
RandomInputHandler()
{
leftdown = false;
rightdown = false;
leftclicked = false;
rightclicked = false;
leftreleased = false;
rightreleased = false;
keydown.clear();
}
virtual bool isKeyDown(const KeyPress &keyCode)
{
return keydown[keyCode];
}
virtual bool wasKeyDown(const KeyPress &keyCode)
{
return false;
}
virtual v2s32 getMousePos()
{
return mousepos;
}
virtual void setMousePos(s32 x, s32 y)
{
2014-06-01 00:55:23 +02:00
mousepos = v2s32(x, y);
}
virtual bool getLeftState()
{
return leftdown;
}
virtual bool getRightState()
{
return rightdown;
}
virtual bool getLeftClicked()
{
return leftclicked;
}
virtual bool getRightClicked()
{
return rightclicked;
}
virtual void resetLeftClicked()
{
leftclicked = false;
}
virtual void resetRightClicked()
{
rightclicked = false;
}
virtual bool getLeftReleased()
{
return leftreleased;
}
virtual bool getRightReleased()
{
return rightreleased;
}
virtual void resetLeftReleased()
{
leftreleased = false;
}
virtual void resetRightReleased()
{
rightreleased = false;
}
virtual s32 getMouseWheel()
{
return 0;
}
virtual void step(float dtime)
{
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_jump"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_special1"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_forward"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_left"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 20);
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 30);
leftdown = !leftdown;
2014-06-01 00:55:23 +02:00
if (leftdown)
leftclicked = true;
2014-06-01 00:55:23 +02:00
if (!leftdown)
leftreleased = true;
}
}
{
static float counter1 = 0;
counter1 -= dtime;
2014-06-01 00:55:23 +02:00
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 15);
rightdown = !rightdown;
2014-06-01 00:55:23 +02:00
if (rightdown)
rightclicked = true;
2014-06-01 00:55:23 +02:00
if (!rightdown)
rightreleased = true;
}
}
mousepos += mousespeed;
}
s32 Rand(s32 min, s32 max)
{
return (myrand()%(max-min+1))+min;
}
private:
KeyList keydown;
v2s32 mousepos;
v2s32 mousespeed;
bool leftdown;
bool rightdown;
bool leftclicked;
bool rightclicked;
bool leftreleased;
bool rightreleased;
};
#endif // !SERVER
// These are defined global so that they're not optimized too much.
// Can't change them to volatile.
s16 temp16;
f32 tempf;
v3f tempv3f1;
v3f tempv3f2;
std::string tempstring;
std::string tempstring2;
void SpeedTests()
{
{
2014-06-01 00:55:23 +02:00
infostream << "The following test should take around 20ms." << std::endl;
TimeTaker timer("Testing std::string speed");
const u32 jj = 10000;
2014-06-01 00:55:23 +02:00
for(u32 j = 0; j < jj; j++) {
tempstring = "";
tempstring2 = "";
const u32 ii = 10;
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < ii; i++) {
tempstring2 += "asd";
}
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < ii+1; i++) {
tempstring += "asd";
2014-06-01 00:55:23 +02:00
if (tempstring == tempstring2)
break;
}
}
}
2014-06-01 00:55:23 +02:00
infostream << "All of the following tests should take around 100ms each."
<< std::endl;
{
TimeTaker timer("Testing floating-point conversion speed");
tempf = 0.001;
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < 4000000; i++) {
temp16 += tempf;
tempf += 0.001;
}
}
{
TimeTaker timer("Testing floating-point vector speed");
2014-06-01 00:55:23 +02:00
tempv3f1 = v3f(1, 2, 3);
tempv3f2 = v3f(4, 5, 6);
for(u32 i = 0; i < 10000000; i++) {
tempf += tempv3f1.dotProduct(tempv3f2);
2014-06-01 00:55:23 +02:00
tempv3f2 += v3f(7, 8, 9);
}
}
{
2012-12-20 18:19:49 +01:00
TimeTaker timer("Testing std::map speed");
2012-12-20 18:19:49 +01:00
std::map<v2s16, f32> map1;
tempf = -324;
2014-06-01 00:55:23 +02:00
const s16 ii = 300;
for(s16 y = 0; y < ii; y++) {
for(s16 x = 0; x < ii; x++) {
map1[v2s16(x, y)] = tempf;
tempf += 1;
}
}
2014-06-01 00:55:23 +02:00
for(s16 y = ii - 1; y >= 0; y--) {
for(s16 x = 0; x < ii; x++) {
tempf = map1[v2s16(x, y)];
}
}
}
{
2014-06-01 00:55:23 +02:00
infostream << "Around 5000/ms should do well here." << std::endl;
TimeTaker timer("Testing mutex speed");
JMutex m;
u32 n = 0;
u32 i = 0;
2014-06-01 00:55:23 +02:00
do {
n += 10000;
2014-06-01 00:55:23 +02:00
for(; i < n; i++) {
m.Lock();
m.Unlock();
}
}
// Do at least 10ms
while(timer.getTimerTime() < 10);
u32 dtime = timer.stop();
u32 per_ms = n / dtime;
2014-06-01 00:55:23 +02:00
infostream << "Done. " << dtime << "ms, " << per_ms << "/ms" << std::endl;
}
}
2014-06-01 00:55:23 +02:00
static void print_worldspecs(const std::vector<WorldSpec> &worldspecs, std::ostream &os)
{
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < worldspecs.size(); i++) {
std::string name = worldspecs[i].name;
std::string path = worldspecs[i].path;
2014-06-01 00:55:23 +02:00
if (name.find(" ") != std::string::npos)
name = std::string("'") + name + "'";
path = std::string("'") + path + "'";
name = padStringRight(name, 14);
2014-06-01 00:55:23 +02:00
os << " " << name << " " << path << std::endl;
}
}
int main(int argc, char *argv[])
{
2012-03-11 11:06:59 +01:00
int retval = 0;
/*
Initialization
*/
2011-10-17 09:46:16 +02:00
log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
log_add_output_all_levs(&main_dstream_no_stderr_log_out);
log_register_thread("main");
/*
Parse command line
*/
// List all allowed options
2012-12-20 18:19:49 +01:00
std::map<std::string, ValueSpec> allowed_options;
allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
_("Show allowed options"))));
2013-09-25 04:47:44 +02:00
allowed_options.insert(std::make_pair("version", ValueSpec(VALUETYPE_FLAG,
_("Show version information"))));
2012-12-20 18:19:49 +01:00
allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
_("Load configuration from specified file"))));
allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
_("Set network port (UDP)"))));
allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
_("Disable unit tests"))));
allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
_("Enable unit tests"))));
allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
_("Same as --world (deprecated)"))));
allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
_("Set world path (implies local game) ('list' lists all)"))));
allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
_("Set world by name (implies local game)"))));
allowed_options.insert(std::make_pair("quiet", ValueSpec(VALUETYPE_FLAG,
_("Print to console errors only"))));
2012-12-20 18:19:49 +01:00
allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
_("Print more information to console"))));
allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG,
_("Print even more information to console"))));
allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
_("Print enormous amounts of information to log and console"))));
allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
_("Set logfile path ('' = no logging)"))));
allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
_("Set gameid (\"--gameid list\" prints available ones)"))));
2013-09-06 18:22:12 +02:00
allowed_options.insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING,
_("Migrate from current map backend to another (Only works when using minetestserver or with --server)"))));
#ifndef SERVER
allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
_("Show available video modes"))));
2012-12-20 18:19:49 +01:00
allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
_("Run speed tests"))));
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
_("Address to connect to. ('' = local game)"))));
allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
_("Enable random user input, for testing"))));
allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
_("Run dedicated server"))));
allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
_("Set player name"))));
allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
_("Set password"))));
allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
_("Disable main menu"))));
#endif
Settings cmd_args;
bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
2014-06-01 00:55:23 +02:00
if (ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1")) {
dstream << _("Allowed options:") << std::endl;
2012-12-20 18:19:49 +01:00
for(std::map<std::string, ValueSpec>::iterator
i = allowed_options.begin();
2014-06-01 00:55:23 +02:00
i != allowed_options.end(); ++i) {
2012-03-11 10:02:22 +01:00
std::ostringstream os1(std::ios::binary);
2014-06-01 00:55:23 +02:00
os1 << " --"<<i->first;
if (i->second.type == VALUETYPE_FLAG) {
} else
os1 << _(" <value>");
dstream << padStringRight(os1.str(), 24);
if (i->second.help != NULL)
dstream << i->second.help;
dstream << std::endl;
}
return cmd_args.getFlag("help") ? 0 : 1;
}
2013-09-25 04:47:44 +02:00
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("version")) {
2013-09-25 04:47:44 +02:00
#ifdef SERVER
2014-06-01 00:55:23 +02:00
dstream << "minetestserver " << minetest_version_hash << std::endl;
2013-09-25 04:47:44 +02:00
#else
2014-06-01 00:55:23 +02:00
dstream << "Minetest " << minetest_version_hash << std::endl;
dstream << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
2013-09-25 04:47:44 +02:00
#endif
2014-06-01 00:55:23 +02:00
dstream << "Build info: " << minetest_build_info << std::endl;
2013-09-25 04:47:44 +02:00
return 0;
}
/*
Low-level initialization
*/
// Quiet mode, print errors only
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("quiet")) {
log_remove_output(&main_stderr_log_out);
log_add_output_maxlev(&main_stderr_log_out, LMT_ERROR);
}
// If trace is enabled, enable logging of certain things
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("trace")) {
dstream << _("Enabling trace level debug output") << std::endl;
log_trace_level_enabled = true;
dout_con_ptr = &verbosestream; // this is somewhat old crap
socket_enable_debug_output = true; // socket doesn't use log.h
}
2012-03-11 11:06:59 +01:00
// In certain cases, output info level on stderr
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
2011-10-17 09:46:16 +02:00
log_add_output(&main_stderr_log_out, LMT_INFO);
// In certain cases, output verbose level on stderr
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
log_add_output(&main_stderr_log_out, LMT_VERBOSE);
porting::signal_handler_init();
bool &kill = *porting::signal_handler_killstatus();
porting::initializePaths();
#ifdef __ANDROID__
porting::initAndroid();
porting::setExternalStorageDir(porting::jnienv);
if (!fs::PathExists(porting::path_user)) {
fs::CreateDir(porting::path_user);
}
porting::copyAssets();
#else
// Create user data directory
2012-03-10 14:56:24 +01:00
fs::CreateDir(porting::path_user);
#endif
2014-06-01 00:55:23 +02:00
infostream << "path_share = " << porting::path_share << std::endl;
infostream << "path_user = " << porting::path_user << std::endl;
2012-03-11 14:28:35 +01:00
// Initialize debug stacks
debug_stacks_init();
DSTACK(__FUNCTION_NAME);
// Debug handler
BEGIN_DEBUG_EXCEPTION_HANDLER
2012-03-11 14:20:42 +01:00
// List gameids if requested
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") {
2012-03-11 14:20:42 +01:00
std::set<std::string> gameids = getAvailableGameIds();
for(std::set<std::string>::const_iterator i = gameids.begin();
i != gameids.end(); i++)
dstream<<(*i)<<std::endl;
return 0;
}
2012-05-20 16:09:46 +02:00
// List worlds if requested
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("world") && cmd_args.get("world") == "list") {
dstream << _("Available worlds:") << std::endl;
2012-05-20 16:09:46 +02:00
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
print_worldspecs(worldspecs, dstream);
return 0;
}
// Print startup message
2014-06-01 00:55:23 +02:00
infostream<<PROJECT_NAME << " "<< _("with") << " SER_FMT_VER_HIGHEST_READ="
<< (int)SER_FMT_VER_HIGHEST_READ << ", " << minetest_build_info << std::endl;
/*
Basic initialization
*/
// Initialize default settings
2011-10-12 12:53:38 +02:00
set_default_settings(g_settings);
// Initialize sockets
sockets_init();
atexit(sockets_cleanup);
/*
Read config file
*/
// Path of configuration file in use
g_settings_path = "";
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("config")) {
2011-10-12 12:53:38 +02:00
bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
2014-06-01 00:55:23 +02:00
if (r == false) {
errorstream << "Could not read configuration from \""
<< cmd_args.get("config") << "\"" << std::endl;
return 1;
}
g_settings_path = cmd_args.get("config");
2014-06-01 00:55:23 +02:00
} else {
2012-12-20 18:19:49 +01:00
std::vector<std::string> filenames;
2012-03-10 14:56:24 +01:00
filenames.push_back(porting::path_user +
DIR_DELIM + "minetest.conf");
2012-03-10 15:10:26 +01:00
// Legacy configuration file location
filenames.push_back(porting::path_user +
DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
2012-07-23 14:23:33 +02:00
#if RUN_IN_PLACE
2012-03-10 14:56:24 +01:00
// Try also from a lower level (to aid having the same configuration
// for many RUN_IN_PLACE installs)
filenames.push_back(porting::path_user +
2012-03-10 15:10:26 +01:00
DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
#endif
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < filenames.size(); i++) {
2011-10-12 12:53:38 +02:00
bool r = g_settings->readConfigFile(filenames[i].c_str());
2014-06-01 00:55:23 +02:00
if (r) {
g_settings_path = filenames[i];
break;
}
}
// If no path found, use the first one (menu creates the file)
2014-06-01 00:55:23 +02:00
if (g_settings_path == "")
g_settings_path = filenames[0];
}
2013-02-09 03:52:23 +01:00
// Initialize debug streams
#define DEBUGFILE "debug.txt"
#if RUN_IN_PLACE
std::string logfile = DEBUGFILE;
#else
std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
#endif
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("logfile"))
2013-02-09 03:52:23 +01:00
logfile = cmd_args.get("logfile");
2013-02-09 03:52:23 +01:00
log_remove_output(&main_dstream_no_stderr_log_out);
int loglevel = g_settings->getS32("debug_log_level");
if (loglevel == 0) //no logging
logfile = "";
else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
2014-06-01 00:55:23 +02:00
log_add_output_maxlev(&main_dstream_no_stderr_log_out,
(LogMessageLevel)(loglevel - 1));
2013-02-09 03:52:23 +01:00
2014-06-01 00:55:23 +02:00
if (logfile != "")
2013-02-09 03:52:23 +01:00
debugstreams_init(false, logfile.c_str());
else
debugstreams_init(false, NULL);
2014-06-01 00:55:23 +02:00
infostream << "logfile = " << logfile << std::endl;
// Initialize random seed
srand(time(0));
mysrand(time(0));
// Initialize HTTP fetcher
httpfetch_init(g_settings->getS32("curl_parallel_limit"));
#ifndef __ANDROID__
/*
Run unit tests
*/
2014-06-01 00:55:23 +02:00
if ((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
|| cmd_args.getFlag("enable-unittests") == true) {
run_tests();
}
#endif
#ifdef _MSC_VER
2014-06-01 00:55:23 +02:00
init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
g_settings->get("language"), argc, argv);
#else
2014-06-01 00:55:23 +02:00
init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
g_settings->get("language"));
#endif
/*
Game parameters
*/
// Port
u16 port = 30000;
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("port"))
port = cmd_args.getU16("port");
2014-06-01 00:55:23 +02:00
else if (g_settings->exists("port"))
2011-10-12 12:53:38 +02:00
port = g_settings->getU16("port");
2014-06-01 00:55:23 +02:00
if (port == 0)
port = 30000;
// World directory
std::string commanded_world = "";
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("world"))
commanded_world = cmd_args.get("world");
2014-06-01 00:55:23 +02:00
else if (cmd_args.exists("map-dir"))
commanded_world = cmd_args.get("map-dir");
2014-06-01 00:55:23 +02:00
else if (cmd_args.exists("nonopt0")) // First nameless argument
commanded_world = cmd_args.get("nonopt0");
2014-06-01 00:55:23 +02:00
else if (g_settings->exists("map-dir"))
commanded_world = g_settings->get("map-dir");
// World name
std::string commanded_worldname = "";
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("worldname"))
commanded_worldname = cmd_args.get("worldname");
// Strip world.mt from commanded_world
{
std::string worldmt = "world.mt";
2014-06-01 00:55:23 +02:00
if (commanded_world.size() > worldmt.size() &&
commanded_world.substr(commanded_world.size() - worldmt.size())
== worldmt) {
dstream << _("Supplied world.mt file - stripping it off.") << std::endl;
commanded_world = commanded_world.substr(0,
2014-06-01 00:55:23 +02:00
commanded_world.size() - worldmt.size());
}
}
// If a world name was specified, convert it to a path
2014-06-01 00:55:23 +02:00
if (commanded_worldname != "") {
// Get information about available worlds
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
bool found = false;
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < worldspecs.size(); i++) {
std::string name = worldspecs[i].name;
2014-06-01 00:55:23 +02:00
if (name == commanded_worldname) {
if (commanded_world != "") {
dstream << _("--worldname takes precedence over previously "
"selected world.") << std::endl;
}
commanded_world = worldspecs[i].path;
found = true;
break;
}
}
2014-06-01 00:55:23 +02:00
if (!found) {
dstream << _("World") << " '"<<commanded_worldname << _("' not "
"available. Available worlds:") << std::endl;
print_worldspecs(worldspecs, dstream);
return 1;
}
}
// Gamespec
SubgameSpec commanded_gamespec;
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("gameid")) {
std::string gameid = cmd_args.get("gameid");
commanded_gamespec = findSubgame(gameid);
2014-06-01 00:55:23 +02:00
if (!commanded_gamespec.isValid()) {
errorstream << "Game \"" << gameid << "\" not found" << std::endl;
2012-03-11 13:54:23 +01:00
return 1;
}
}
/*
Run dedicated server if asked to or no other option
*/
#ifdef SERVER
bool run_dedicated_server = true;
#else
bool run_dedicated_server = cmd_args.getFlag("server");
#endif
2013-02-21 23:00:44 +01:00
g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
2014-06-01 00:55:23 +02:00
if (run_dedicated_server)
{
DSTACK("Dedicated server branch");
// Create time getter if built with Irrlicht
#ifndef SERVER
g_timegetter = new SimpleTimeGetter();
#endif
// World directory
std::string world_path;
2014-06-01 00:55:23 +02:00
verbosestream << _("Determining world path") << std::endl;
bool is_legacy_world = false;
// If a world was commanded, use it
2014-06-01 00:55:23 +02:00
if (commanded_world != "") {
world_path = commanded_world;
2014-06-01 00:55:23 +02:00
infostream << "Using commanded world path [" << world_path << "]"
<< std::endl;
} else { // No world was specified; try to select it automatically
// Get information about available worlds
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
// If a world name was specified, select it
2014-06-01 00:55:23 +02:00
if (commanded_worldname != "") {
world_path = "";
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < worldspecs.size(); i++) {
std::string name = worldspecs[i].name;
2014-06-01 00:55:23 +02:00
if (name == commanded_worldname) {
world_path = worldspecs[i].path;
break;
}
}
2014-06-01 00:55:23 +02:00
if (world_path == "") {
dstream << _("World") << " '" << commanded_worldname << "' " << _("not "
"available. Available worlds:") << std::endl;
print_worldspecs(worldspecs, dstream);
return 1;
}
}
// If there is only a single world, use it
2014-06-01 00:55:23 +02:00
if (worldspecs.size() == 1) {
world_path = worldspecs[0].path;
2014-06-01 00:55:23 +02:00
dstream <<_("Automatically selecting world at") << " ["
<< world_path << "]" << std::endl;
// If there are multiple worlds, list them
2014-06-01 00:55:23 +02:00
} else if (worldspecs.size() > 1) {
dstream << _("Multiple worlds are available.") << std::endl;
dstream << _("Please select one using --worldname <name>"
" or --world <path>") << std::endl;
print_worldspecs(worldspecs, dstream);
return 1;
// If there are no worlds, automatically create a new one
} else {
// This is the ultimate default world path
world_path = porting::path_user + DIR_DELIM + "worlds" +
DIR_DELIM + "world";
2014-06-01 00:55:23 +02:00
infostream << "Creating default world at ["
<< world_path << "]" << std::endl;
}
}
2014-06-01 00:55:23 +02:00
if (world_path == "") {
errorstream << "No world path specified or found." << std::endl;
return 1;
}
2014-06-01 00:55:23 +02:00
verbosestream << _("Using world path") << " [" << world_path << "]" << std::endl;
// We need a gamespec.
SubgameSpec gamespec;
2014-06-01 00:55:23 +02:00
verbosestream << _("Determining gameid/gamespec") << std::endl;
// If world doesn't exist
2014-06-01 00:55:23 +02:00
if (!getWorldExists(world_path)) {
// Try to take gamespec from command line
2014-06-01 00:55:23 +02:00
if (commanded_gamespec.isValid()) {
gamespec = commanded_gamespec;
2014-06-01 00:55:23 +02:00
infostream << "Using commanded gameid [" << gamespec.id << "]" << std::endl;
} else { // Otherwise we will be using "minetest"
gamespec = findSubgame(g_settings->get("default_game"));
2014-06-01 00:55:23 +02:00
infostream << "Using default gameid [" << gamespec.id << "]" << std::endl;
}
2014-06-01 00:55:23 +02:00
} else { // World exists
std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
// If commanded to use a gameid, do so
2014-06-01 00:55:23 +02:00
if (commanded_gamespec.isValid()) {
gamespec = commanded_gamespec;
2014-06-01 00:55:23 +02:00
if (commanded_gamespec.id != world_gameid) {
errorstream << "WARNING: Using commanded gameid ["
<< gamespec.id << "]" << " instead of world gameid ["
<< world_gameid << "]" << std::endl;
}
2014-06-01 00:55:23 +02:00
} else {
// If world contains an embedded game, use it;
// Otherwise find world from local system.
gamespec = findWorldSubgame(world_path);
2014-06-01 00:55:23 +02:00
infostream << "Using world gameid [" << gamespec.id << "]" << std::endl;
}
}
2014-06-01 00:55:23 +02:00
if (!gamespec.isValid()) {
errorstream << "Subgame [" << gamespec.id << "] could not be found."
<< std::endl;
return 1;
}
2014-06-01 00:55:23 +02:00
verbosestream << _("Using gameid") << " [" << gamespec.id<<"]" << std::endl;
// Bind address
std::string bind_str = g_settings->get("bind_address");
2014-06-01 00:55:23 +02:00
Address bind_addr(0, 0, 0, 0, port);
if (g_settings->getBool("ipv6_server")) {
bind_addr.setAddress((IPv6AddressBytes*) NULL);
}
try {
bind_addr.Resolve(bind_str.c_str());
} catch (ResolveError &e) {
infostream << "Resolving bind address \"" << bind_str
<< "\" failed: " << e.what()
<< " -- Listening on all addresses." << std::endl;
}
2014-06-01 00:55:23 +02:00
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
errorstream << "Unable to listen on "
<< bind_addr.serializeString()
<< L" because IPv6 is disabled" << std::endl;
return 1;
}
// Create server
Server server(world_path, gamespec, false, bind_addr.isIPv6());
// Database migration
if (cmd_args.exists("migrate")) {
std::string migrate_to = cmd_args.get("migrate");
Settings world_mt;
bool success = world_mt.readConfigFile((world_path + DIR_DELIM
2014-06-01 00:55:23 +02:00
+ "world.mt").c_str());
if (!success) {
errorstream << "Cannot read world.mt" << std::endl;
return 1;
}
if (!world_mt.exists("backend")) {
errorstream << "Please specify your current backend in world.mt file:"
2014-04-08 21:39:21 +02:00
<< std::endl << " backend = {sqlite3|leveldb|redis|dummy}" << std::endl;
return 1;
}
std::string backend = world_mt.get("backend");
Database *new_db;
if (backend == migrate_to) {
2014-06-01 00:55:23 +02:00
errorstream << "Cannot migrate: new backend is same"
<<" as the old one" << std::endl;
return 1;
}
if (migrate_to == "sqlite3")
new_db = new Database_SQLite3(&(ServerMap&)server.getMap(), world_path);
#if USE_LEVELDB
else if (migrate_to == "leveldb")
new_db = new Database_LevelDB(&(ServerMap&)server.getMap(), world_path);
#endif
2014-04-08 21:39:21 +02:00
#if USE_REDIS
else if (migrate_to == "redis")
new_db = new Database_Redis(&(ServerMap&)server.getMap(), world_path);
#endif
else {
errorstream << "Migration to " << migrate_to
2014-06-01 00:55:23 +02:00
<< " is not supported" << std::endl;
return 1;
}
2013-09-06 18:22:12 +02:00
std::list<v3s16> blocks;
ServerMap &old_map = ((ServerMap&)server.getMap());
old_map.listAllLoadableBlocks(blocks);
int count = 0;
new_db->beginSave();
2014-06-01 00:55:23 +02:00
for (std::list<v3s16>::iterator i = blocks.begin(); i != blocks.end(); i++) {
MapBlock *block = old_map.loadBlock(*i);
if (!block) {
errorstream << "Failed to load block " << PP(*i) << ", skipping it.";
} else {
old_map.saveBlock(block, new_db);
MapSector *sector = old_map.getSectorNoGenerate(v2s16(i->X, i->Z));
sector->deleteBlock(block);
}
++count;
if (count % 500 == 0)
actionstream << "Migrated " << count << " blocks "
<< (100.0 * count / blocks.size()) << "% completed" << std::endl;
}
new_db->endSave();
2014-02-05 18:52:59 +01:00
delete new_db;
actionstream << "Successfully migrated " << count << " blocks" << std::endl;
2013-09-09 20:07:25 +02:00
world_mt.set("backend", migrate_to);
2014-06-01 00:55:23 +02:00
if (!world_mt.updateConfigFile((world_path + DIR_DELIM + "world.mt").c_str()))
errorstream << "Failed to update world.mt!" << std::endl;
2013-09-09 20:07:25 +02:00
else
2014-06-01 00:55:23 +02:00
actionstream << "world.mt updated" << std::endl;
return 0;
}
server.start(bind_addr);
// Run server
dedicated_server_loop(server, kill);
return 0;
}
#ifndef SERVER // Exclude from dedicated server build
/*
More parameters
*/
2012-03-11 11:06:59 +01:00
std::string address = g_settings->get("address");
2014-06-01 00:55:23 +02:00
if (commanded_world != "")
address = "";
2014-06-01 00:55:23 +02:00
else if (cmd_args.exists("address"))
address = cmd_args.get("address");
2011-10-12 12:53:38 +02:00
std::string playername = g_settings->get("name");
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("name"))
2012-03-11 11:06:59 +01:00
playername = cmd_args.get("name");
2012-03-11 11:06:59 +01:00
bool skip_main_menu = cmd_args.getFlag("go");
/*
Device initialization
*/
// Resolution selection
bool fullscreen = g_settings->getBool("fullscreen");
2011-10-12 12:53:38 +02:00
u16 screenW = g_settings->getU16("screenW");
u16 screenH = g_settings->getU16("screenH");
// bpp, fsaa, vsync
bool vsync = g_settings->getBool("vsync");
u16 bits = g_settings->getU16("fullscreen_bpp");
u16 fsaa = g_settings->getU16("fsaa");
// Determine driver
video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
static const char* driverids[] = {
"null",
"software",
"burningsvideo",
"direct3d8",
"direct3d9",
"opengl"
2013-03-02 17:44:08 +01:00
#ifdef _IRR_COMPILE_WITH_OGLES1_
,"ogles1"
2013-03-02 17:44:08 +01:00
#endif
#ifdef _IRR_COMPILE_WITH_OGLES2_
,"ogles2"
2013-03-02 17:44:08 +01:00
#endif
,"invalid"
};
std::string driverstring = g_settings->get("video_driver");
for (unsigned int i = 0;
i < (sizeof(driverids)/sizeof(driverids[0]));
i++)
{
if (strcasecmp(driverstring.c_str(), driverids[i]) == 0) {
driverType = (video::E_DRIVER_TYPE) i;
break;
}
if (strcasecmp("invalid", driverids[i]) == 0) {
errorstream << "WARNING: Invalid video_driver specified; defaulting "
<< "to opengl" << std::endl;
break;
}
}
/*
List video modes if requested
*/
MyEventReceiver* receiver = new MyEventReceiver();
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("videomodes")) {
IrrlichtDevice *nulldevice;
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
params.DriverType = video::EDT_NULL;
params.WindowSize = core::dimension2d<u32>(640, 480);
params.Bits = 24;
params.AntiAlias = fsaa;
params.Fullscreen = false;
params.Stencilbuffer = false;
params.Vsync = vsync;
params.EventReceiver = receiver;
params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
nulldevice = createDeviceEx(params);
2014-06-01 00:55:23 +02:00
if (nulldevice == 0)
return 1;
2014-06-01 00:55:23 +02:00
dstream << _("Available video modes (WxHxD):") << std::endl;
video::IVideoModeList *videomode_list =
nulldevice->getVideoModeList();
2014-06-01 00:55:23 +02:00
if (videomode_list == 0) {
nulldevice->drop();
return 1;
}
s32 videomode_count = videomode_list->getVideoModeCount();
core::dimension2d<u32> videomode_res;
s32 videomode_depth;
2014-06-01 00:55:23 +02:00
for (s32 i = 0; i < videomode_count; ++i) {
videomode_res = videomode_list->getVideoModeResolution(i);
videomode_depth = videomode_list->getVideoModeDepth(i);
2014-06-01 00:55:23 +02:00
dstream<<videomode_res.Width << "x" << videomode_res.Height
<< "x" << videomode_depth << std::endl;
}
2014-06-01 00:55:23 +02:00
dstream << _("Active video mode (WxHxD):") << std::endl;
videomode_res = videomode_list->getDesktopResolution();
videomode_depth = videomode_list->getDesktopDepth();
2014-06-01 00:55:23 +02:00
dstream << videomode_res.Width << "x" << videomode_res.Height
<< "x" << videomode_depth << std::endl;
nulldevice->drop();
delete receiver;
return 0;
}
/*
Create device and exit if creation failed
*/
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
params.DriverType = driverType;
params.WindowSize = core::dimension2d<u32>(screenW, screenH);
params.Bits = bits;
params.AntiAlias = fsaa;
params.Fullscreen = fullscreen;
params.Stencilbuffer = false;
params.Vsync = vsync;
params.EventReceiver = receiver;
params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
#ifdef __ANDROID__
params.PrivateData = porting::app_global;
params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
"media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
#endif
IrrlichtDevice * device = createDeviceEx(params);
if (device == 0) {
delete receiver;
return 1; // could not create selected driver.
}
// Map our log level to irrlicht engine one.
static const irr::ELOG_LEVEL irr_log_level[5] = {
ELL_NONE,
ELL_ERROR,
ELL_WARNING,
ELL_INFORMATION,
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
ELL_INFORMATION
#else
ELL_DEBUG
#endif
};
ILogger* irr_logger = device->getLogger();
irr_logger->setLogLevel(irr_log_level[loglevel]);
porting::initIrrlicht(device);
late_init_default_settings(g_settings);
/*
Continue initialization
*/
video::IVideoDriver* driver = device->getVideoDriver();
/*
This changes the minimum allowed number of vertices in a VBO.
Default is 500.
*/
//driver->setMinHardwareBufferVertexCount(50);
// Create time getter
g_timegetter = new IrrlichtTimeGetter(device);
// Create game callback for menus
g_gamecallback = new MainGameCallback(device);
/*
Speed tests (done after irrlicht is loaded to get timer)
*/
2014-06-01 00:55:23 +02:00
if (cmd_args.getFlag("speedtests"))
{
2014-06-01 00:55:23 +02:00
dstream << "Running speed tests" << std::endl;
SpeedTests();
device->drop();
return 0;
}
device->setResizable(true);
2011-10-12 12:53:38 +02:00
bool random_input = g_settings->getBool("random_input")
|| cmd_args.getFlag("random-input");
InputHandler *input = NULL;
2014-06-01 00:55:23 +02:00
if (random_input) {
input = new RandomInputHandler();
} else {
input = new RealInputHandler(device,receiver);
}
scene::ISceneManager* smgr = device->getSceneManager();
guienv = device->getGUIEnvironment();
gui::IGUISkin* skin = guienv->getSkin();
2013-02-03 13:19:09 +01:00
std::string font_path = g_settings->get("font_path");
gui::IGUIFont *font;
#if USE_FREETYPE
bool use_freetype = g_settings->getBool("freetype");
if (use_freetype) {
std::string fallback;
if (is_yes(gettext("needs_fallback_font")))
fallback = "fallback_";
u16 font_size = g_settings->getU16(fallback + "font_size");
font_path = g_settings->get(fallback + "font_path");
2013-12-12 18:47:54 +01:00
u32 font_shadow = g_settings->getU16(fallback + "font_shadow");
2013-12-12 21:02:09 +01:00
u32 font_shadow_alpha = g_settings->getU16(fallback + "font_shadow_alpha");
2014-06-01 00:55:23 +02:00
font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size,
true, true, font_shadow, font_shadow_alpha);
} else {
font = guienv->getFont(font_path.c_str());
}
2013-02-03 13:19:09 +01:00
#else
font = guienv->getFont(font_path.c_str());
2013-02-03 13:19:09 +01:00
#endif
2014-06-01 00:55:23 +02:00
if (font)
skin->setFont(font);
else
2014-06-01 00:55:23 +02:00
errorstream << "WARNING: Font file was not found."
<< " Using default font." << std::endl;
// If font was not found, this will get us one
font = skin->getFont();
assert(font);
u32 text_height = font->getDimension(L"Hello, world!").Height;
2014-06-01 00:55:23 +02:00
infostream << "text_height=" << text_height << std::endl;
2014-06-01 00:55:23 +02:00
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 0, 0, 0));
skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 100, 50));
skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
// Irrlicht 1.8 input colours
2014-06-01 00:55:23 +02:00
skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
#endif
// Create the menu clouds
if (!g_menucloudsmgr)
g_menucloudsmgr = smgr->createNewSceneManager();
if (!g_menuclouds)
g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
g_menucloudsmgr, -1, rand(), 100);
2014-06-01 00:55:23 +02:00
g_menuclouds->update(v2f(0, 0), video::SColor(255, 200, 200, 255));
scene::ICameraSceneNode* camera;
camera = g_menucloudsmgr->addCameraSceneNode(0,
2014-06-01 00:55:23 +02:00
v3f(0, 0, 0), v3f(0, 60, 100));
camera->setFarValue(10000);
/*
GUI stuff
*/
ChatBackend chat_backend;
/*
If an error occurs, this is set to something and the
menu-game loop is restarted. It is then displayed before
the menu.
*/
std::wstring error_message = L"";
// The password entered during the menu screen,
std::string password;
bool first_loop = true;
/*
Menu-game loop
*/
while (device->run() && (kill == false) &&
(g_gamecallback->shutdown_requested == false))
{
2012-03-11 13:54:23 +01:00
// Set the window caption
wchar_t* text = wgettext("Main Menu");
2014-06-01 00:55:23 +02:00
device->setWindowCaption((std::wstring(L"Minetest [") + text + L"]").c_str());
delete[] text;
// This is used for catching disconnects
try
{
/*
Clear everything from the GUIEnvironment
*/
guienv->clear();
/*
We need some kind of a root node to be able to add
custom gui elements directly on the screen.
Otherwise they won't be automatically drawn.
*/
guiroot = guienv->addStaticText(L"",
2014-06-01 00:55:23 +02:00
core::rect<s32>(0, 0, 10000, 10000));
SubgameSpec gamespec;
WorldSpec worldspec;
bool simple_singleplayer_mode = false;
// These are set up based on the menu and other things
std::string current_playername = "inv£lid";
std::string current_password = "";
std::string current_address = "does-not-exist";
int current_port = 0;
/*
Out-of-game menu loop.
Loop quits when menu returns proper parameters.
*/
2014-06-01 00:55:23 +02:00
while (kill == false) {
// If skip_main_menu, only go through here once
2014-06-01 00:55:23 +02:00
if (skip_main_menu && !first_loop) {
kill = true;
break;
}
first_loop = false;
// Cursor can be non-visible when coming from the game
#ifndef ANDROID
device->getCursorControl()->setVisible(true);
#endif
// Some stuff are left to scene manager when coming from the game
// (map at least?)
smgr->clear();
// Initialize menu data
MainMenuData menudata;
menudata.address = address;
menudata.name = playername;
menudata.port = itos(port);
menudata.errormessage = wide_to_narrow(error_message);
error_message = L"";
2014-06-01 00:55:23 +02:00
if (cmd_args.exists("password"))
menudata.password = cmd_args.get("password");
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
menudata.enable_public = g_settings->getBool("server_announce");
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
// If a world was commanded, append and select it
if(commanded_world != "") {
worldspec.gameid = getWorldGameId(commanded_world, true);
worldspec.name = _("[--world parameter]");
if(worldspec.gameid == "") {
worldspec.gameid = g_settings->get("default_game");
worldspec.name += " [new]";
}
worldspec.path = commanded_world;
}
2014-06-01 00:55:23 +02:00
if (skip_main_menu == false) {
2012-03-26 01:19:41 +02:00
video::IVideoDriver* driver = device->getVideoDriver();
2014-06-01 00:55:23 +02:00
infostream << "Waiting for other menus" << std::endl;
while (device->run() && kill == false) {
if (noMenuActive())
2012-03-26 01:19:41 +02:00
break;
driver->beginScene(true, true,
2014-06-01 00:55:23 +02:00
video::SColor(255, 128, 128, 128));
2012-03-26 01:19:41 +02:00
guienv->drawAll();
driver->endScene();
// On some computers framerate doesn't seem to be
// automatically limited
sleep_ms(25);
}
2014-06-01 00:55:23 +02:00
infostream << "Waited for other menus" << std::endl;
2012-03-26 01:19:41 +02:00
/* show main menu */
GUIEngine mymenu(device, guiroot, &g_menumgr,smgr,&menudata,kill);
//once finished you'll never end up here
smgr->clear();
}
2013-01-31 18:03:14 +01:00
2014-06-01 00:55:23 +02:00
if (menudata.errormessage != "") {
error_message = narrow_to_wide(menudata.errormessage);
continue;
}
//update worldspecs (necessary as new world may have been created)
worldspecs = getAvailableWorlds();
if (menudata.name == "")
menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
else
playername = menudata.name;
password = translatePassword(playername, narrow_to_wide(menudata.password));
2012-03-15 13:17:05 +01:00
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
address = menudata.address;
int newport = stoi(menudata.port);
2014-06-01 00:55:23 +02:00
if (newport != 0)
port = newport;
simple_singleplayer_mode = menudata.simple_singleplayer_mode;
2012-03-15 13:17:05 +01:00
// Save settings
g_settings->set("name", playername);
2012-03-15 13:17:05 +01:00
// Break out of menu-game loop to shut down cleanly
2014-06-01 00:55:23 +02:00
if (device->run() == false || kill == true)
2012-03-15 13:17:05 +01:00
break;
current_playername = playername;
current_password = password;
current_address = address;
current_port = port;
// If using simple singleplayer mode, override
2014-06-01 00:55:23 +02:00
if (simple_singleplayer_mode) {
current_playername = "singleplayer";
current_password = "";
current_address = "";
2013-07-12 13:26:25 +02:00
current_port = myrand_range(49152, 65535);
2014-06-01 00:55:23 +02:00
} else if (address != "") {
ServerListSpec server;
2013-02-21 23:00:44 +01:00
server["name"] = menudata.servername;
server["address"] = menudata.address;
server["port"] = menudata.port;
2013-02-21 23:00:44 +01:00
server["description"] = menudata.serverdescription;
ServerList::insert(server);
}
if ((!skip_main_menu) &&
(menudata.selected_world >= 0) &&
(menudata.selected_world < (int)worldspecs.size())) {
g_settings->set("selected_world_path",
worldspecs[menudata.selected_world].path);
worldspec = worldspecs[menudata.selected_world];
}
infostream <<"Selected world: " << worldspec.name
<< " ["<<worldspec.path<<"]" <<std::endl;
// If local game
2014-06-01 00:55:23 +02:00
if (current_address == "") {
if (worldspec.path == "") {
error_message = wgettext("No world selected and no address "
"provided. Nothing to do.");
2014-06-01 00:55:23 +02:00
errorstream << wide_to_narrow(error_message) << std::endl;
continue;
}
if (!fs::PathExists(worldspec.path)) {
error_message = wgettext("Provided world path doesn't exist: ")
+ narrow_to_wide(worldspec.path);
errorstream << wide_to_narrow(error_message) << std::endl;
continue;
}
// Load gamespec for required game
gamespec = findWorldSubgame(worldspec.path);
2014-06-01 00:55:23 +02:00
if (!gamespec.isValid() && !commanded_gamespec.isValid()) {
error_message = wgettext("Could not find or load game \"")
+ narrow_to_wide(worldspec.gameid) + L"\"";
2014-06-01 00:55:23 +02:00
errorstream << wide_to_narrow(error_message) << std::endl;
continue;
}
2014-06-01 00:55:23 +02:00
if (commanded_gamespec.isValid() &&
commanded_gamespec.id != worldspec.gameid) {
errorstream<<"WARNING: Overriding gamespec from \""
2014-06-01 00:55:23 +02:00
<< worldspec.gameid << "\" to \""
<< commanded_gamespec.id << "\"" << std::endl;
gamespec = commanded_gamespec;
}
2014-06-01 00:55:23 +02:00
if (!gamespec.isValid()) {
error_message = wgettext("Invalid gamespec.");
error_message += L" (world_gameid="
2014-06-01 00:55:23 +02:00
+ narrow_to_wide(worldspec.gameid) + L")";
errorstream << wide_to_narrow(error_message) << std::endl;
continue;
}
}
// Continue to game
break;
}
2013-07-28 23:14:42 +02:00
// Break out of menu-game loop to shut down cleanly
2014-06-01 00:55:23 +02:00
if (device->run() == false || kill == true) {
if (g_settings_path != "") {
g_settings->updateConfigFile(g_settings_path.c_str());
}
break;
}
2014-08-22 14:03:04 +02:00
if (current_playername.length() > PLAYERNAME_SIZE-1) {
2014-08-22 21:51:20 +02:00
error_message = wgettext("Player name too long.");
2014-08-22 14:03:04 +02:00
playername = current_playername.substr(0,PLAYERNAME_SIZE-1);
g_settings->set("name", playername);
continue;
}
/*
Run game
*/
#ifdef HAVE_TOUCHSCREENGUI
receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
g_touchscreengui = receiver->m_touchscreengui;
#endif
the_game(
Refactor the_game() to make it more understandable and maintainable. The following is a record of 31 commits before squashing: Revert "Remove m_ext_ptr in GUIFormSpecMenu, replaced by refcount mechanism" This reverts commit b49e5cfc7013cef7e9af79d17e04f7e7e4c377d4. Basic reformatting with astyle -- additional formatting will be modified, manually, as the need for it is encountered Start "outlining" what a MinetestApp class might look like Add MinetestApp::shutdown() Converted class member functions to camelCase and created protos for new functions First stage of connect to server done Add get itemdefs/nodedefs/media code Init clouds, camera, sky, init GUI, HUD Input handling Client events, camera, sound, draw Fix wield hand getting stuck digging and add debug text back Fix FPS Added profiler graph back Fix FPS issue Need to work out what went wrong and clean up the copy/paste stuff Annotate Various: Rewrote limitFps() Limited scope of some variables Jitter calcs Reduce scope of objects Move some stuff out of ::run and minor formatting cleanup Scope reduction Function splits Removed old (broken) limitFps() Added exception handling back Fixed some formatting Reverted commented out unit tests (uncommented them) Slow clouds down on loading and media screens so the behaviour is like the original the_game() Formatting/style (no functional changes) Manually reapply upstream b49e5cf: Remove m_ext_ptr in GUIFormSpecMenu, replaced by refcount mechanism Fixed silly errors on my part Minor formatting cleanups Removed strange differentiation in FPS limiting when loading FPS limiting was done differently if cloud_menu_background was true, which does not make sense Cleaning up Add some comments
2014-10-31 13:13:04 +01:00
&kill,
random_input,
input,
device,
font,
worldspec.path,
current_playername,
current_password,
current_address,
current_port,
error_message,
2012-03-11 13:54:23 +01:00
chat_backend,
gamespec,
simple_singleplayer_mode
);
smgr->clear();
#ifdef HAVE_TOUCHSCREENGUI
delete g_touchscreengui;
g_touchscreengui = NULL;
receiver->m_touchscreengui = NULL;
#endif
} //try
catch(con::PeerNotFoundException &e)
{
error_message = wgettext("Connection error (timed out?)");
2014-06-01 00:55:23 +02:00
errorstream << wide_to_narrow(error_message) << std::endl;
}
#ifdef NDEBUG
catch(std::exception &e)
{
std::string narrow_message = "Some exception: \"";
narrow_message += e.what();
narrow_message += "\"";
2014-06-01 00:55:23 +02:00
errorstream << narrow_message << std::endl;
error_message = narrow_to_wide(narrow_message);
}
#endif
2012-03-11 11:06:59 +01:00
// If no main menu, show error and exit
2014-06-01 00:55:23 +02:00
if (skip_main_menu) {
if (error_message != L"") {
verbosestream << "error_message = "
<< wide_to_narrow(error_message) << std::endl;
2012-03-11 11:06:59 +01:00
retval = 1;
}
break;
}
} // Menu-game loop
g_menuclouds->drop();
g_menucloudsmgr->drop();
delete input;
/*
In the end, delete the Irrlicht device.
*/
device->drop();
2013-05-16 02:19:32 +02:00
#if USE_FREETYPE
if (use_freetype)
font->drop();
2013-05-16 02:19:32 +02:00
#endif
delete receiver;
#endif // !SERVER
// Update configuration file
2014-06-01 00:55:23 +02:00
if (g_settings_path != "")
g_settings->updateConfigFile(g_settings_path.c_str());
// Print modified quicktune values
{
bool header_printed = false;
std::vector<std::string> names = getQuicktuneNames();
2014-06-01 00:55:23 +02:00
for(u32 i = 0; i < names.size(); i++) {
QuicktuneValue val = getQuicktuneValue(names[i]);
2014-06-01 00:55:23 +02:00
if (!val.modified)
continue;
2014-06-01 00:55:23 +02:00
if (!header_printed) {
dstream << "Modified quicktune values:" << std::endl;
header_printed = true;
}
2014-06-01 00:55:23 +02:00
dstream<<names[i] << " = " << val.getString() << std::endl;
}
}
// Stop httpfetch thread (if started)
httpfetch_cleanup();
END_DEBUG_EXCEPTION_HANDLER(errorstream)
debugstreams_deinit();
2014-06-01 00:55:23 +02:00
2012-03-11 11:06:59 +01:00
return retval;
}
//END