X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmain.cpp;h=41cf583f5740c2bed78d1ca30dbb12040a3169a7;hb=82073025ccc551c2fd205cc1dc6fcecac61cc7ea;hp=bdfbc35f7d5ea6d93314b1f97909228c1a2db227;hpb=c578efb32b57b08275b9a0a2fd4a76b4e4b9e87d;p=minetest.git diff --git a/src/main.cpp b/src/main.cpp index bdfbc35f7..41cf583f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,1652 +1,1548 @@ -/* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU 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. -*/ - -/* -=============================== NOTES ============================== -NOTE: Things starting with TODO are sometimes only suggestions. - -NOTE: iostream.imbue(std::locale("C")) is very slow -NOTE: Global locale is now set at initialization - -NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the - hardware buffer (it is not freed automatically) - -NOTE: A random to-do list saved here as documentation: -A list of "active blocks" in which stuff happens. (+=done) - + Add a never-resetted game timer to the server - + Add a timestamp value to blocks - + The simple rule: All blocks near some player are "active" - - Do stuff in real time in active blocks - + Handle objects - - Grow grass, delete leaves without a tree - - Spawn some mobs based on some rules - - Transform cobble to mossy cobble near water - - Run a custom script - - ...And all kinds of other dynamic stuff - + Keep track of when a block becomes active and becomes inactive - + When a block goes inactive: - + Store objects statically to block - + Store timer value as the timestamp - + When a block goes active: - + Create active objects out of static objects - - Simulate the results of what would have happened if it would have - been active for all the time - - Grow a lot of grass and so on - + Initially it is fine to send information about every active object - to every player. Eventually it should be modified to only send info - about the nearest ones. - + This was left to be done by the old system and it sends only the - nearest ones. - -Old, wild and random suggestions that probably won't be done: -------------------------------------------------------------- - -SUGG: If player is on ground, mainly fetch ground-level blocks - -SUGG: Expose Connection's seqnums and ACKs to server and client. - - This enables saving many packets and making a faster connection - - This also enables server to check if client has received the - most recent block sent, for example. -SUGG: Add a sane bandwidth throttling system to Connection - -SUGG: More fine-grained control of client's dumping of blocks from - memory - - ...What does this mean in the first place? - -SUGG: A map editing mode (similar to dedicated server mode) - -SUGG: Transfer more blocks in a single packet -SUGG: A blockdata combiner class, to which blocks are added and at - destruction it sends all the stuff in as few packets as possible. -SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize - it by sending more stuff in a single packet. - - Add a packet queue to RemoteClient, from which packets will be - combined with object data packets - - This is not exactly trivial: the object data packets are - sometimes very big by themselves - - This might not give much network performance gain though. - -SUGG: Precalculate lighting translation table at runtime (at startup) - - This is not doable because it is currently hand-made and not - based on some mathematical function. - - Note: This has been changing lately - -SUGG: A version number to blocks, which increments when the block is - modified (node add/remove, water update, lighting update) - - This can then be used to make sure the most recent version of - a block has been sent to client, for example - -SUGG: Make the amount of blocks sending to client and the total - amount of blocks dynamically limited. Transferring blocks is the - main network eater of this system, so it is the one that has - to be throttled so that RTTs stay low. - -SUGG: Meshes of blocks could be split into 6 meshes facing into - different directions and then only those drawn that need to be - -SUGG: Background music based on cellular automata? - http://www.earslap.com/projectslab/otomata - -SUGG: Simple light color information to air - -SUGG: Server-side objects could be moved based on nodes to enable very - lightweight operation and simple AI - - Not practical; client would still need to show smooth movement. - -SUGG: Make a system for pregenerating quick information for mapblocks, so - that the client can show them as cubes before they are actually sent - or even generated. - -SUGG: Erosion simulation at map generation time - - This might be plausible if larger areas of map were pregenerated - without lighting (which is slow) - - Simulate water flows, which would carve out dirt fast and - then turn stone into gravel and sand and relocate it. - - How about relocating minerals, too? Coal and gold in - downstream sand and gravel would be kind of cool - - This would need a better way of handling minerals, mainly - to have mineral content as a separate field. the first - parameter field is free for this. - - Simulate rock falling from cliffs when water has removed - enough solid rock from the bottom - -SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface - stuff as simple flags/values - - Light? - - A building? - And at some point make the server send this data to the client too, - instead of referring to the noise functions - - Ground height - - Surface ground type - - Trees? - -Gaming ideas: -------------- - -- Aim for something like controlling a single dwarf in Dwarf Fortress -- The player could go faster by a crafting a boat, or riding an animal -- Random NPC traders. what else? - -Game content: -------------- - -- When furnace is destroyed, move items to player's inventory -- Add lots of stuff -- Glass blocks -- Growing grass, decaying leaves - - This can be done in the active blocks I guess. - - Lots of stuff can be done in the active blocks. - - Uh, is there an active block list somewhere? I think not. Add it. -- Breaking weak structures - - This can probably be accomplished in the same way as grass -- Player health points - - When player dies, throw items on map (needs better item-on-map - implementation) -- Cobble to get mossy if near water -- More slots in furnace source list, so that multiple ingredients - are possible. -- Keys to chests? - -- The Treasure Guard; a big monster with a hammer - - The hammer does great damage, shakes the ground and removes a block - - You can drop on top of it, and have some time to attack there - before he shakes you off - -- Maybe the difficulty could come from monsters getting tougher in - far-away places, and the player starting to need something from - there when time goes by. - - The player would have some of that stuff at the beginning, and - would need new supplies of it when it runs out - -- A bomb -- A spread-items-on-map routine for the bomb, and for dying players - -- Fighting: - - Proper sword swing simulation - - Player should get damage from colliding to a wall at high speed - -Documentation: --------------- - -Build system / running: ------------------------ - -Networking and serialization: ------------------------------ - -SUGG: Fix address to be ipv6 compatible - -User Interface: ---------------- - -Graphics: ---------- - -SUGG: Combine MapBlock's face caches to so big pieces that VBO - can be used - - That is >500 vertices - - This is not easy; all the MapBlocks close to the player would - still need to be drawn separately and combining the blocks - would have to happen in a background thread - -SUGG: Make fetching sector's blocks more efficient when rendering - sectors that have very large amounts of blocks (on client) - - Is this necessary at all? - -SUGG: Draw cubes in inventory directly with 3D drawing commands, so that - animating them is easier. - -SUGG: Option for enabling proper alpha channel for textures - -TODO: Flowing water animation - -TODO: A setting for enabling bilinear filtering for textures - -TODO: Better control of draw_control.wanted_max_blocks - -TODO: Further investigate the use of GPU lighting in addition to the - current one - -TODO: Artificial (night) light could be more yellow colored than sunlight. - - This is technically doable. - - Also the actual colors of the textures could be made less colorful - in the dark but it's a bit more difficult. - -SUGG: Somehow make the night less colorful - -TODO: Occlusion culling - - At the same time, move some of the renderMap() block choosing code - to the same place as where the new culling happens. - - Shoot some rays per frame and when ready, make a new list of - blocks for usage of renderMap and give it a new pointer to it. - -Configuration: --------------- - -Client: -------- - -TODO: Untie client network operations from framerate - - Needs some input queues or something - - This won't give much performance boost because calculating block - meshes takes so long - -SUGG: Make morning and evening transition more smooth and maybe shorter - -TODO: Don't update all meshes always on single node changes, but - check which ones should be updated - - implement Map::updateNodeMeshes() and the usage of it - - It will give almost always a 4x boost in mesh update performance. - -- A weapon engine - -- Tool/weapon visualization - -FIXME: When disconnected to the menu, memory is not freed properly - -TODO: Investigate how much the mesh generator thread gets used when - transferring map data - -Server: -------- - -SUGG: Make an option to the server to disable building and digging near - the starting position - -FIXME: Server sometimes goes into some infinite PeerNotFoundException loop - -* Fix the problem with the server constantly saving one or a few - blocks? List the first saved block, maybe it explains. - - It is probably caused by oscillating water - - TODO: Investigate if this still happens (this is a very old one) -* Make a small history check to transformLiquids to detect and log - continuous oscillations, in such detail that they can be fixed. - -FIXME: The new optimized map sending doesn't sometimes send enough blocks - from big caves and such -FIXME: Block send distance configuration does not take effect for some reason - -Environment: ------------- - -TODO: Add proper hooks to when adding and removing active blocks - -TODO: Finish the ActiveBlockModifier stuff and use it for something - -Objects: --------- - -TODO: Get rid of MapBlockObjects and use only ActiveObjects - - Skipping the MapBlockObject data is nasty - there is no "total - length" stored; have to make a SkipMBOs function which contains - enough of the current code to skip them properly. - -SUGG: MovingObject::move and Player::move are basically the same. - combine them. - - NOTE: This is a bit tricky because player has the sneaking ability - - NOTE: Player::move is more up-to-date. - - NOTE: There is a simple move implementation now in collision.{h,cpp} - - NOTE: MovingObject will be deleted (MapBlockObject) - -TODO: Add a long step function to objects that is called with the time - difference when block activates - -Map: ----- - -TODO: Mineral and ground material properties - - This way mineral ground toughness can be calculated with just - some formula, as well as tool strengths - - There are TODOs in appropriate files: material.h, content_mapnode.h - -TODO: Flowing water to actually contain flow direction information - - There is a space for this - it just has to be implemented. - -TODO: Consider smoothening cave floors after generating them - -Misc. stuff: ------------- -TODO: Make sure server handles removing grass when a block is placed (etc) - - The client should not do it by itself - - NOTE: I think nobody does it currently... -TODO: Block cube placement around player's head -TODO: Protocol version field -TODO: Think about using same bits for material for fences and doors, for - example -TODO: Move mineral to param2, increment map serialization version, add - conversion - -TODO: Restart irrlicht completely when coming back to main menu from game. - - This gets rid of everything that is stored in irrlicht's caches. - -TODO: Merge bahamada's audio stuff (clean patch available) - -TODO: Merge key configuration menu (no clean patch available) - -Making it more portable: ------------------------- - -Stuff to do before release: ---------------------------- - -Fixes to the current release: ------------------------------ - -Stuff to do after release: ---------------------------- - -Doing currently: ----------------- - -====================================================================== - -*/ - -#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 - #pragma comment(lib, "Irrlicht.lib") - //#pragma comment(lib, "jthread.lib") - #pragma comment(lib, "zlibwapi.lib") - #pragma comment(lib, "Shell32.lib") - // This would get rid of the console window - //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") -#endif - -#include -#include -#include -#include "main.h" -#include "common_irrlicht.h" -#include "debug.h" -#include "test.h" -#include "server.h" -#include "constants.h" -#include "porting.h" -#include "gettime.h" -#include "guiMessageMenu.h" -#include "filesys.h" -#include "config.h" -#include "guiMainMenu.h" -#include "mineral.h" -#include "materials.h" -#include "game.h" -#include "keycode.h" -#include "tile.h" - -#include "gettext.h" - -// This makes textures -ITextureSource *g_texturesource = NULL; - -/* - Settings. - These are loaded from the config file. -*/ - -Settings g_settings; -// This is located in defaultsettings.cpp -extern void set_default_settings(); - -// Global profiler -Profiler g_profiler; - -/* - Random stuff -*/ - -/* - GUI Stuff -*/ - -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; - -/* - Debug streams -*/ - -// Connection -std::ostream *dout_con_ptr = &dummyout; -std::ostream *derr_con_ptr = &dstream_no_stderr; -//std::ostream *dout_con_ptr = &dstream_no_stderr; -//std::ostream *derr_con_ptr = &dstream_no_stderr; -//std::ostream *dout_con_ptr = &dstream; -//std::ostream *derr_con_ptr = &dstream; - -// Server -std::ostream *dout_server_ptr = &dstream; -std::ostream *derr_server_ptr = &dstream; - -// Client -std::ostream *dout_client_ptr = &dstream; -std::ostream *derr_client_ptr = &dstream; - -/* - gettime.h implementation -*/ - -// A small helper class -class TimeGetter -{ -public: - virtual u32 getTime() = 0; -}; - -// A precise irrlicht one -class IrrlichtTimeGetter: public TimeGetter -{ -public: - IrrlichtTimeGetter(IrrlichtDevice *device): - m_device(device) - {} - u32 getTime() - { - if(m_device == NULL) - return 0; - return m_device->getTimer()->getRealTime(); - } -private: - IrrlichtDevice *m_device; -}; -// Not so precise one which works without irrlicht -class SimpleTimeGetter: public TimeGetter -{ -public: - u32 getTime() - { - return porting::getTimeMs(); - } -}; - -// A pointer to a global instance of the time getter -// TODO: why? -TimeGetter *g_timegetter = NULL; - -u32 getTimeMs() -{ - if(g_timegetter == NULL) - return 0; - return g_timegetter->getTime(); -} - -/* - 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 - */ - if(noMenuActive() == false) - { - return false; - } - - // Remember whether each key is down or up - if(event.EventType == irr::EET_KEY_INPUT_EVENT) - { - keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; - - if(event.KeyInput.PressedDown) - keyWasDown[event.KeyInput.Key] = true; - } - - if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) - { - if(noMenuActive() == false) - { - left_active = false; - middle_active = false; - right_active = false; - } - else - { - //dstream<<"MyEventReceiver: mouse input"<IsKeyDown(keyCode); - } - virtual bool wasKeyDown(EKEY_CODE keyCode) - { - return m_receiver->WasKeyDown(keyCode); - } - virtual v2s32 getMousePos() - { - return m_device->getCursorControl()->getPosition(); - } - virtual void setMousePos(s32 x, s32 y) - { - m_device->getCursorControl()->setPosition(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; -}; - -class RandomInputHandler : public InputHandler -{ -public: - RandomInputHandler() - { - leftdown = false; - rightdown = false; - leftclicked = false; - rightclicked = false; - leftreleased = false; - rightreleased = false; - for(u32 i=0; i map1; - tempf = -324; - const s16 ii=300; - for(s16 y=0; y=0; y--){ - for(s16 x=0; x screensize = driver->getScreenSize(); - - video::ITexture *bgtexture = - driver->getTexture(getTexturePath("mud.png").c_str()); - if(bgtexture) - { - s32 texturesize = 128; - s32 tiled_y = screensize.Height / texturesize + 1; - s32 tiled_x = screensize.Width / texturesize + 1; - - for(s32 y=0; y rect(0,0,texturesize,texturesize); - rect += v2s32(x*texturesize, y*texturesize); - driver->draw2DImage(bgtexture, rect, - core::rect(core::position2d(0,0), - core::dimension2di(bgtexture->getSize())), - NULL, NULL, true); - } - } - - video::ITexture *logotexture = - driver->getTexture(getTexturePath("menulogo.png").c_str()); - if(logotexture) - { - v2s32 logosize(logotexture->getOriginalSize().Width, - logotexture->getOriginalSize().Height); - logosize *= 4; - - video::SColor bgcolor(255,50,50,50); - core::rect bgrect(0, screensize.Height-logosize.Y-20, - screensize.Width, screensize.Height); - driver->draw2DRectangle(bgcolor, bgrect, NULL); - - core::rect rect(0,0,logosize.X,logosize.Y); - rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y); - rect -= v2s32(logosize.X/2, 0); - driver->draw2DImage(logotexture, rect, - core::rect(core::position2d(0,0), - core::dimension2di(logotexture->getSize())), - NULL, NULL, true); - } -} - -int main(int argc, char *argv[]) -{ - /* - Initialization - */ - - // Set locale. This is for forcing '.' as the decimal point. - std::locale::global(std::locale("C")); - // This enables printing all characters in bitmap font - setlocale(LC_CTYPE, "en_US"); - /* - Parse command line - */ - - // List all allowed options - core::map allowed_options; - allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, - "Run server directly")); - allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, - "Load configuration from specified file")); - allowed_options.insert("port", ValueSpec(VALUETYPE_STRING)); - allowed_options.insert("address", ValueSpec(VALUETYPE_STRING)); - allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG)); - allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING)); -#ifdef _WIN32 - allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG)); -#endif - allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG)); - - Settings cmd_args; - - bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); - - if(ret == false || cmd_args.getFlag("help")) - { - dstream<<"Allowed options:"<::Iterator - i = allowed_options.getIterator(); - i.atEnd() == false; i++) - { - dstream<<" --"<getKey(); - if(i.getNode()->getValue().type == VALUETYPE_FLAG) - { - } - else - { - dstream<<" "; - } - dstream<getValue().help != NULL) - { - dstream<<" "<getValue().help - < filenames; - filenames.push_back(porting::path_userdata + "/minetest.conf"); -#ifdef RUN_IN_PLACE - filenames.push_back(porting::path_userdata + "/../minetest.conf"); -#endif - - for(u32 i=0; i(screenW, screenH), - 16, fullscreen, false, false, &receiver); - - if (device == 0) - return 1; // could not create selected driver. - - // Set device in game parameters - device = device; - - // Set the window caption - device->setWindowCaption(L"Minetest [Main Menu]"); - - // Create time getter - g_timegetter = new IrrlichtTimeGetter(device); - - // Create game callback for menus - g_gamecallback = new MainGameCallback(device); - - // Create texture source - g_texturesource = new TextureSource(device); - - /* - Speed tests (done after irrlicht is loaded to get timer) - */ - if(cmd_args.getFlag("speedtests")) - { - dstream<<"Running speed tests"<setResizable(true); - - bool random_input = g_settings.getBool("random_input") - || cmd_args.getFlag("random-input"); - InputHandler *input = NULL; - if(random_input) - input = new RandomInputHandler(); - else - input = new RealInputHandler(device, &receiver); - - /* - Continue initialization - */ - - //video::IVideoDriver* driver = device->getVideoDriver(); - - /* - This changes the minimum allowed number of vertices in a VBO. - Default is 500. - */ - //driver->setMinHardwareBufferVertexCount(50); - - scene::ISceneManager* smgr = device->getSceneManager(); - - guienv = device->getGUIEnvironment(); - gui::IGUISkin* skin = guienv->getSkin(); - gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); - if(font) - skin->setFont(font); - else - dstream<<"WARNING: Font file was not found." - " Using default font."<getFont(); - assert(font); - - u32 text_height = font->getDimension(L"Hello, world!").Height; - dstream<<"text_height="<setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); - skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); - //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); - //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); - 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)); - - /* - Preload some textures and stuff - */ - - init_mapnode(); // Second call with g_texturesource set - init_mineral(); - - /* - GUI stuff - */ - - /* - 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; - - /* - Menu-game loop - */ - while(device->run() && kill == false) - { - - // 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"", - core::rect(0, 0, 10000, 10000)); - - /* - Out-of-game menu loop. - - Loop quits when menu returns proper parameters. - */ - while(kill == false) - { - // Cursor can be non-visible when coming from the game - device->getCursorControl()->setVisible(true); - // Some stuff are left to scene manager when coming from the game - // (map at least?) - smgr->clear(); - // Reset or hide the debug gui texts - /*guitext->setText(L"Minetest-c55"); - guitext2->setVisible(false); - guitext_info->setVisible(false); - guitext_chat->setVisible(false);*/ - - // Initialize menu data - MainMenuData menudata; - menudata.address = narrow_to_wide(address); - menudata.name = narrow_to_wide(playername); - menudata.port = narrow_to_wide(itos(port)); - menudata.fancy_trees = g_settings.getBool("new_style_leaves"); - menudata.smooth_lighting = g_settings.getBool("smooth_lighting"); - menudata.creative_mode = g_settings.getBool("creative_mode"); - menudata.enable_damage = g_settings.getBool("enable_damage"); - - GUIMainMenu *menu = - new GUIMainMenu(guienv, guiroot, -1, - &g_menumgr, &menudata, g_gamecallback); - menu->allowFocusRemoval(true); - - if(error_message != L"") - { - dstream<<"WARNING: error_message = " - <drop(); - error_message = L""; - } - - video::IVideoDriver* driver = device->getVideoDriver(); - - dstream<<"Created main menu"<run() && kill == false) - { - if(menu->getStatus() == true) - break; - - //driver->beginScene(true, true, video::SColor(255,0,0,0)); - driver->beginScene(true, true, video::SColor(255,128,128,128)); - - drawMenuBackground(driver); - - guienv->drawAll(); - - driver->endScene(); - - // On some computers framerate doesn't seem to be - // automatically limited - sleep_ms(25); - } - - // Break out of menu-game loop to shut down cleanly - if(device->run() == false || kill == true) - break; - - dstream<<"Dropping main menu"<drop(); - - // Delete map if requested - if(menudata.delete_map) - { - bool r = fs::RecursiveDeleteContent(map_dir); - if(r == false) - error_message = L"Delete failed"; - continue; - } - - playername = wide_to_narrow(menudata.name); - - password = translatePassword(playername, menudata.password); - - address = wide_to_narrow(menudata.address); - int newport = stoi(wide_to_narrow(menudata.port)); - if(newport != 0) - port = newport; - g_settings.set("new_style_leaves", itos(menudata.fancy_trees)); - g_settings.set("smooth_lighting", itos(menudata.smooth_lighting)); - g_settings.set("creative_mode", itos(menudata.creative_mode)); - g_settings.set("enable_damage", itos(menudata.enable_damage)); - - // NOTE: These are now checked server side; no need to do it - // here, so let's not do it here. - /*// Check for valid parameters, restart menu if invalid. - if(playername == "") - { - error_message = L"Name required."; - continue; - } - // Check that name has only valid chars - if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false) - { - error_message = L"Characters allowed: " - +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS); - continue; - }*/ - - // Save settings - g_settings.set("name", playername); - g_settings.set("address", address); - g_settings.set("port", itos(port)); - // Update configuration file - if(configpath != "") - g_settings.updateConfigFile(configpath.c_str()); - - // Continue to game - break; - } - - // Break out of menu-game loop to shut down cleanly - if(device->run() == false) - break; - - // Initialize mapnode again to enable changed graphics settings - init_mapnode(); - - /* - Run game - */ - the_game( - kill, - random_input, - input, - device, - font, - map_dir, - playername, - password, - address, - port, - error_message - ); - - } //try - catch(con::PeerNotFoundException &e) - { - dstream<drop(); - - END_DEBUG_EXCEPTION_HANDLER - - debugstreams_deinit(); - - return 0; -} - -//END +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU 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 + +#include "irrlicht.h" // createDevice + +#include "main.h" +#include "mainmenumanager.h" +#include +#include +#include +#include "common_irrlicht.h" +#include "debug.h" +#include "test.h" +#include "server.h" +#include "constants.h" +#include "porting.h" +#include "gettime.h" +#include "guiMessageMenu.h" +#include "filesys.h" +#include "config.h" +#include "guiMainMenu.h" +#include "game.h" +#include "keycode.h" +#include "tile.h" +#include "chat.h" +#include "defaultsettings.h" +#include "gettext.h" +#include "settings.h" +#include "profiler.h" +#include "log.h" +#include "mods.h" +#include "utility_string.h" +#include "subgame.h" +#include "quicktune.h" + +/* + Settings. + These are loaded from the config file. +*/ +Settings main_settings; +Settings *g_settings = &main_settings; + +// Global profiler +Profiler main_profiler; +Profiler *g_profiler = &main_profiler; + +/* + Debug streams +*/ + +// Connection +std::ostream *dout_con_ptr = &dummyout; +std::ostream *derr_con_ptr = &verbosestream; +//std::ostream *dout_con_ptr = &infostream; +//std::ostream *derr_con_ptr = &errorstream; + +// 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::getTimeMs(); +} + +#else + +// A small helper class +class TimeGetter +{ +public: + virtual u32 getTime() = 0; +}; + +// A precise irrlicht one +class IrrlichtTimeGetter: public TimeGetter +{ +public: + IrrlichtTimeGetter(IrrlichtDevice *device): + m_device(device) + {} + u32 getTime() + { + if(m_device == NULL) + return 0; + return m_device->getTimer()->getRealTime(); + } +private: + IrrlichtDevice *m_device; +}; +// Not so precise one which works without irrlicht +class SimpleTimeGetter: public TimeGetter +{ +public: + u32 getTime() + { + return porting::getTimeMs(); + } +}; + +// A pointer to a global instance of the time getter +// TODO: why? +TimeGetter *g_timegetter = NULL; + +u32 getTimeMs() +{ + if(g_timegetter == NULL) + return 0; + return g_timegetter->getTime(); +} + +#endif + +class StderrLogOutput: public ILogOutput +{ +public: + /* line: Full line with timestamp, level and thread */ + void printLog(const std::string &line) + { + std::cerr<IsKeyDown(keyCode); + } + virtual bool wasKeyDown(const KeyPress &keyCode) + { + return m_receiver->WasKeyDown(keyCode); + } + virtual v2s32 getMousePos() + { + return m_device->getCursorControl()->getPosition(); + } + virtual void setMousePos(s32 x, s32 y) + { + m_device->getCursorControl()->setPosition(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; +}; + +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) + { + 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; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 40); + keydown.toggle(getKeySetting("keymap_jump")); + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 40); + keydown.toggle(getKeySetting("keymap_special1")); + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 40); + keydown.toggle(getKeySetting("keymap_forward")); + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 40); + keydown.toggle(getKeySetting("keymap_left")); + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 20); + mousespeed = v2s32(Rand(-20,20), Rand(-15,20)); + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 30); + leftdown = !leftdown; + if(leftdown) + leftclicked = true; + if(!leftdown) + leftreleased = true; + } + } + { + static float counter1 = 0; + counter1 -= dtime; + if(counter1 < 0.0) + { + counter1 = 0.1*Rand(1, 15); + rightdown = !rightdown; + if(rightdown) + rightclicked = true; + 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; +}; + +void drawMenuBackground(video::IVideoDriver* driver) +{ + core::dimension2d screensize = driver->getScreenSize(); + + video::ITexture *bgtexture = + driver->getTexture(getTexturePath("menubg.png").c_str()); + if(bgtexture) + { + s32 scaledsize = 128; + + // The important difference between destsize and screensize is + // that destsize is rounded to whole scaled pixels. + // These formulas use component-wise multiplication and division of v2u32. + v2u32 texturesize = bgtexture->getSize(); + v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1); + v2u32 destsize = scaledsize * sourcesize / texturesize; + + // Default texture wrapping mode in Irrlicht is ETC_REPEAT. + driver->draw2DImage(bgtexture, + core::rect(0, 0, destsize.X, destsize.Y), + core::rect(0, 0, sourcesize.X, sourcesize.Y), + NULL, NULL, true); + } + + video::ITexture *logotexture = + driver->getTexture(getTexturePath("menulogo.png").c_str()); + if(logotexture) + { + v2s32 logosize(logotexture->getOriginalSize().Width, + logotexture->getOriginalSize().Height); + logosize *= 4; + + video::SColor bgcolor(255,50,50,50); + core::rect bgrect(0, screensize.Height-logosize.Y-20, + screensize.Width, screensize.Height); + driver->draw2DRectangle(bgcolor, bgrect, NULL); + + core::rect rect(0,0,logosize.X,logosize.Y); + rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y); + rect -= v2s32(logosize.X/2, 0); + driver->draw2DImage(logotexture, rect, + core::rect(core::position2d(0,0), + core::dimension2di(logotexture->getSize())), + NULL, NULL, true); + } +} + +#endif + +// 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() +{ + { + infostream<<"The following test should take around 20ms."< map1; + tempf = -324; + const s16 ii=300; + for(s16 y=0; y=0; y--){ + for(s16 x=0; x allowed_options; + allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG, + "Show allowed options")); + allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, + "Load configuration from specified file")); + allowed_options.insert("port", ValueSpec(VALUETYPE_STRING, + "Set network port (UDP)")); + allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG, + "Disable unit tests")); + allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG, + "Enable unit tests")); + allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING, + "Same as --world (deprecated)")); + allowed_options.insert("world", ValueSpec(VALUETYPE_STRING, + "Set world path (implies local game)")); + allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG, + "Print more information to console")); + allowed_options.insert("logfile", ValueSpec(VALUETYPE_STRING, + "Set logfile path ('' = no logging)")); + allowed_options.insert("gameid", ValueSpec(VALUETYPE_STRING, + "Set gameid (\"--gameid list\" prints available ones)")); +#ifndef SERVER + allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG, + "Run speed tests")); + allowed_options.insert("address", ValueSpec(VALUETYPE_STRING, + "Address to connect to. ('' = local game)")); + allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG, + "Enable random user input, for testing")); + allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, + "Run dedicated server")); + allowed_options.insert("name", ValueSpec(VALUETYPE_STRING, + "Set player name")); + allowed_options.insert("password", ValueSpec(VALUETYPE_STRING, + "Set password")); + allowed_options.insert("go", ValueSpec(VALUETYPE_FLAG, + "Disable main menu")); +#endif + + Settings cmd_args; + + bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); + + if(ret == false || cmd_args.getFlag("help")) + { + dstream<<"Allowed options:"<::Iterator + i = allowed_options.getIterator(); + i.atEnd() == false; i++) + { + std::ostringstream os1(std::ios::binary); + os1<<" --"<getKey(); + if(i.getNode()->getValue().type == VALUETYPE_FLAG) + {} + else + os1<<" "; + dstream<getValue().help != NULL) + dstream<getValue().help; + dstream< gameids = getAvailableGameIds(); + for(std::set::const_iterator i = gameids.begin(); + i != gameids.end(); i++) + dstream<<(*i)<readConfigFile(cmd_args.get("config").c_str()); + if(r == false) + { + errorstream<<"Could not read configuration from \"" + < filenames; + filenames.push_back(porting::path_user + + DIR_DELIM + "minetest.conf"); + // Legacy configuration file location + filenames.push_back(porting::path_user + + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); +#ifdef RUN_IN_PLACE + // Try also from a lower level (to aid having the same configuration + // for many RUN_IN_PLACE installs) + filenames.push_back(porting::path_user + + DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); +#endif + + for(u32 i=0; ireadConfigFile(filenames[i].c_str()); + if(r) + { + configpath = filenames[i]; + break; + } + } + + // If no path found, use the first one (menu creates the file) + if(configpath == "") + configpath = filenames[0]; + } + + // Initialize random seed + srand(time(0)); + mysrand(time(0)); + + /* + Run unit tests + */ + + if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false) + || cmd_args.getFlag("enable-unittests") == true) + { + run_tests(); + } + + /* + Game parameters + */ + + // Port + u16 port = 30000; + if(cmd_args.exists("port")) + port = cmd_args.getU16("port"); + else if(g_settings->exists("port")) + port = g_settings->getU16("port"); + if(port == 0) + port = 30000; + + // World directory + std::string commanded_world = ""; + if(cmd_args.exists("world")) + commanded_world = cmd_args.get("world"); + else if(cmd_args.exists("map-dir")) + commanded_world = cmd_args.get("map-dir"); + else if(g_settings->exists("map-dir")) + commanded_world = g_settings->get("map-dir"); + + // Gamespec + SubgameSpec commanded_gamespec; + if(cmd_args.exists("gameid")){ + std::string gameid = cmd_args.get("gameid"); + commanded_gamespec = findSubgame(gameid); + if(!commanded_gamespec.isValid()){ + errorstream<<"Game \""<get("address"); + if(cmd_args.exists("address")) + address = cmd_args.get("address"); + else if(cmd_args.exists("world")) + address = ""; + + std::string playername = g_settings->get("name"); + if(cmd_args.exists("name")) + playername = cmd_args.get("name"); + + bool skip_main_menu = cmd_args.getFlag("go"); + + /* + Device initialization + */ + + // Resolution selection + + bool fullscreen = false; + u16 screenW = g_settings->getU16("screenW"); + u16 screenH = g_settings->getU16("screenH"); + + // Determine driver + + video::E_DRIVER_TYPE driverType; + + std::string driverstring = g_settings->get("video_driver"); + + if(driverstring == "null") + driverType = video::EDT_NULL; + else if(driverstring == "software") + driverType = video::EDT_SOFTWARE; + else if(driverstring == "burningsvideo") + driverType = video::EDT_BURNINGSVIDEO; + else if(driverstring == "direct3d8") + driverType = video::EDT_DIRECT3D8; + else if(driverstring == "direct3d9") + driverType = video::EDT_DIRECT3D9; + else if(driverstring == "opengl") + driverType = video::EDT_OPENGL; + else + { + errorstream<<"WARNING: Invalid video_driver specified; defaulting " + "to opengl"<(screenW, screenH), + 16, fullscreen, false, false, &receiver); + + if (device == 0) + return 1; // could not create selected driver. + + /* + Continue initialization + */ + + video::IVideoDriver* driver = device->getVideoDriver(); + + // Disable mipmaps (because some of them look ugly) + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); + + /* + 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) + */ + if(cmd_args.getFlag("speedtests")) + { + dstream<<"Running speed tests"<setResizable(true); + + bool random_input = g_settings->getBool("random_input") + || cmd_args.getFlag("random-input"); + InputHandler *input = NULL; + 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(); + gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); + if(font) + skin->setFont(font); + else + errorstream<<"WARNING: Font file was not found." + " Using default font."<getFont(); + assert(font); + + u32 text_height = font->getDimension(L"Hello, world!").Height; + infostream<<"text_height="<setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); + skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); + //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); + //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); + 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)); + + /* + 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) + { + // Set the window caption + device->setWindowCaption(L"Minetest [Main Menu]"); + + // 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"", + core::rect(0, 0, 10000, 10000)); + + SubgameSpec gamespec; + WorldSpec worldspec; + + /* + Out-of-game menu loop. + + Loop quits when menu returns proper parameters. + */ + while(kill == false) + { + // If skip_main_menu, only go through here once + if(skip_main_menu && !first_loop){ + kill = true; + break; + } + first_loop = false; + + // Cursor can be non-visible when coming from the game + device->getCursorControl()->setVisible(true); + // Some stuff are left to scene manager when coming from the game + // (map at least?) + smgr->clear(); + + // Initialize menu data + MainMenuData menudata; + menudata.address = narrow_to_wide(address); + menudata.name = narrow_to_wide(playername); + menudata.port = narrow_to_wide(itos(port)); + if(cmd_args.exists("password")) + menudata.password = narrow_to_wide(cmd_args.get("password")); + menudata.fancy_trees = g_settings->getBool("new_style_leaves"); + menudata.smooth_lighting = g_settings->getBool("smooth_lighting"); + menudata.clouds_3d = g_settings->getBool("enable_3d_clouds"); + menudata.opaque_water = g_settings->getBool("opaque_water"); + menudata.creative_mode = g_settings->getBool("creative_mode"); + menudata.enable_damage = g_settings->getBool("enable_damage"); + // Get world listing for the menu + std::vector worldspecs = getAvailableWorlds(); + for(std::vector::const_iterator i = worldspecs.begin(); + i != worldspecs.end(); i++) + menudata.worlds.push_back(narrow_to_wide( + i->name + " [" + i->gameid + "]")); + // Default to selecting nothing + menudata.selected_world = -1; + // If there is only one world, select it + if(worldspecs.size() == 1){ + menudata.selected_world = 0; + } + // Otherwise try to select according to selected_world_path + else if(g_settings->exists("selected_world_path")){ + std::string trypath = g_settings->get("selected_world_path"); + for(u32 i=0; iget("default_game"); + WorldSpec spec(commanded_world, "[commanded world]", gameid); + worldspecs.push_back(spec); + menudata.worlds.push_back(narrow_to_wide(spec.name) + +L" ["+narrow_to_wide(spec.gameid)+L"]"); + menudata.selected_world = menudata.worlds.size()-1; + } + + if(skip_main_menu == false) + { + GUIMainMenu *menu = + new GUIMainMenu(guienv, guiroot, -1, + &g_menumgr, &menudata, g_gamecallback); + menu->allowFocusRemoval(true); + + if(error_message != L"") + { + verbosestream<<"error_message = " + <drop(); + error_message = L""; + } + + video::IVideoDriver* driver = device->getVideoDriver(); + + infostream<<"Created main menu"<run() && kill == false) + { + if(menu->getStatus() == true) + break; + + //driver->beginScene(true, true, video::SColor(255,0,0,0)); + driver->beginScene(true, true, video::SColor(255,128,128,128)); + + drawMenuBackground(driver); + + guienv->drawAll(); + + driver->endScene(); + + // On some computers framerate doesn't seem to be + // automatically limited + sleep_ms(25); + } + + // Break out of menu-game loop to shut down cleanly + if(device->run() == false || kill == true) + break; + + infostream<<"Dropping main menu"<drop(); + } + + // Set world path to selected one + if(menudata.selected_world != -1){ + worldspec = worldspecs[menudata.selected_world]; + infostream<<"Selected world: "<set("new_style_leaves", itos(menudata.fancy_trees)); + g_settings->set("smooth_lighting", itos(menudata.smooth_lighting)); + g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d)); + g_settings->set("opaque_water", itos(menudata.opaque_water)); + g_settings->set("creative_mode", itos(menudata.creative_mode)); + g_settings->set("enable_damage", itos(menudata.enable_damage)); + g_settings->set("name", playername); + g_settings->set("address", address); + g_settings->set("port", itos(port)); + if(menudata.selected_world != -1) + g_settings->set("selected_world_path", + worldspecs[menudata.selected_world].path); + // Update configuration file + if(configpath != "") + g_settings->updateConfigFile(configpath.c_str()); + + // If local game + if(address == "") + { + if(menudata.selected_world == -1){ + error_message = L"No world selected and no address " + L"provided. Nothing to do."; + errorstream<run() == false || kill == true) + break; + + /* + Run game + */ + the_game( + kill, + random_input, + input, + device, + font, + worldspec.path, + playername, + password, + address, + port, + error_message, + configpath, + chat_backend, + gamespec + ); + + } //try + catch(con::PeerNotFoundException &e) + { + error_message = L"Connection error (timed out?)"; + errorstream<updateConfigFile(configpath.c_str()); + + // Print modified quicktune values + { + bool header_printed = false; + std::vector names = getQuicktuneNames(); + for(u32 i=0; i