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
171 TODO: Untie client network operations from framerate
\r
172 - Needs some input queues or something
\r
173 - This won't give much performance boost because calculating block
\r
174 meshes takes so long
\r
176 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
178 TODO: Don't update all meshes always on single node changes, but
\r
179 check which ones should be updated
\r
180 - implement Map::updateNodeMeshes() and the usage of it
\r
181 - It will give almost always a 4x boost in mesh update performance.
\r
185 - Tool/weapon visualization
\r
190 SUGG: Make an option to the server to disable building and digging near
\r
191 the starting position
\r
193 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
195 * Fix the problem with the server constantly saving one or a few
\r
196 blocks? List the first saved block, maybe it explains.
\r
197 - It is probably caused by oscillating water
\r
198 * Make a small history check to transformLiquids to detect and log
\r
199 continuous oscillations, in such detail that they can be fixed.
\r
204 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
206 SUGG: MovingObject::move and Player::move are basically the same.
\r
208 - NOTE: Player::move is more up-to-date.
\r
209 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
214 TODO: Mineral and ground material properties
\r
215 - This way mineral ground toughness can be calculated with just
\r
216 some formula, as well as tool strengths
\r
218 TODO: Flowing water to actually contain flow direction information
\r
219 - There is a space for this - it just has to be implemented.
\r
221 SUGG: Erosion simulation at map generation time
\r
222 - Simulate water flows, which would carve out dirt fast and
\r
223 then turn stone into gravel and sand and relocate it.
\r
224 - How about relocating minerals, too? Coal and gold in
\r
225 downstream sand and gravel would be kind of cool
\r
226 - This would need a better way of handling minerals, mainly
\r
227 to have mineral content as a separate field. the first
\r
228 parameter field is free for this.
\r
229 - Simulate rock falling from cliffs when water has removed
\r
230 enough solid rock from the bottom
\r
232 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
233 of low-res 3d noise and interpolate linearly.
\r
236 * Possibly add some kind of erosion and other stuff
\r
237 * Better water generation (spread it to underwater caverns but don't
\r
238 fill dungeons that don't touch big water masses)
\r
239 * When generating a chunk and the neighboring chunk doesn't have mud
\r
240 and stuff yet and the ground is fairly flat, the mud will flow to
\r
241 the other chunk making nasty straight walls when the other chunk
\r
242 is generated. Fix it. Maybe just a special case if the ground is
\r
247 * Make an "environment metafile" to store at least time of day
\r
248 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
249 - Or maybe move content_features to material.{h,cpp}?
\r
251 Make a system for pregenerating quick information for mapblocks, so
\r
252 that the client can show them as cubes before they are actually sent
\r
255 Making it more portable:
\r
256 ------------------------
\r
257 * Some MSVC: std::sto* are defined without a namespace and collide
\r
258 with the ones in utility.h
\r
260 ======================================================================
\r
266 #pragma message ("Disabling unit tests")
\r
268 #warning "Disabling unit tests"
\r
270 // Disable unit tests
\r
271 #define ENABLE_TESTS 0
\r
273 // Enable unit tests
\r
274 #define ENABLE_TESTS 1
\r
278 #pragma comment(lib, "Irrlicht.lib")
\r
279 //#pragma comment(lib, "jthread.lib")
\r
280 #pragma comment(lib, "zlibwapi.lib")
\r
281 #pragma comment(lib, "Shell32.lib")
\r
282 // This would get rid of the console window
\r
283 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
286 #include <iostream>
\r
288 //#include <jmutexautolock.h>
\r
289 #include <locale.h>
\r
291 #include "common_irrlicht.h"
\r
294 //#include "player.h"
\r
296 #include "server.h"
\r
297 //#include "client.h"
\r
298 #include "constants.h"
\r
299 #include "porting.h"
\r
300 #include "gettime.h"
\r
301 #include "guiMessageMenu.h"
\r
302 #include "filesys.h"
\r
303 #include "config.h"
\r
304 #include "guiMainMenu.h"
\r
305 #include "mineral.h"
\r
306 //#include "noise.h"
\r
307 //#include "tile.h"
\r
308 #include "materials.h"
\r
311 // This makes textures
\r
312 ITextureSource *g_texturesource = NULL;
\r
316 These are loaded from the config file.
\r
319 Settings g_settings;
\r
320 // This is located in defaultsettings.cpp
\r
321 extern void set_default_settings();
\r
331 gui::IGUIEnvironment* guienv = NULL;
\r
332 gui::IGUIStaticText *guiroot = NULL;
\r
334 MainMenuManager g_menumgr;
\r
336 bool noMenuActive()
\r
338 return (g_menumgr.menuCount() == 0);
\r
341 // Passed to menus to allow disconnecting and exiting
\r
343 MainGameCallback *g_gamecallback = NULL;
\r
350 std::ostream *dout_con_ptr = &dummyout;
\r
351 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
352 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
353 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
354 //std::ostream *dout_con_ptr = &dstream;
\r
355 //std::ostream *derr_con_ptr = &dstream;
\r
358 std::ostream *dout_server_ptr = &dstream;
\r
359 std::ostream *derr_server_ptr = &dstream;
\r
362 std::ostream *dout_client_ptr = &dstream;
\r
363 std::ostream *derr_client_ptr = &dstream;
\r
366 gettime.h implementation
\r
369 // A small helper class
\r
373 TimeGetter(IrrlichtDevice *device):
\r
378 if(m_device == NULL)
\r
380 return m_device->getTimer()->getRealTime();
\r
383 IrrlichtDevice *m_device;
\r
386 // A pointer to a global instance of the time getter
\r
387 TimeGetter *g_timegetter = NULL;
\r
391 if(g_timegetter == NULL)
\r
393 return g_timegetter->getTime();
\r
397 Event handler for Irrlicht
\r
399 NOTE: Everything possible should be moved out from here,
\r
400 probably to InputHandler and the_game
\r
403 class MyEventReceiver : public IEventReceiver
\r
406 // This is the one method that we have to implement
\r
407 virtual bool OnEvent(const SEvent& event)
\r
410 React to nothing here if a menu is active
\r
412 if(noMenuActive() == false)
\r
417 // Remember whether each key is down or up
\r
418 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
420 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
422 if(event.KeyInput.PressedDown)
\r
423 keyWasDown[event.KeyInput.Key] = true;
\r
426 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
428 if(noMenuActive() == false)
\r
430 left_active = false;
\r
431 middle_active = false;
\r
432 right_active = false;
\r
436 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
437 left_active = event.MouseInput.isLeftPressed();
\r
438 middle_active = event.MouseInput.isMiddlePressed();
\r
439 right_active = event.MouseInput.isRightPressed();
\r
441 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
443 leftclicked = true;
\r
445 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
447 rightclicked = true;
\r
449 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
451 leftreleased = true;
\r
453 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
455 rightreleased = true;
\r
457 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
459 mouse_wheel += event.MouseInput.Wheel;
\r
467 bool IsKeyDown(EKEY_CODE keyCode) const
\r
469 return keyIsDown[keyCode];
\r
472 // Checks whether a key was down and resets the state
\r
473 bool WasKeyDown(EKEY_CODE keyCode)
\r
475 bool b = keyWasDown[keyCode];
\r
476 keyWasDown[keyCode] = false;
\r
480 s32 getMouseWheel()
\r
482 s32 a = mouse_wheel;
\r
489 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
491 keyIsDown[i] = false;
\r
492 keyWasDown[i] = false;
\r
495 leftclicked = false;
\r
496 rightclicked = false;
\r
497 leftreleased = false;
\r
498 rightreleased = false;
\r
500 left_active = false;
\r
501 middle_active = false;
\r
502 right_active = false;
\r
515 bool rightreleased;
\r
518 bool middle_active;
\r
524 IrrlichtDevice *m_device;
\r
526 // The current state of keys
\r
527 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
528 // Whether a key has been pressed or not
\r
529 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
533 Separated input handler
\r
536 class RealInputHandler : public InputHandler
\r
539 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
541 m_receiver(receiver)
\r
544 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
546 return m_receiver->IsKeyDown(keyCode);
\r
548 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
550 return m_receiver->WasKeyDown(keyCode);
\r
552 virtual v2s32 getMousePos()
\r
554 return m_device->getCursorControl()->getPosition();
\r
556 virtual void setMousePos(s32 x, s32 y)
\r
558 m_device->getCursorControl()->setPosition(x, y);
\r
561 virtual bool getLeftState()
\r
563 return m_receiver->left_active;
\r
565 virtual bool getRightState()
\r
567 return m_receiver->right_active;
\r
570 virtual bool getLeftClicked()
\r
572 return m_receiver->leftclicked;
\r
574 virtual bool getRightClicked()
\r
576 return m_receiver->rightclicked;
\r
578 virtual void resetLeftClicked()
\r
580 m_receiver->leftclicked = false;
\r
582 virtual void resetRightClicked()
\r
584 m_receiver->rightclicked = false;
\r
587 virtual bool getLeftReleased()
\r
589 return m_receiver->leftreleased;
\r
591 virtual bool getRightReleased()
\r
593 return m_receiver->rightreleased;
\r
595 virtual void resetLeftReleased()
\r
597 m_receiver->leftreleased = false;
\r
599 virtual void resetRightReleased()
\r
601 m_receiver->rightreleased = false;
\r
604 virtual s32 getMouseWheel()
\r
606 return m_receiver->getMouseWheel();
\r
611 m_receiver->clearInput();
\r
614 IrrlichtDevice *m_device;
\r
615 MyEventReceiver *m_receiver;
\r
618 class RandomInputHandler : public InputHandler
\r
621 RandomInputHandler()
\r
625 leftclicked = false;
\r
626 rightclicked = false;
\r
627 leftreleased = false;
\r
628 rightreleased = false;
\r
629 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
630 keydown[i] = false;
\r
632 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
634 return keydown[keyCode];
\r
636 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
640 virtual v2s32 getMousePos()
\r
644 virtual void setMousePos(s32 x, s32 y)
\r
646 mousepos = v2s32(x,y);
\r
649 virtual bool getLeftState()
\r
653 virtual bool getRightState()
\r
658 virtual bool getLeftClicked()
\r
660 return leftclicked;
\r
662 virtual bool getRightClicked()
\r
664 return rightclicked;
\r
666 virtual void resetLeftClicked()
\r
668 leftclicked = false;
\r
670 virtual void resetRightClicked()
\r
672 rightclicked = false;
\r
675 virtual bool getLeftReleased()
\r
677 return leftreleased;
\r
679 virtual bool getRightReleased()
\r
681 return rightreleased;
\r
683 virtual void resetLeftReleased()
\r
685 leftreleased = false;
\r
687 virtual void resetRightReleased()
\r
689 rightreleased = false;
\r
692 virtual s32 getMouseWheel()
\r
697 virtual void step(float dtime)
\r
700 static float counter1 = 0;
\r
704 counter1 = 0.1*Rand(1, 40);
\r
705 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
709 static float counter1 = 0;
\r
713 counter1 = 0.1*Rand(1, 40);
\r
714 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
718 static float counter1 = 0;
\r
722 counter1 = 0.1*Rand(1, 40);
\r
723 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
727 static float counter1 = 0;
\r
731 counter1 = 0.1*Rand(1, 40);
\r
732 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
736 static float counter1 = 0;
\r
740 counter1 = 0.1*Rand(1, 20);
\r
741 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
745 static float counter1 = 0;
\r
749 counter1 = 0.1*Rand(1, 30);
\r
750 leftdown = !leftdown;
\r
752 leftclicked = true;
\r
754 leftreleased = true;
\r
758 static float counter1 = 0;
\r
762 counter1 = 0.1*Rand(1, 15);
\r
763 rightdown = !rightdown;
\r
765 rightclicked = true;
\r
767 rightreleased = true;
\r
770 mousepos += mousespeed;
\r
773 s32 Rand(s32 min, s32 max)
\r
775 return (myrand()%(max-min+1))+min;
\r
778 bool keydown[KEY_KEY_CODES_COUNT];
\r
786 bool rightreleased;
\r
789 // These are defined global so that they're not optimized too much.
\r
790 // Can't change them to volatile.
\r
795 std::string tempstring;
\r
796 std::string tempstring2;
\r
801 dstream<<"The following test should take around 20ms."<<std::endl;
\r
802 TimeTaker timer("Testing std::string speed");
\r
803 const u32 jj = 10000;
\r
804 for(u32 j=0; j<jj; j++)
\r
809 for(u32 i=0; i<ii; i++){
\r
810 tempstring2 += "asd";
\r
812 for(u32 i=0; i<ii+1; i++){
\r
813 tempstring += "asd";
\r
814 if(tempstring == tempstring2)
\r
820 dstream<<"All of the following tests should take around 100ms each."
\r
824 TimeTaker timer("Testing floating-point conversion speed");
\r
826 for(u32 i=0; i<4000000; i++){
\r
833 TimeTaker timer("Testing floating-point vector speed");
\r
835 tempv3f1 = v3f(1,2,3);
\r
836 tempv3f2 = v3f(4,5,6);
\r
837 for(u32 i=0; i<10000000; i++){
\r
838 tempf += tempv3f1.dotProduct(tempv3f2);
\r
839 tempv3f2 += v3f(7,8,9);
\r
844 TimeTaker timer("Testing core::map speed");
\r
846 core::map<v2s16, f32> map1;
\r
849 for(s16 y=0; y<ii; y++){
\r
850 for(s16 x=0; x<ii; x++){
\r
851 map1.insert(v2s16(x,y), tempf);
\r
855 for(s16 y=ii-1; y>=0; y--){
\r
856 for(s16 x=0; x<ii; x++){
\r
857 tempf = map1[v2s16(x,y)];
\r
863 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
864 TimeTaker timer("Testing mutex speed");
\r
877 // Do at least 10ms
\r
878 while(timer.getTime() < 10);
\r
880 u32 dtime = timer.stop();
\r
881 u32 per_ms = n / dtime;
\r
882 std::cout<<"Done. "<<dtime<<"ms, "
\r
883 <<per_ms<<"/ms"<<std::endl;
\r
887 int main(int argc, char *argv[])
\r
893 // List all allowed options
\r
894 core::map<std::string, ValueSpec> allowed_options;
\r
895 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
896 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
897 "Run server directly"));
\r
898 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
899 "Load configuration from specified file"));
\r
900 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
901 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
902 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
903 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
904 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
905 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
907 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
909 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
913 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
915 if(ret == false || cmd_args.getFlag("help"))
\r
917 dstream<<"Allowed options:"<<std::endl;
\r
918 for(core::map<std::string, ValueSpec>::Iterator
\r
919 i = allowed_options.getIterator();
\r
920 i.atEnd() == false; i++)
\r
922 dstream<<" --"<<i.getNode()->getKey();
\r
923 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
928 dstream<<" <value>";
\r
930 dstream<<std::endl;
\r
932 if(i.getNode()->getValue().help != NULL)
\r
934 dstream<<" "<<i.getNode()->getValue().help
\r
939 return cmd_args.getFlag("help") ? 0 : 1;
\r
943 Low-level initialization
\r
946 bool disable_stderr = false;
\r
948 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
949 disable_stderr = true;
\r
952 // Initialize debug streams
\r
953 debugstreams_init(disable_stderr, DEBUGFILE);
\r
954 // Initialize debug stacks
\r
955 debug_stacks_init();
\r
957 DSTACK(__FUNCTION_NAME);
\r
959 porting::signal_handler_init();
\r
960 bool &kill = *porting::signal_handler_killstatus();
\r
962 porting::initializePaths();
\r
963 // Create user data directory
\r
964 fs::CreateDir(porting::path_userdata);
\r
966 // C-style stuff initialization
\r
967 initializeMaterialProperties();
\r
970 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
972 // Print startup message
\r
973 dstream<<DTIME<<"minetest-c55"
\r
974 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
979 Basic initialization
\r
982 // Initialize default settings
\r
983 set_default_settings();
\r
985 // Set locale. This is for forcing '.' as the decimal point.
\r
986 std::locale::global(std::locale("C"));
\r
987 // This enables printing all characters in bitmap font
\r
988 setlocale(LC_CTYPE, "en_US");
\r
990 // Initialize sockets
\r
992 atexit(sockets_cleanup);
\r
1002 // Path of configuration file in use
\r
1003 std::string configpath = "";
\r
1005 if(cmd_args.exists("config"))
\r
1007 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1010 dstream<<"Could not read configuration from \""
\r
1011 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1014 configpath = cmd_args.get("config");
\r
1018 core::array<std::string> filenames;
\r
1019 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1020 #ifdef RUN_IN_PLACE
\r
1021 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1024 for(u32 i=0; i<filenames.size(); i++)
\r
1026 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1029 configpath = filenames[i];
\r
1034 // If no path found, use the first one (menu creates the file)
\r
1035 if(configpath == "")
\r
1036 configpath = filenames[0];
\r
1039 // Initialize random seed
\r
1044 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1046 These are needed for unit tests at least.
\r
1049 // Initial call with g_texturesource not set.
\r
1056 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1057 || cmd_args.getFlag("enable-unittests") == true)
\r
1062 /*for(s16 y=-100; y<100; y++)
\r
1063 for(s16 x=-100; x<100; x++)
\r
1065 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1075 if(cmd_args.exists("port"))
\r
1076 port = cmd_args.getU16("port");
\r
1077 else if(g_settings.exists("port"))
\r
1078 port = g_settings.getU16("port");
\r
1081 std::string map_dir = porting::path_userdata+"/map";
\r
1082 if(cmd_args.exists("map-dir"))
\r
1083 map_dir = cmd_args.get("map-dir");
\r
1084 else if(g_settings.exists("map-dir"))
\r
1085 map_dir = g_settings.get("map-dir");
\r
1087 // Run dedicated server if asked to
\r
1088 if(cmd_args.getFlag("server"))
\r
1090 DSTACK("Dedicated server branch");
\r
1093 Server server(map_dir.c_str());
\r
1094 server.start(port);
\r
1097 dedicated_server_loop(server, kill);
\r
1106 // Address to connect to
\r
1107 std::string address = "";
\r
1109 if(cmd_args.exists("address"))
\r
1111 address = cmd_args.get("address");
\r
1115 address = g_settings.get("address");
\r
1118 std::string playername = g_settings.get("name");
\r
1121 Device initialization
\r
1124 // Resolution selection
\r
1126 bool fullscreen = false;
\r
1127 u16 screenW = g_settings.getU16("screenW");
\r
1128 u16 screenH = g_settings.getU16("screenH");
\r
1130 // Determine driver
\r
1132 video::E_DRIVER_TYPE driverType;
\r
1134 std::string driverstring = g_settings.get("video_driver");
\r
1136 if(driverstring == "null")
\r
1137 driverType = video::EDT_NULL;
\r
1138 else if(driverstring == "software")
\r
1139 driverType = video::EDT_SOFTWARE;
\r
1140 else if(driverstring == "burningsvideo")
\r
1141 driverType = video::EDT_BURNINGSVIDEO;
\r
1142 else if(driverstring == "direct3d8")
\r
1143 driverType = video::EDT_DIRECT3D8;
\r
1144 else if(driverstring == "direct3d9")
\r
1145 driverType = video::EDT_DIRECT3D9;
\r
1146 else if(driverstring == "opengl")
\r
1147 driverType = video::EDT_OPENGL;
\r
1150 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1151 "to opengl"<<std::endl;
\r
1152 driverType = video::EDT_OPENGL;
\r
1156 Create device and exit if creation failed
\r
1159 MyEventReceiver receiver;
\r
1161 IrrlichtDevice *device;
\r
1162 device = createDevice(driverType,
\r
1163 core::dimension2d<u32>(screenW, screenH),
\r
1164 16, fullscreen, false, false, &receiver);
\r
1167 return 1; // could not create selected driver.
\r
1169 // Set device in game parameters
\r
1172 // Create time getter
\r
1173 g_timegetter = new TimeGetter(device);
\r
1175 // Create game callback for menus
\r
1176 g_gamecallback = new MainGameCallback(device);
\r
1178 // Create texture source
\r
1179 g_texturesource = new TextureSource(device);
\r
1182 Speed tests (done after irrlicht is loaded to get timer)
\r
1184 if(cmd_args.getFlag("speedtests"))
\r
1186 dstream<<"Running speed tests"<<std::endl;
\r
1191 device->setResizable(true);
\r
1193 bool random_input = g_settings.getBool("random_input")
\r
1194 || cmd_args.getFlag("random-input");
\r
1195 InputHandler *input = NULL;
\r
1197 input = new RandomInputHandler();
\r
1199 input = new RealInputHandler(device, &receiver);
\r
1202 Continue initialization
\r
1205 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1208 This changes the minimum allowed number of vertices in a VBO.
\r
1211 //driver->setMinHardwareBufferVertexCount(50);
\r
1213 scene::ISceneManager* smgr = device->getSceneManager();
\r
1215 guienv = device->getGUIEnvironment();
\r
1216 gui::IGUISkin* skin = guienv->getSkin();
\r
1217 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1219 skin->setFont(font);
\r
1221 dstream<<"WARNING: Font file was not found."
\r
1222 " Using default font."<<std::endl;
\r
1223 // If font was not found, this will get us one
\r
1224 font = skin->getFont();
\r
1227 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1228 dstream<<"text_height="<<text_height<<std::endl;
\r
1230 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1231 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1232 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1233 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1234 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1235 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1238 Preload some textures and stuff
\r
1241 init_content_inventory_texture_paths();
\r
1242 init_mapnode(); // Second call with g_texturesource set
\r
1252 while(device->run() && kill == false)
\r
1255 If an error occurs, this is set to something and the
\r
1256 menu-game loop is restarted. It is then displayed before
\r
1259 std::wstring error_message = L"";
\r
1261 // This is used for catching disconnects
\r
1266 Clear everything from the GUIEnvironment
\r
1271 We need some kind of a root node to be able to add
\r
1272 custom gui elements directly on the screen.
\r
1273 Otherwise they won't be automatically drawn.
\r
1275 guiroot = guienv->addStaticText(L"",
\r
1276 core::rect<s32>(0, 0, 10000, 10000));
\r
1279 Out-of-game menu loop.
\r
1281 Loop quits when menu returns proper parameters.
\r
1283 while(kill == false)
\r
1285 // Cursor can be non-visible when coming from the game
\r
1286 device->getCursorControl()->setVisible(true);
\r
1287 // Some stuff are left to scene manager when coming from the game
\r
1288 // (map at least?)
\r
1290 // Reset or hide the debug gui texts
\r
1291 /*guitext->setText(L"Minetest-c55");
\r
1292 guitext2->setVisible(false);
\r
1293 guitext_info->setVisible(false);
\r
1294 guitext_chat->setVisible(false);*/
\r
1296 // Initialize menu data
\r
1297 MainMenuData menudata;
\r
1298 menudata.address = narrow_to_wide(address);
\r
1299 menudata.name = narrow_to_wide(playername);
\r
1300 menudata.port = narrow_to_wide(itos(port));
\r
1301 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1303 GUIMainMenu *menu =
\r
1304 new GUIMainMenu(guienv, guiroot, -1,
\r
1305 &g_menumgr, &menudata, g_gamecallback);
\r
1306 menu->allowFocusRemoval(true);
\r
1308 if(error_message != L"")
\r
1310 dstream<<"WARNING: error_message = "
\r
1311 <<wide_to_narrow(error_message)<<std::endl;
\r
1313 GUIMessageMenu *menu2 =
\r
1314 new GUIMessageMenu(guienv, guiroot, -1,
\r
1315 &g_menumgr, error_message.c_str());
\r
1317 error_message = L"";
\r
1320 video::IVideoDriver* driver = device->getVideoDriver();
\r
1322 dstream<<"Created main menu"<<std::endl;
\r
1324 while(device->run() && kill == false)
\r
1326 if(menu->getStatus() == true)
\r
1329 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1330 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1331 guienv->drawAll();
\r
1332 driver->endScene();
\r
1335 // Break out of menu-game loop to shut down cleanly
\r
1336 if(device->run() == false || kill == true)
\r
1339 dstream<<"Dropping main menu"<<std::endl;
\r
1343 // Delete map if requested
\r
1344 if(menudata.delete_map)
\r
1346 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1348 error_message = L"Delete failed";
\r
1352 playername = wide_to_narrow(menudata.name);
\r
1353 address = wide_to_narrow(menudata.address);
\r
1354 int newport = stoi(wide_to_narrow(menudata.port));
\r
1357 //port = stoi(wide_to_narrow(menudata.port));
\r
1358 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1360 // Check for valid parameters, restart menu if invalid.
\r
1361 if(playername == "")
\r
1363 error_message = L"Name required.";
\r
1368 g_settings.set("name", playername);
\r
1369 g_settings.set("address", address);
\r
1370 g_settings.set("port", itos(port));
\r
1371 // Update configuration file
\r
1372 if(configpath != "")
\r
1373 g_settings.updateConfigFile(configpath.c_str());
\r
1375 // Continue to game
\r
1379 // Break out of menu-game loop to shut down cleanly
\r
1380 if(device->run() == false)
\r
1400 catch(con::PeerNotFoundException &e)
\r
1402 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1403 error_message = L"Connection error (timed out?)";
\r
1406 } // Menu-game loop
\r
1411 In the end, delete the Irrlicht device.
\r
1415 END_DEBUG_EXCEPTION_HANDLER
\r
1417 debugstreams_deinit();
\r