X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fmain.cpp;h=73be969adff29aafdbd408b9a521a7003a0b56eb;hb=6823ce99a7deabe410dd8b143b688cd364490cec;hp=3cc7489541c1f56bc7a5681eafb4c0cc823df505;hpb=cdadbdbd17d624dd45cca67f41309cbe776dc348;p=oweals%2Fminetest.git diff --git a/src/main.cpp b/src/main.cpp index 3cc748954..73be969ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,397 +1,28 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 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 +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 General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +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. */ -/* -=============================== 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. - -Vim conversion regexpes for moving to extended content type storage: -%s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g -%s/content_features(\([^.]*\)\.d)/content_features(\1)/g -%s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g -%s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g -%s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g -%s/\.d;/.getContent();/g -%s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g -Other things to note: -- node.d = node.param0 (only in raw serialization; use getContent() otherwise) -- node.param = node.param1 -- node.dir = node.param2 -- content_walkable(node.d) etc should be changed to - content_features(node).walkable etc -- Also check for lines that store the result of getContent to a 8-bit - variable and fix them (result of getContent() must be stored in - content_t, which is 16-bit) - -NOTE: Seeds in 1260:6c77e7dbfd29: -5721858502589302589: - Spawns you on a small sand island with a surface dungeon -2983455799928051958: - Enormous jungle + a surface dungeon at ~(250,0,0) - -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. Sounds too. - - 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 - -TODO: Fix make_tree, make_* to use seed-position-consistent pseudorandom - - delta also - -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 - -SUGG: Restart irrlicht completely when coming back to main menu from game. - - This gets rid of everything that is stored in irrlicht's caches. - - This might be needed for texture pack selection in menu - -TODO: Merge bahamada's audio stuff (clean patch available) - -TODO: Move content_features to mapnode_content_features.{h,cpp} or so - -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 + /*#ifdef _WIN32 #pragma message ("Disabling unit tests") #else #warning "Disabling unit tests" - #endif + #endif*/ // Disable unit tests #define ENABLE_TESTS 0 #else @@ -400,21 +31,26 @@ Doing currently: #endif #ifdef _MSC_VER +#ifndef SERVER // Dedicated server isn't linked with Irrlicht #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 + #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 "main.h" -#include "common_irrlicht.h" +#include "irrlichttypes_extrabloated.h" #include "debug.h" #include "test.h" +#include "clouds.h" #include "server.h" #include "constants.h" #include "porting.h" @@ -423,40 +59,60 @@ Doing currently: #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 "chat.h" +#include "defaultsettings.h" #include "gettext.h" - -// This makes textures -ITextureSource *g_texturesource = NULL; +#include "settings.h" +#include "profiler.h" +#include "log.h" +#include "mods.h" +#if USE_FREETYPE +#include "xCGUITTFont.h" +#endif +#include "util/string.h" +#include "subgame.h" +#include "quicktune.h" +#include "serverlist.h" /* Settings. These are loaded from the config file. */ - -Settings g_settings; -// This is located in defaultsettings.cpp -extern void set_default_settings(); +Settings main_settings; +Settings *g_settings = &main_settings; // Global profiler -Profiler g_profiler; +Profiler main_profiler; +Profiler *g_profiler = &main_profiler; /* - Random stuff + 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 /* - GUI Stuff + Random stuff */ +/* mainmenumanager.h */ + gui::IGUIEnvironment* guienv = NULL; gui::IGUIStaticText *guiroot = NULL; - MainMenuManager g_menumgr; bool noMenuActive() @@ -465,32 +121,22 @@ bool noMenuActive() } // Passed to menus to allow disconnecting and exiting - MainGameCallback *g_gamecallback = NULL; +#endif /* - Debug streams + gettime.h implementation */ -// 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; +#ifdef SERVER -// Client -std::ostream *dout_client_ptr = &dstream; -std::ostream *derr_client_ptr = &dstream; +u32 getTimeMs() +{ + /* Use imprecise system calls directly (from porting.h) */ + return porting::getTimeMs(); +} -/* - gettime.h implementation -*/ +#else // A small helper class class TimeGetter @@ -536,6 +182,30 @@ u32 getTimeMs() 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(EKEY_CODE keyCode) + virtual bool wasKeyDown(const KeyPress &keyCode) { return m_receiver->WasKeyDown(keyCode); } @@ -769,14 +438,13 @@ public: rightclicked = false; leftreleased = false; rightreleased = false; - for(u32 i=0; i screensize = driver->getScreenSize(); + + std::string path = getTexturePath("menubg.png"); + if (path[0]) { + video::ITexture *bgtexture = + driver->getTexture(path.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); + } + } +} + +//Draw the footer at the bottom of the window +void drawMenuFooter(video::IVideoDriver* driver, bool clouds) { + core::dimension2d screensize = driver->getScreenSize(); + std::string path = getTexturePath(clouds ? + "menufooter_clouds.png" : "menufooter.png"); + if (path[0]) { + video::ITexture *footertexture = + driver->getTexture(path.c_str()); + + if (footertexture) { + f32 mult = (((f32)screensize.Width)) / + ((f32)footertexture->getOriginalSize().Width); + + v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult, + ((f32)footertexture->getOriginalSize().Height) * mult); + + // Don't draw the footer if there isn't enough room + s32 free_space = (((s32)screensize.Height)-320)/2; + if (free_space > footersize.Y) { + core::rect rect(0,0,footersize.X,footersize.Y); + rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y); + rect -= v2s32(footersize.X/2, 0); + + driver->draw2DImage(footertexture, rect, + core::rect(core::position2d(0,0), + core::dimension2di(footertexture->getSize())), + NULL, NULL, true); + } + } + } +} + +// Draw the Header over the main menu +void drawMenuHeader(video::IVideoDriver* driver) { + core::dimension2d screensize = driver->getScreenSize(); + + std::string path = getTexturePath("menuheader.png"); + if (path[0]) { + video::ITexture *splashtexture = + driver->getTexture(path.c_str()); + + if(splashtexture) { + //v2s32 splashsize((splashtexture->getOriginalSize().Width*100)/ + // splashtexture->getOriginalSize().Height, 80); + + f32 mult = (((f32)screensize.Width / 2)) / + ((f32)splashtexture->getOriginalSize().Width); + + v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult, + ((f32)splashtexture->getOriginalSize().Height) * mult); + + // Don't draw the header is there isn't enough room + s32 free_space = (((s32)screensize.Height)-320)/2; + if (free_space > splashsize.Y) { + core::rect splashrect(0, 0, splashsize.X, splashsize.Y); + splashrect += v2s32((screensize.Width/2)-(splashsize.X/2), + ((free_space/2)-splashsize.Y/2)+10); + + video::SColor bgcolor(255,50,50,50); + + driver->draw2DImage(splashtexture, splashrect, + core::rect(core::position2d(0,0), + core::dimension2di(splashtexture->getSize())), + NULL, NULL, true); + } + } + } +} + +// Draw the Splash over the clouds and under the main menu +void drawMenuSplash(video::IVideoDriver* driver) { + core::dimension2d screensize = driver->getScreenSize(); + if (getTexturePath("menusplash.png") != "") { + video::ITexture *splashtexture = + driver->getTexture(getTexturePath("menusplash.png").c_str()); + + if(splashtexture) { + core::rect splashrect(0, 0, screensize.Width, screensize.Height); + + video::SColor bgcolor(255,50,50,50); + + driver->draw2DImage(splashtexture, splashrect, + core::rect(core::position2d(0,0), + core::dimension2di(splashtexture->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; @@ -945,7 +728,7 @@ std::string tempstring2; void SpeedTests() { { - dstream<<"The following test should take around 20ms."< map1; + std::map map1; tempf = -324; const s16 ii=300; for(s16 y=0; y &worldspecs, + std::ostream &os) { - core::dimension2d 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); + for(u32 i=0; i 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)); + std::map allowed_options; + allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG, + _("Show allowed options")))); + 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("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)")))); +#ifndef SERVER + 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 - 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")) + if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1")) { - dstream<<"Allowed options:"<::Iterator - i = allowed_options.getIterator(); - i.atEnd() == false; i++) + dstream<<_("Allowed options:")<::iterator + i = allowed_options.begin(); + i != allowed_options.end(); ++i) { - dstream<<" --"<getKey(); - if(i.getNode()->getValue().type == VALUETYPE_FLAG) - { - } + std::ostringstream os1(std::ios::binary); + os1<<" --"<first; + if(i->second.type == VALUETYPE_FLAG) + {} else - { - dstream<<" "; - } - dstream<"); + dstream<getValue().help != NULL) - { - dstream<<" "<getValue().help - <second.help != NULL) + dstream<second.help; + dstream< gameids = getAvailableGameIds(); + for(std::set::const_iterator i = gameids.begin(); + i != gameids.end(); i++) + dstream<<(*i)< worldspecs = getAvailableWorlds(); + print_worldspecs(worldspecs, dstream); + return 0; + } + // Print startup message - dstream<readConfigFile(cmd_args.get("config").c_str()); if(r == false) { - dstream<<"Could not read configuration from \"" + errorstream<<"Could not read configuration from \"" < filenames; - filenames.push_back(porting::path_userdata + "/minetest.conf"); -#ifdef RUN_IN_PLACE - filenames.push_back(porting::path_userdata + "/../minetest.conf"); + std::vector 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"); +#if 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]; @@ -1242,20 +1051,36 @@ int main(int argc, char *argv[]) if(configpath == "") configpath = filenames[0]; } + + // 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 + if(cmd_args.exists("logfile")) + logfile = cmd_args.get("logfile"); + + 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) + log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1)); + + if(logfile != "") + debugstreams_init(false, logfile.c_str()); + else + debugstreams_init(false, NULL); + + infostream<<"logfile = "<exists("port")) + port = g_settings->getU16("port"); if(port == 0) port = 30000; - // Map directory - std::string map_dir = porting::path_userdata+"/world"; - if(cmd_args.exists("map-dir")) - map_dir = cmd_args.get("map-dir"); - else if(g_settings.exists("map-dir")) - map_dir = g_settings.get("map-dir"); + // 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(cmd_args.exists("nonopt0")) // First nameless argument + commanded_world = cmd_args.get("nonopt0"); + else if(g_settings->exists("map-dir")) + commanded_world = g_settings->get("map-dir"); + + // World name + std::string commanded_worldname = ""; + if(cmd_args.exists("worldname")) + commanded_worldname = cmd_args.get("worldname"); - // Run dedicated server if asked to - if(cmd_args.getFlag("server")) + // Strip world.mt from commanded_world { - DSTACK("Dedicated server branch"); + std::string worldmt = "world.mt"; + if(commanded_world.size() > worldmt.size() && + commanded_world.substr(commanded_world.size()-worldmt.size()) + == worldmt){ + dstream<<_("Supplied world.mt file - stripping it off.")< worldspecs = getAvailableWorlds(); + bool found = false; + for(u32 i=0; iset("server_dedicated", run_dedicated_server ? "true" : "false"); + 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; + verbosestream<<_("Determining world path")< worldspecs = getAvailableWorlds(); + // If a world name was specified, select it + if(commanded_worldname != ""){ + world_path = ""; + for(u32 i=0; i 1){ + dstream<<_("Multiple worlds are available.")<" + " or --world ")<get("default_game")); + infostream<<"Using default gameid ["<get("address"); + if(commanded_world != "") + address = ""; + else if(cmd_args.exists("address")) address = cmd_args.get("address"); - } - else - { - address = g_settings.get("address"); - } - std::string playername = g_settings.get("name"); + 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 @@ -1336,15 +1322,21 @@ int main(int argc, char *argv[]) // Resolution selection - bool fullscreen = false; - u16 screenW = g_settings.getU16("screenW"); - u16 screenH = g_settings.getU16("screenH"); + bool fullscreen = g_settings->getBool("fullscreen"); + 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; - std::string driverstring = g_settings.get("video_driver"); + std::string driverstring = g_settings->get("video_driver"); if(driverstring == "null") driverType = video::EDT_NULL; @@ -1360,7 +1352,7 @@ int main(int argc, char *argv[]) driverType = video::EDT_OPENGL; else { - dstream<<"WARNING: Invalid video_driver specified; defaulting " + errorstream<<"WARNING: Invalid video_driver specified; defaulting " "to opengl"<(screenW, screenH), - 16, fullscreen, false, false, &receiver); + + SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); + params.DriverType = driverType; + params.WindowSize = core::dimension2d(screenW, screenH); + params.Bits = bits; + params.AntiAlias = fsaa; + params.Fullscreen = fullscreen; + params.Stencilbuffer = false; + params.Vsync = vsync; + params.EventReceiver = &receiver; + + device = createDeviceEx(params); if (device == 0) return 1; // could not create selected driver. - // Set device in game parameters - device = device; + /* + Continue initialization + */ + + video::IVideoDriver* driver = device->getVideoDriver(); + + /* + This changes the minimum allowed number of vertices in a VBO. + Default is 500. + */ + //driver->setMinHardwareBufferVertexCount(50); - // 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) */ @@ -1406,7 +1410,7 @@ int main(int argc, char *argv[]) device->setResizable(true); - bool random_input = g_settings.getBool("random_input") + bool random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); InputHandler *input = NULL; if(random_input) @@ -1414,34 +1418,28 @@ int main(int argc, char *argv[]) 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(); + #if USE_FREETYPE + std::string font_path = g_settings->get("font_path"); + u16 font_size = g_settings->getU16("font_size"); + gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size); + #else gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); + #endif if(font) skin->setFont(font); else - dstream<<"WARNING: Font file was not found." + errorstream<<"WARNING: Font file was not found." " Using default font."<getFont(); assert(font); u32 text_height = font->getDimension(L"Hello, world!").Height; - dstream<<"text_height="<addStaticText(L"", core::rect(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. @@ -1501,131 +1516,316 @@ int main(int argc, char *argv[]) */ 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(); - // 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; + if(g_settings->exists("selected_mainmenu_tab")) + menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab"); 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""; + 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.mip_map = g_settings->getBool("mip_map"); + menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter"); + menudata.bilinear_filter = g_settings->getBool("bilinear_filter"); + menudata.trilinear_filter = g_settings->getBool("trilinear_filter"); + menudata.enable_shaders = g_settings->getS32("enable_shaders"); + menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals"); + menudata.enable_particles = g_settings->getBool("enable_particles"); + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map); + menudata.creative_mode = g_settings->getBool("creative_mode"); + menudata.enable_damage = g_settings->getBool("enable_damage"); + menudata.enable_public = g_settings->getBool("server_announce"); + // Default to selecting nothing + menudata.selected_world = -1; + // Get world listing for the menu + std::vector worldspecs = getAvailableWorlds(); + // 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"); + name += " [new]"; + } + WorldSpec spec(commanded_world, name, gameid); + worldspecs.push_back(spec); + menudata.selected_world = worldspecs.size()-1; + } + // Copy worldspecs to menu + menudata.worlds = worldspecs; - video::IVideoDriver* driver = device->getVideoDriver(); - - dstream<<"Created main menu"<run() && kill == false) + if(skip_main_menu == 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(); + video::IVideoDriver* driver = device->getVideoDriver(); - driver->endScene(); + infostream<<"Waiting for other menus"<run() && kill == false) + { + if(noMenuActive()) + break; + 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); + } + infostream<<"Waited for other menus"<allowFocusRemoval(true); + + // Clouds for the main menu + bool cloud_menu_background = false; + Clouds *clouds = NULL; + if (g_settings->getBool("menu_clouds")) { + cloud_menu_background = true; + clouds = new Clouds(smgr->getRootSceneNode(), + smgr, -1, rand(), 100); + clouds->update(v2f(0, 0), video::SColor(255,200,200,255)); + + // A camera to see the clouds + scene::ICameraSceneNode* camera; + camera = smgr->addCameraSceneNode(0, + v3f(0,0,0), v3f(0, 60, 100)); + camera->setFarValue(10000); + } + + if(error_message != L"") + { + verbosestream<<"error_message = " + <drop(); + error_message = L""; + } + + // Time is in milliseconds, for clouds + u32 lasttime = device->getTimer()->getTime(); + + infostream<<"Created main menu"<run() && kill == false) + { + if(menu->getStatus() == true) + break; + + // Time calc for the clouds + f32 dtime; // in seconds + if (cloud_menu_background) { + u32 time = device->getTimer()->getTime(); + if(time > lasttime) + dtime = (time - lasttime) / 1000.0; + else + dtime = 0; + lasttime = time; + } + + //driver->beginScene(true, true, video::SColor(255,0,0,0)); + driver->beginScene(true, true, video::SColor(255,140,186,250)); + + if (cloud_menu_background) { + // *3 otherwise the clouds would move very slowly + clouds->step(dtime*3); + clouds->render(); + smgr->drawAll(); + drawMenuSplash(driver); + drawMenuFooter(driver, true); + drawMenuHeader(driver); + } else { + drawMenuBackground(driver); + drawMenuFooter(driver, false); + } + + guienv->drawAll(); + + driver->endScene(); + + // On some computers framerate doesn't seem to be + // automatically limited + if (!cloud_menu_background) + sleep_ms(25); + } - // 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; + menu->drop(); + if (cloud_menu_background) { + clouds->drop(); + smgr->clear(); + } } playername = wide_to_narrow(menudata.name); - password = translatePassword(playername, menudata.password); + //infostream<<"Main: password hash: '"<setS32("selected_mainmenu_tab", menudata.selected_tab); + g_settings->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("mip_map", itos(menudata.mip_map)); + g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter)); + g_settings->set("bilinear_filter", itos(menudata.bilinear_filter)); + g_settings->set("trilinear_filter", itos(menudata.trilinear_filter)); + + g_settings->setS32("enable_shaders", menudata.enable_shaders); + g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals)); + g_settings->set("enable_particles", itos(menudata.enable_particles)); + + g_settings->set("creative_mode", itos(menudata.creative_mode)); + g_settings->set("enable_damage", itos(menudata.enable_damage)); + g_settings->set("server_announce", itos(menudata.enable_public)); + 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); + + // Break out of menu-game loop to shut down cleanly + if(device->run() == false || kill == true) + break; - // 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 == "") + current_playername = playername; + current_password = password; + current_address = address; + current_port = port; + + // If using simple singleplayer mode, override + if(simple_singleplayer_mode){ + current_playername = "singleplayer"; + current_password = ""; + current_address = ""; + current_port = 30011; + } + else if (address != "") { - error_message = L"Name required."; + ServerListSpec server; + server["name"] = menudata.servername; + server["address"] = wide_to_narrow(menudata.address); + server["port"] = wide_to_narrow(menudata.port); + server["description"] = menudata.serverdescription; + ServerList::insert(server); + } + + // Set world path to selected one + if(menudata.selected_world != -1){ + worldspec = worldspecs[menudata.selected_world]; + infostream<<"Selected world: "<set("selected_world_path", path); continue; - }*/ + } + + // If local game + if(current_address == "") + { + if(menudata.selected_world == -1){ + error_message = wgettext("No world selected and no address " + "provided. Nothing to do."); + errorstream<run() == false) + if(device->run() == false || kill == true) break; - - // Initialize mapnode again to enable changed graphics settings - init_mapnode(); /* Run game @@ -1636,37 +1836,55 @@ int main(int argc, char *argv[]) input, device, font, - map_dir, - playername, - password, - address, - port, + worldspec.path, + current_playername, + current_password, + current_address, + current_port, error_message, - configpath + configpath, + chat_backend, + gamespec, + simple_singleplayer_mode ); } //try catch(con::PeerNotFoundException &e) { - dstream<updateConfigFile(configpath.c_str()); - END_DEBUG_EXCEPTION_HANDLER + // Print modified quicktune values + { + bool header_printed = false; + std::vector names = getQuicktuneNames(); + for(u32 i=0; i