3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
\r
5 This program is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU General Public License as published by
\r
7 the Free Software Foundation; either version 2 of the License, or
\r
8 (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 =============================== NOTES ==============================
\r
22 NOTE: Things starting with TODO are sometimes only suggestions.
\r
24 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
25 NOTE: Global locale is now set at initialization
\r
27 NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
\r
28 hardware buffer (it is not freed automatically)
\r
30 Random suggeestions (AKA very old suggestions that haven't been done):
\r
31 ----------------------------------------------------------------------
\r
33 SUGG: Fix address to be ipv6 compatible
\r
35 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
37 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
38 - This enables saving many packets and making a faster connection
\r
39 - This also enables server to check if client has received the
\r
40 most recent block sent, for example.
\r
41 SUGG: Add a sane bandwidth throttling system to Connection
\r
43 SUGG: More fine-grained control of client's dumping of blocks from
\r
45 - ...What does this mean in the first place?
\r
47 SUGG: A map editing mode (similar to dedicated server mode)
\r
49 SUGG: Transfer more blocks in a single packet
\r
50 SUGG: A blockdata combiner class, to which blocks are added and at
\r
51 destruction it sends all the stuff in as few packets as possible.
\r
52 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
53 it by sending more stuff in a single packet.
\r
54 - Add a packet queue to RemoteClient, from which packets will be
\r
55 combined with object data packets
\r
56 - This is not exactly trivial: the object data packets are
\r
57 sometimes very big by themselves
\r
58 - This might not give much network performance gain though.
\r
60 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
61 - This is not doable because it is currently hand-made and not
\r
62 based on some mathematical function.
\r
63 - Note: This has been changing lately
\r
65 SUGG: A version number to blocks, which increments when the block is
\r
66 modified (node add/remove, water update, lighting update)
\r
67 - This can then be used to make sure the most recent version of
\r
68 a block has been sent to client, for example
\r
70 SUGG: Make the amount of blocks sending to client and the total
\r
71 amount of blocks dynamically limited. Transferring blocks is the
\r
72 main network eater of this system, so it is the one that has
\r
73 to be throttled so that RTTs stay low.
\r
75 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
76 different directions and then only those drawn that need to be
\r
78 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
81 SUGG: Background music based on cellular automata?
\r
82 http://www.earslap.com/projectslab/otomata
\r
88 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
90 - The player could go faster by a crafting a boat, or riding an animal
\r
92 - Random NPC traders. what else?
\r
96 - When furnace is destroyed, move items to player's inventory
\r
99 - Growing grass, decaying leaves
\r
100 - This can be done in the active blocks I guess.
\r
101 - Lots of stuff can be done in the active blocks.
\r
102 - Uh, is there an active block list somewhere? I think not. Add it.
\r
103 - Breaking weak structures
\r
104 - This can probably be accomplished in the same way as grass
\r
105 - Player health points
\r
106 - When player dies, throw items on map (needs better item-on-map
\r
108 - Cobble to get mossy if near water
\r
109 - More slots in furnace source list, so that multiple ingredients
\r
113 - The Treasure Guard; a big monster with a hammer
\r
114 - The hammer does great damage, shakes the ground and removes a block
\r
115 - You can drop on top of it, and have some time to attack there
\r
116 before he shakes you off
\r
118 - Maybe the difficulty could come from monsters getting tougher in
\r
119 far-away places, and the player starting to need something from
\r
120 there when time goes by.
\r
121 - The player would have some of that stuff at the beginning, and
\r
122 would need new supplies of it when it runs out
\r
125 - A spread-items-on-map routine for the bomb, and for dying players
\r
130 Build system / running:
\r
131 -----------------------
\r
133 Networking and serialization:
\r
134 -----------------------------
\r
136 TODO: Get rid of GotSplitPacketException
\r
141 TODO: Configuration menu, at least for keys
\r
146 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
148 - That is >500 vertices
\r
149 - This is not easy; all the MapBlocks close to the player would
\r
150 still need to be drawn separately and combining the blocks
\r
151 would have to happen in a background thread
\r
153 SUGG: Make fetching sector's blocks more efficient when rendering
\r
154 sectors that have very large amounts of blocks (on client)
\r
155 - Is this necessary at all?
\r
157 TODO: Flowing water animation
\r
159 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
160 animating them is easier.
\r
162 SUGG: Option for enabling proper alpha channel for textures
\r
163 TODO: A setting for enabling bilinear filtering for textures
\r
165 TODO: Better control of draw_control.wanted_max_blocks
\r
173 TODO: Untie client network operations from framerate
\r
174 - Needs some input queues or something
\r
175 - This won't give much performance boost because calculating block
\r
176 meshes takes so long
\r
178 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
180 TODO: Don't update all meshes always on single node changes, but
\r
181 check which ones should be updated
\r
182 - implement Map::updateNodeMeshes() and the usage of it
\r
183 - It will give almost always a 4x boost in mesh update performance.
\r
187 - Tool/weapon visualization
\r
192 SUGG: Make an option to the server to disable building and digging near
\r
193 the starting position
\r
195 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
197 * Fix the problem with the server constantly saving one or a few
\r
198 blocks? List the first saved block, maybe it explains.
\r
199 - It is probably caused by oscillating water
\r
200 * Make a small history check to transformLiquids to detect and log
\r
201 continuous oscillations, in such detail that they can be fixed.
\r
206 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
208 SUGG: MovingObject::move and Player::move are basically the same.
\r
210 - NOTE: Player::move is more up-to-date.
\r
211 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
216 TODO: Mineral and ground material properties
\r
217 - This way mineral ground toughness can be calculated with just
\r
218 some formula, as well as tool strengths
\r
220 TODO: Flowing water to actually contain flow direction information
\r
221 - There is a space for this - it just has to be implemented.
\r
223 SUGG: Erosion simulation at map generation time
\r
224 - Simulate water flows, which would carve out dirt fast and
\r
225 then turn stone into gravel and sand and relocate it.
\r
226 - How about relocating minerals, too? Coal and gold in
\r
227 downstream sand and gravel would be kind of cool
\r
228 - This would need a better way of handling minerals, mainly
\r
229 to have mineral content as a separate field. the first
\r
230 parameter field is free for this.
\r
231 - Simulate rock falling from cliffs when water has removed
\r
232 enough solid rock from the bottom
\r
234 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
235 of low-res 3d noise and interpolate linearly.
\r
238 * Possibly add some kind of erosion and other stuff
\r
239 * Better water generation (spread it to underwater caverns but don't
\r
240 fill dungeons that don't touch big water masses)
\r
241 * When generating a chunk and the neighboring chunk doesn't have mud
\r
242 and stuff yet and the ground is fairly flat, the mud will flow to
\r
243 the other chunk making nasty straight walls when the other chunk
\r
244 is generated. Fix it. Maybe just a special case if the ground is
\r
249 * Make an "environment metafile" to store at least time of day
\r
250 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
251 - Or maybe move content_features to material.{h,cpp}?
\r
253 Make a system for pregenerating quick information for mapblocks, so
\r
254 that the client can show them as cubes before they are actually sent
\r
257 Making it more portable:
\r
258 ------------------------
\r
259 * Some MSVC: std::sto* are defined without a namespace and collide
\r
260 with the ones in utility.h
\r
262 ======================================================================
\r
268 #pragma message ("Disabling unit tests")
\r
270 #warning "Disabling unit tests"
\r
272 // Disable unit tests
\r
273 #define ENABLE_TESTS 0
\r
275 // Enable unit tests
\r
276 #define ENABLE_TESTS 1
\r
280 #pragma comment(lib, "Irrlicht.lib")
\r
281 //#pragma comment(lib, "jthread.lib")
\r
282 #pragma comment(lib, "zlibwapi.lib")
\r
283 #pragma comment(lib, "Shell32.lib")
\r
284 // This would get rid of the console window
\r
285 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
288 #include <iostream>
\r
290 //#include <jmutexautolock.h>
\r
291 #include <locale.h>
\r
293 #include "common_irrlicht.h"
\r
296 //#include "player.h"
\r
298 #include "server.h"
\r
299 //#include "client.h"
\r
300 #include "constants.h"
\r
301 #include "porting.h"
\r
302 #include "gettime.h"
\r
303 #include "guiMessageMenu.h"
\r
304 #include "filesys.h"
\r
305 #include "config.h"
\r
306 #include "guiMainMenu.h"
\r
307 #include "mineral.h"
\r
308 //#include "noise.h"
\r
309 //#include "tile.h"
\r
310 #include "materials.h"
\r
313 // This makes textures
\r
314 ITextureSource *g_texturesource = NULL;
\r
318 These are loaded from the config file.
\r
321 Settings g_settings;
\r
322 // This is located in defaultsettings.cpp
\r
323 extern void set_default_settings();
\r
333 gui::IGUIEnvironment* guienv = NULL;
\r
334 gui::IGUIStaticText *guiroot = NULL;
\r
336 MainMenuManager g_menumgr;
\r
338 bool noMenuActive()
\r
340 return (g_menumgr.menuCount() == 0);
\r
343 // Passed to menus to allow disconnecting and exiting
\r
345 MainGameCallback *g_gamecallback = NULL;
\r
352 std::ostream *dout_con_ptr = &dummyout;
\r
353 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
354 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
355 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
356 //std::ostream *dout_con_ptr = &dstream;
\r
357 //std::ostream *derr_con_ptr = &dstream;
\r
360 std::ostream *dout_server_ptr = &dstream;
\r
361 std::ostream *derr_server_ptr = &dstream;
\r
364 std::ostream *dout_client_ptr = &dstream;
\r
365 std::ostream *derr_client_ptr = &dstream;
\r
368 gettime.h implementation
\r
371 // A small helper class
\r
375 TimeGetter(IrrlichtDevice *device):
\r
380 if(m_device == NULL)
\r
382 return m_device->getTimer()->getRealTime();
\r
385 IrrlichtDevice *m_device;
\r
388 // A pointer to a global instance of the time getter
\r
389 TimeGetter *g_timegetter = NULL;
\r
393 if(g_timegetter == NULL)
\r
395 return g_timegetter->getTime();
\r
399 Event handler for Irrlicht
\r
401 NOTE: Everything possible should be moved out from here,
\r
402 probably to InputHandler and the_game
\r
405 class MyEventReceiver : public IEventReceiver
\r
408 // This is the one method that we have to implement
\r
409 virtual bool OnEvent(const SEvent& event)
\r
412 React to nothing here if a menu is active
\r
414 if(noMenuActive() == false)
\r
419 // Remember whether each key is down or up
\r
420 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
422 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
424 if(event.KeyInput.PressedDown)
\r
425 keyWasDown[event.KeyInput.Key] = true;
\r
428 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
430 if(noMenuActive() == false)
\r
432 left_active = false;
\r
433 middle_active = false;
\r
434 right_active = false;
\r
438 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
439 left_active = event.MouseInput.isLeftPressed();
\r
440 middle_active = event.MouseInput.isMiddlePressed();
\r
441 right_active = event.MouseInput.isRightPressed();
\r
443 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
445 leftclicked = true;
\r
447 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
449 rightclicked = true;
\r
451 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
453 leftreleased = true;
\r
455 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
457 rightreleased = true;
\r
459 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
461 mouse_wheel += event.MouseInput.Wheel;
\r
469 bool IsKeyDown(EKEY_CODE keyCode) const
\r
471 return keyIsDown[keyCode];
\r
474 // Checks whether a key was down and resets the state
\r
475 bool WasKeyDown(EKEY_CODE keyCode)
\r
477 bool b = keyWasDown[keyCode];
\r
478 keyWasDown[keyCode] = false;
\r
482 s32 getMouseWheel()
\r
484 s32 a = mouse_wheel;
\r
491 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
493 keyIsDown[i] = false;
\r
494 keyWasDown[i] = false;
\r
497 leftclicked = false;
\r
498 rightclicked = false;
\r
499 leftreleased = false;
\r
500 rightreleased = false;
\r
502 left_active = false;
\r
503 middle_active = false;
\r
504 right_active = false;
\r
517 bool rightreleased;
\r
520 bool middle_active;
\r
526 IrrlichtDevice *m_device;
\r
528 // The current state of keys
\r
529 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
530 // Whether a key has been pressed or not
\r
531 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
535 Separated input handler
\r
538 class RealInputHandler : public InputHandler
\r
541 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
543 m_receiver(receiver)
\r
546 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
548 return m_receiver->IsKeyDown(keyCode);
\r
550 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
552 return m_receiver->WasKeyDown(keyCode);
\r
554 virtual v2s32 getMousePos()
\r
556 return m_device->getCursorControl()->getPosition();
\r
558 virtual void setMousePos(s32 x, s32 y)
\r
560 m_device->getCursorControl()->setPosition(x, y);
\r
563 virtual bool getLeftState()
\r
565 return m_receiver->left_active;
\r
567 virtual bool getRightState()
\r
569 return m_receiver->right_active;
\r
572 virtual bool getLeftClicked()
\r
574 return m_receiver->leftclicked;
\r
576 virtual bool getRightClicked()
\r
578 return m_receiver->rightclicked;
\r
580 virtual void resetLeftClicked()
\r
582 m_receiver->leftclicked = false;
\r
584 virtual void resetRightClicked()
\r
586 m_receiver->rightclicked = false;
\r
589 virtual bool getLeftReleased()
\r
591 return m_receiver->leftreleased;
\r
593 virtual bool getRightReleased()
\r
595 return m_receiver->rightreleased;
\r
597 virtual void resetLeftReleased()
\r
599 m_receiver->leftreleased = false;
\r
601 virtual void resetRightReleased()
\r
603 m_receiver->rightreleased = false;
\r
606 virtual s32 getMouseWheel()
\r
608 return m_receiver->getMouseWheel();
\r
613 m_receiver->clearInput();
\r
616 IrrlichtDevice *m_device;
\r
617 MyEventReceiver *m_receiver;
\r
620 class RandomInputHandler : public InputHandler
\r
623 RandomInputHandler()
\r
627 leftclicked = false;
\r
628 rightclicked = false;
\r
629 leftreleased = false;
\r
630 rightreleased = false;
\r
631 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
632 keydown[i] = false;
\r
634 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
636 return keydown[keyCode];
\r
638 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
642 virtual v2s32 getMousePos()
\r
646 virtual void setMousePos(s32 x, s32 y)
\r
648 mousepos = v2s32(x,y);
\r
651 virtual bool getLeftState()
\r
655 virtual bool getRightState()
\r
660 virtual bool getLeftClicked()
\r
662 return leftclicked;
\r
664 virtual bool getRightClicked()
\r
666 return rightclicked;
\r
668 virtual void resetLeftClicked()
\r
670 leftclicked = false;
\r
672 virtual void resetRightClicked()
\r
674 rightclicked = false;
\r
677 virtual bool getLeftReleased()
\r
679 return leftreleased;
\r
681 virtual bool getRightReleased()
\r
683 return rightreleased;
\r
685 virtual void resetLeftReleased()
\r
687 leftreleased = false;
\r
689 virtual void resetRightReleased()
\r
691 rightreleased = false;
\r
694 virtual s32 getMouseWheel()
\r
699 virtual void step(float dtime)
\r
702 static float counter1 = 0;
\r
706 counter1 = 0.1*Rand(1, 40);
\r
707 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
711 static float counter1 = 0;
\r
715 counter1 = 0.1*Rand(1, 40);
\r
716 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
720 static float counter1 = 0;
\r
724 counter1 = 0.1*Rand(1, 40);
\r
725 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
729 static float counter1 = 0;
\r
733 counter1 = 0.1*Rand(1, 40);
\r
734 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
738 static float counter1 = 0;
\r
742 counter1 = 0.1*Rand(1, 20);
\r
743 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
747 static float counter1 = 0;
\r
751 counter1 = 0.1*Rand(1, 30);
\r
752 leftdown = !leftdown;
\r
754 leftclicked = true;
\r
756 leftreleased = true;
\r
760 static float counter1 = 0;
\r
764 counter1 = 0.1*Rand(1, 15);
\r
765 rightdown = !rightdown;
\r
767 rightclicked = true;
\r
769 rightreleased = true;
\r
772 mousepos += mousespeed;
\r
775 s32 Rand(s32 min, s32 max)
\r
777 return (myrand()%(max-min+1))+min;
\r
780 bool keydown[KEY_KEY_CODES_COUNT];
\r
788 bool rightreleased;
\r
791 // These are defined global so that they're not optimized too much.
\r
792 // Can't change them to volatile.
\r
797 std::string tempstring;
\r
798 std::string tempstring2;
\r
803 dstream<<"The following test should take around 20ms."<<std::endl;
\r
804 TimeTaker timer("Testing std::string speed");
\r
805 const u32 jj = 10000;
\r
806 for(u32 j=0; j<jj; j++)
\r
811 for(u32 i=0; i<ii; i++){
\r
812 tempstring2 += "asd";
\r
814 for(u32 i=0; i<ii+1; i++){
\r
815 tempstring += "asd";
\r
816 if(tempstring == tempstring2)
\r
822 dstream<<"All of the following tests should take around 100ms each."
\r
826 TimeTaker timer("Testing floating-point conversion speed");
\r
828 for(u32 i=0; i<4000000; i++){
\r
835 TimeTaker timer("Testing floating-point vector speed");
\r
837 tempv3f1 = v3f(1,2,3);
\r
838 tempv3f2 = v3f(4,5,6);
\r
839 for(u32 i=0; i<10000000; i++){
\r
840 tempf += tempv3f1.dotProduct(tempv3f2);
\r
841 tempv3f2 += v3f(7,8,9);
\r
846 TimeTaker timer("Testing core::map speed");
\r
848 core::map<v2s16, f32> map1;
\r
851 for(s16 y=0; y<ii; y++){
\r
852 for(s16 x=0; x<ii; x++){
\r
853 map1.insert(v2s16(x,y), tempf);
\r
857 for(s16 y=ii-1; y>=0; y--){
\r
858 for(s16 x=0; x<ii; x++){
\r
859 tempf = map1[v2s16(x,y)];
\r
865 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
866 TimeTaker timer("Testing mutex speed");
\r
879 // Do at least 10ms
\r
880 while(timer.getTime() < 10);
\r
882 u32 dtime = timer.stop();
\r
883 u32 per_ms = n / dtime;
\r
884 std::cout<<"Done. "<<dtime<<"ms, "
\r
885 <<per_ms<<"/ms"<<std::endl;
\r
889 int main(int argc, char *argv[])
\r
895 // List all allowed options
\r
896 core::map<std::string, ValueSpec> allowed_options;
\r
897 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
898 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
899 "Run server directly"));
\r
900 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
901 "Load configuration from specified file"));
\r
902 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
903 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
904 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
905 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
906 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
907 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
909 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
911 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
915 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
917 if(ret == false || cmd_args.getFlag("help"))
\r
919 dstream<<"Allowed options:"<<std::endl;
\r
920 for(core::map<std::string, ValueSpec>::Iterator
\r
921 i = allowed_options.getIterator();
\r
922 i.atEnd() == false; i++)
\r
924 dstream<<" --"<<i.getNode()->getKey();
\r
925 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
930 dstream<<" <value>";
\r
932 dstream<<std::endl;
\r
934 if(i.getNode()->getValue().help != NULL)
\r
936 dstream<<" "<<i.getNode()->getValue().help
\r
941 return cmd_args.getFlag("help") ? 0 : 1;
\r
945 Low-level initialization
\r
948 bool disable_stderr = false;
\r
950 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
951 disable_stderr = true;
\r
954 // Initialize debug streams
\r
955 debugstreams_init(disable_stderr, DEBUGFILE);
\r
956 // Initialize debug stacks
\r
957 debug_stacks_init();
\r
959 DSTACK(__FUNCTION_NAME);
\r
961 porting::signal_handler_init();
\r
962 bool &kill = *porting::signal_handler_killstatus();
\r
964 porting::initializePaths();
\r
965 // Create user data directory
\r
966 fs::CreateDir(porting::path_userdata);
\r
968 // C-style stuff initialization
\r
969 initializeMaterialProperties();
\r
972 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
974 // Print startup message
\r
975 dstream<<DTIME<<"minetest-c55"
\r
976 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
981 Basic initialization
\r
984 // Initialize default settings
\r
985 set_default_settings();
\r
987 // Set locale. This is for forcing '.' as the decimal point.
\r
988 std::locale::global(std::locale("C"));
\r
989 // This enables printing all characters in bitmap font
\r
990 setlocale(LC_CTYPE, "en_US");
\r
992 // Initialize sockets
\r
994 atexit(sockets_cleanup);
\r
1004 // Path of configuration file in use
\r
1005 std::string configpath = "";
\r
1007 if(cmd_args.exists("config"))
\r
1009 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1012 dstream<<"Could not read configuration from \""
\r
1013 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1016 configpath = cmd_args.get("config");
\r
1020 core::array<std::string> filenames;
\r
1021 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1022 #ifdef RUN_IN_PLACE
\r
1023 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1026 for(u32 i=0; i<filenames.size(); i++)
\r
1028 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1031 configpath = filenames[i];
\r
1036 // If no path found, use the first one (menu creates the file)
\r
1037 if(configpath == "")
\r
1038 configpath = filenames[0];
\r
1041 // Initialize random seed
\r
1046 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1048 These are needed for unit tests at least.
\r
1051 // Initial call with g_texturesource not set.
\r
1058 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1059 || cmd_args.getFlag("enable-unittests") == true)
\r
1064 /*for(s16 y=-100; y<100; y++)
\r
1065 for(s16 x=-100; x<100; x++)
\r
1067 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1077 if(cmd_args.exists("port"))
\r
1078 port = cmd_args.getU16("port");
\r
1079 else if(g_settings.exists("port"))
\r
1080 port = g_settings.getU16("port");
\r
1085 std::string map_dir = porting::path_userdata+"/map";
\r
1086 if(cmd_args.exists("map-dir"))
\r
1087 map_dir = cmd_args.get("map-dir");
\r
1088 else if(g_settings.exists("map-dir"))
\r
1089 map_dir = g_settings.get("map-dir");
\r
1091 // Run dedicated server if asked to
\r
1092 if(cmd_args.getFlag("server"))
\r
1094 DSTACK("Dedicated server branch");
\r
1097 Server server(map_dir.c_str());
\r
1098 server.start(port);
\r
1101 dedicated_server_loop(server, kill);
\r
1110 // Address to connect to
\r
1111 std::string address = "";
\r
1113 if(cmd_args.exists("address"))
\r
1115 address = cmd_args.get("address");
\r
1119 address = g_settings.get("address");
\r
1122 std::string playername = g_settings.get("name");
\r
1125 Device initialization
\r
1128 // Resolution selection
\r
1130 bool fullscreen = false;
\r
1131 u16 screenW = g_settings.getU16("screenW");
\r
1132 u16 screenH = g_settings.getU16("screenH");
\r
1134 // Determine driver
\r
1136 video::E_DRIVER_TYPE driverType;
\r
1138 std::string driverstring = g_settings.get("video_driver");
\r
1140 if(driverstring == "null")
\r
1141 driverType = video::EDT_NULL;
\r
1142 else if(driverstring == "software")
\r
1143 driverType = video::EDT_SOFTWARE;
\r
1144 else if(driverstring == "burningsvideo")
\r
1145 driverType = video::EDT_BURNINGSVIDEO;
\r
1146 else if(driverstring == "direct3d8")
\r
1147 driverType = video::EDT_DIRECT3D8;
\r
1148 else if(driverstring == "direct3d9")
\r
1149 driverType = video::EDT_DIRECT3D9;
\r
1150 else if(driverstring == "opengl")
\r
1151 driverType = video::EDT_OPENGL;
\r
1154 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1155 "to opengl"<<std::endl;
\r
1156 driverType = video::EDT_OPENGL;
\r
1160 Create device and exit if creation failed
\r
1163 MyEventReceiver receiver;
\r
1165 IrrlichtDevice *device;
\r
1166 device = createDevice(driverType,
\r
1167 core::dimension2d<u32>(screenW, screenH),
\r
1168 16, fullscreen, false, false, &receiver);
\r
1171 return 1; // could not create selected driver.
\r
1173 // Set device in game parameters
\r
1176 // Create time getter
\r
1177 g_timegetter = new TimeGetter(device);
\r
1179 // Create game callback for menus
\r
1180 g_gamecallback = new MainGameCallback(device);
\r
1182 // Create texture source
\r
1183 g_texturesource = new TextureSource(device);
\r
1186 Speed tests (done after irrlicht is loaded to get timer)
\r
1188 if(cmd_args.getFlag("speedtests"))
\r
1190 dstream<<"Running speed tests"<<std::endl;
\r
1195 device->setResizable(true);
\r
1197 bool random_input = g_settings.getBool("random_input")
\r
1198 || cmd_args.getFlag("random-input");
\r
1199 InputHandler *input = NULL;
\r
1201 input = new RandomInputHandler();
\r
1203 input = new RealInputHandler(device, &receiver);
\r
1206 Continue initialization
\r
1209 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1212 This changes the minimum allowed number of vertices in a VBO.
\r
1215 //driver->setMinHardwareBufferVertexCount(50);
\r
1217 scene::ISceneManager* smgr = device->getSceneManager();
\r
1219 guienv = device->getGUIEnvironment();
\r
1220 gui::IGUISkin* skin = guienv->getSkin();
\r
1221 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1223 skin->setFont(font);
\r
1225 dstream<<"WARNING: Font file was not found."
\r
1226 " Using default font."<<std::endl;
\r
1227 // If font was not found, this will get us one
\r
1228 font = skin->getFont();
\r
1231 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1232 dstream<<"text_height="<<text_height<<std::endl;
\r
1234 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1235 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1236 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1237 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1238 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1239 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1242 Preload some textures and stuff
\r
1245 init_content_inventory_texture_paths();
\r
1246 init_mapnode(); // Second call with g_texturesource set
\r
1254 If an error occurs, this is set to something and the
\r
1255 menu-game loop is restarted. It is then displayed before
\r
1258 std::wstring error_message = L"";
\r
1263 while(device->run() && kill == false)
\r
1265 // This is used for catching disconnects
\r
1270 Clear everything from the GUIEnvironment
\r
1275 We need some kind of a root node to be able to add
\r
1276 custom gui elements directly on the screen.
\r
1277 Otherwise they won't be automatically drawn.
\r
1279 guiroot = guienv->addStaticText(L"",
\r
1280 core::rect<s32>(0, 0, 10000, 10000));
\r
1283 Out-of-game menu loop.
\r
1285 Loop quits when menu returns proper parameters.
\r
1287 while(kill == false)
\r
1289 // Cursor can be non-visible when coming from the game
\r
1290 device->getCursorControl()->setVisible(true);
\r
1291 // Some stuff are left to scene manager when coming from the game
\r
1292 // (map at least?)
\r
1294 // Reset or hide the debug gui texts
\r
1295 /*guitext->setText(L"Minetest-c55");
\r
1296 guitext2->setVisible(false);
\r
1297 guitext_info->setVisible(false);
\r
1298 guitext_chat->setVisible(false);*/
\r
1300 // Initialize menu data
\r
1301 MainMenuData menudata;
\r
1302 menudata.address = narrow_to_wide(address);
\r
1303 menudata.name = narrow_to_wide(playername);
\r
1304 menudata.port = narrow_to_wide(itos(port));
\r
1305 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1307 GUIMainMenu *menu =
\r
1308 new GUIMainMenu(guienv, guiroot, -1,
\r
1309 &g_menumgr, &menudata, g_gamecallback);
\r
1310 menu->allowFocusRemoval(true);
\r
1312 if(error_message != L"")
\r
1314 dstream<<"WARNING: error_message = "
\r
1315 <<wide_to_narrow(error_message)<<std::endl;
\r
1317 GUIMessageMenu *menu2 =
\r
1318 new GUIMessageMenu(guienv, guiroot, -1,
\r
1319 &g_menumgr, error_message.c_str());
\r
1321 error_message = L"";
\r
1324 video::IVideoDriver* driver = device->getVideoDriver();
\r
1326 dstream<<"Created main menu"<<std::endl;
\r
1328 while(device->run() && kill == false)
\r
1330 if(menu->getStatus() == true)
\r
1333 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1334 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1335 guienv->drawAll();
\r
1336 driver->endScene();
\r
1339 // Break out of menu-game loop to shut down cleanly
\r
1340 if(device->run() == false || kill == true)
\r
1343 dstream<<"Dropping main menu"<<std::endl;
\r
1347 // Delete map if requested
\r
1348 if(menudata.delete_map)
\r
1350 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1352 error_message = L"Delete failed";
\r
1356 playername = wide_to_narrow(menudata.name);
\r
1357 address = wide_to_narrow(menudata.address);
\r
1358 int newport = stoi(wide_to_narrow(menudata.port));
\r
1361 //port = stoi(wide_to_narrow(menudata.port));
\r
1362 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1364 // Check for valid parameters, restart menu if invalid.
\r
1365 if(playername == "")
\r
1367 error_message = L"Name required.";
\r
1372 g_settings.set("name", playername);
\r
1373 g_settings.set("address", address);
\r
1374 g_settings.set("port", itos(port));
\r
1375 // Update configuration file
\r
1376 if(configpath != "")
\r
1377 g_settings.updateConfigFile(configpath.c_str());
\r
1379 // Continue to game
\r
1383 // Break out of menu-game loop to shut down cleanly
\r
1384 if(device->run() == false)
\r
1404 catch(con::PeerNotFoundException &e)
\r
1406 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1407 error_message = L"Connection error (timed out?)";
\r
1409 catch(SocketException &e)
\r
1411 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1412 error_message = L"Socket error (port already in use?)";
\r
1415 catch(std::exception &e)
\r
1417 std::string narrow_message = "Some exception, what()=\"";
\r
1418 narrow_message += e.what();
\r
1419 narrow_message += "\"";
\r
1420 dstream<<DTIME<<narrow_message<<std::endl;
\r
1421 error_message = narrow_to_wide(narrow_message);
\r
1425 } // Menu-game loop
\r
1430 In the end, delete the Irrlicht device.
\r
1434 END_DEBUG_EXCEPTION_HANDLER
\r
1436 debugstreams_deinit();
\r