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
167 TODO: Get player texture (and some others) from the specified texture
\r
170 SUGG: Simple light color information to air
\r
172 TODO: Block mesh generator to tile properly on smooth lighting
\r
180 TODO: Untie client network operations from framerate
\r
181 - Needs some input queues or something
\r
182 - This won't give much performance boost because calculating block
\r
183 meshes takes so long
\r
185 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
187 TODO: Don't update all meshes always on single node changes, but
\r
188 check which ones should be updated
\r
189 - implement Map::updateNodeMeshes() and the usage of it
\r
190 - It will give almost always a 4x boost in mesh update performance.
\r
194 - Tool/weapon visualization
\r
196 FIXME: When disconnected to the menu, memory is not freed properly
\r
201 SUGG: Make an option to the server to disable building and digging near
\r
202 the starting position
\r
204 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
206 * Fix the problem with the server constantly saving one or a few
\r
207 blocks? List the first saved block, maybe it explains.
\r
208 - It is probably caused by oscillating water
\r
209 * Make a small history check to transformLiquids to detect and log
\r
210 continuous oscillations, in such detail that they can be fixed.
\r
215 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
217 SUGG: MovingObject::move and Player::move are basically the same.
\r
219 - NOTE: Player::move is more up-to-date.
\r
220 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
225 TODO: Mineral and ground material properties
\r
226 - This way mineral ground toughness can be calculated with just
\r
227 some formula, as well as tool strengths
\r
229 TODO: Flowing water to actually contain flow direction information
\r
230 - There is a space for this - it just has to be implemented.
\r
232 SUGG: Erosion simulation at map generation time
\r
233 - Simulate water flows, which would carve out dirt fast and
\r
234 then turn stone into gravel and sand and relocate it.
\r
235 - How about relocating minerals, too? Coal and gold in
\r
236 downstream sand and gravel would be kind of cool
\r
237 - This would need a better way of handling minerals, mainly
\r
238 to have mineral content as a separate field. the first
\r
239 parameter field is free for this.
\r
240 - Simulate rock falling from cliffs when water has removed
\r
241 enough solid rock from the bottom
\r
243 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
244 of low-res 3d noise and interpolate linearly.
\r
247 * Possibly add some kind of erosion and other stuff
\r
248 * Better water generation (spread it to underwater caverns but don't
\r
249 fill dungeons that don't touch big water masses)
\r
250 * When generating a chunk and the neighboring chunk doesn't have mud
\r
251 and stuff yet and the ground is fairly flat, the mud will flow to
\r
252 the other chunk making nasty straight walls when the other chunk
\r
253 is generated. Fix it. Maybe just a special case if the ground is
\r
258 * Make an "environment metafile" to store at least time of day
\r
259 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
260 - Or maybe move content_features to material.{h,cpp}?
\r
262 Make a system for pregenerating quick information for mapblocks, so
\r
263 that the client can show them as cubes before they are actually sent
\r
266 Making it more portable:
\r
267 ------------------------
\r
268 * Some MSVC: std::sto* are defined without a namespace and collide
\r
269 with the ones in utility.h
\r
271 ======================================================================
\r
277 #pragma message ("Disabling unit tests")
\r
279 #warning "Disabling unit tests"
\r
281 // Disable unit tests
\r
282 #define ENABLE_TESTS 0
\r
284 // Enable unit tests
\r
285 #define ENABLE_TESTS 1
\r
289 #pragma comment(lib, "Irrlicht.lib")
\r
290 //#pragma comment(lib, "jthread.lib")
\r
291 #pragma comment(lib, "zlibwapi.lib")
\r
292 #pragma comment(lib, "Shell32.lib")
\r
293 // This would get rid of the console window
\r
294 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
297 #include <iostream>
\r
299 //#include <jmutexautolock.h>
\r
300 #include <locale.h>
\r
302 #include "common_irrlicht.h"
\r
305 //#include "player.h"
\r
307 #include "server.h"
\r
308 //#include "client.h"
\r
309 #include "constants.h"
\r
310 #include "porting.h"
\r
311 #include "gettime.h"
\r
312 #include "guiMessageMenu.h"
\r
313 #include "filesys.h"
\r
314 #include "config.h"
\r
315 #include "guiMainMenu.h"
\r
316 #include "mineral.h"
\r
317 //#include "noise.h"
\r
318 //#include "tile.h"
\r
319 #include "materials.h"
\r
322 // This makes textures
\r
323 ITextureSource *g_texturesource = NULL;
\r
327 These are loaded from the config file.
\r
330 Settings g_settings;
\r
331 // This is located in defaultsettings.cpp
\r
332 extern void set_default_settings();
\r
342 gui::IGUIEnvironment* guienv = NULL;
\r
343 gui::IGUIStaticText *guiroot = NULL;
\r
345 MainMenuManager g_menumgr;
\r
347 bool noMenuActive()
\r
349 return (g_menumgr.menuCount() == 0);
\r
352 // Passed to menus to allow disconnecting and exiting
\r
354 MainGameCallback *g_gamecallback = NULL;
\r
361 std::ostream *dout_con_ptr = &dummyout;
\r
362 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
363 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
364 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
365 //std::ostream *dout_con_ptr = &dstream;
\r
366 //std::ostream *derr_con_ptr = &dstream;
\r
369 std::ostream *dout_server_ptr = &dstream;
\r
370 std::ostream *derr_server_ptr = &dstream;
\r
373 std::ostream *dout_client_ptr = &dstream;
\r
374 std::ostream *derr_client_ptr = &dstream;
\r
377 gettime.h implementation
\r
380 // A small helper class
\r
384 TimeGetter(IrrlichtDevice *device):
\r
389 if(m_device == NULL)
\r
391 return m_device->getTimer()->getRealTime();
\r
394 IrrlichtDevice *m_device;
\r
397 // A pointer to a global instance of the time getter
\r
398 TimeGetter *g_timegetter = NULL;
\r
402 if(g_timegetter == NULL)
\r
404 return g_timegetter->getTime();
\r
408 Event handler for Irrlicht
\r
410 NOTE: Everything possible should be moved out from here,
\r
411 probably to InputHandler and the_game
\r
414 class MyEventReceiver : public IEventReceiver
\r
417 // This is the one method that we have to implement
\r
418 virtual bool OnEvent(const SEvent& event)
\r
421 React to nothing here if a menu is active
\r
423 if(noMenuActive() == false)
\r
428 // Remember whether each key is down or up
\r
429 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
431 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
433 if(event.KeyInput.PressedDown)
\r
434 keyWasDown[event.KeyInput.Key] = true;
\r
437 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
439 if(noMenuActive() == false)
\r
441 left_active = false;
\r
442 middle_active = false;
\r
443 right_active = false;
\r
447 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
448 left_active = event.MouseInput.isLeftPressed();
\r
449 middle_active = event.MouseInput.isMiddlePressed();
\r
450 right_active = event.MouseInput.isRightPressed();
\r
452 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
454 leftclicked = true;
\r
456 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
458 rightclicked = true;
\r
460 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
462 leftreleased = true;
\r
464 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
466 rightreleased = true;
\r
468 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
470 mouse_wheel += event.MouseInput.Wheel;
\r
478 bool IsKeyDown(EKEY_CODE keyCode) const
\r
480 return keyIsDown[keyCode];
\r
483 // Checks whether a key was down and resets the state
\r
484 bool WasKeyDown(EKEY_CODE keyCode)
\r
486 bool b = keyWasDown[keyCode];
\r
487 keyWasDown[keyCode] = false;
\r
491 s32 getMouseWheel()
\r
493 s32 a = mouse_wheel;
\r
500 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
502 keyIsDown[i] = false;
\r
503 keyWasDown[i] = false;
\r
506 leftclicked = false;
\r
507 rightclicked = false;
\r
508 leftreleased = false;
\r
509 rightreleased = false;
\r
511 left_active = false;
\r
512 middle_active = false;
\r
513 right_active = false;
\r
526 bool rightreleased;
\r
529 bool middle_active;
\r
535 IrrlichtDevice *m_device;
\r
537 // The current state of keys
\r
538 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
539 // Whether a key has been pressed or not
\r
540 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
544 Separated input handler
\r
547 class RealInputHandler : public InputHandler
\r
550 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
552 m_receiver(receiver)
\r
555 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
557 return m_receiver->IsKeyDown(keyCode);
\r
559 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
561 return m_receiver->WasKeyDown(keyCode);
\r
563 virtual v2s32 getMousePos()
\r
565 return m_device->getCursorControl()->getPosition();
\r
567 virtual void setMousePos(s32 x, s32 y)
\r
569 m_device->getCursorControl()->setPosition(x, y);
\r
572 virtual bool getLeftState()
\r
574 return m_receiver->left_active;
\r
576 virtual bool getRightState()
\r
578 return m_receiver->right_active;
\r
581 virtual bool getLeftClicked()
\r
583 return m_receiver->leftclicked;
\r
585 virtual bool getRightClicked()
\r
587 return m_receiver->rightclicked;
\r
589 virtual void resetLeftClicked()
\r
591 m_receiver->leftclicked = false;
\r
593 virtual void resetRightClicked()
\r
595 m_receiver->rightclicked = false;
\r
598 virtual bool getLeftReleased()
\r
600 return m_receiver->leftreleased;
\r
602 virtual bool getRightReleased()
\r
604 return m_receiver->rightreleased;
\r
606 virtual void resetLeftReleased()
\r
608 m_receiver->leftreleased = false;
\r
610 virtual void resetRightReleased()
\r
612 m_receiver->rightreleased = false;
\r
615 virtual s32 getMouseWheel()
\r
617 return m_receiver->getMouseWheel();
\r
622 m_receiver->clearInput();
\r
625 IrrlichtDevice *m_device;
\r
626 MyEventReceiver *m_receiver;
\r
629 class RandomInputHandler : public InputHandler
\r
632 RandomInputHandler()
\r
636 leftclicked = false;
\r
637 rightclicked = false;
\r
638 leftreleased = false;
\r
639 rightreleased = false;
\r
640 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
641 keydown[i] = false;
\r
643 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
645 return keydown[keyCode];
\r
647 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
651 virtual v2s32 getMousePos()
\r
655 virtual void setMousePos(s32 x, s32 y)
\r
657 mousepos = v2s32(x,y);
\r
660 virtual bool getLeftState()
\r
664 virtual bool getRightState()
\r
669 virtual bool getLeftClicked()
\r
671 return leftclicked;
\r
673 virtual bool getRightClicked()
\r
675 return rightclicked;
\r
677 virtual void resetLeftClicked()
\r
679 leftclicked = false;
\r
681 virtual void resetRightClicked()
\r
683 rightclicked = false;
\r
686 virtual bool getLeftReleased()
\r
688 return leftreleased;
\r
690 virtual bool getRightReleased()
\r
692 return rightreleased;
\r
694 virtual void resetLeftReleased()
\r
696 leftreleased = false;
\r
698 virtual void resetRightReleased()
\r
700 rightreleased = false;
\r
703 virtual s32 getMouseWheel()
\r
708 virtual void step(float dtime)
\r
711 static float counter1 = 0;
\r
715 counter1 = 0.1*Rand(1, 40);
\r
716 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
720 static float counter1 = 0;
\r
724 counter1 = 0.1*Rand(1, 40);
\r
725 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
729 static float counter1 = 0;
\r
733 counter1 = 0.1*Rand(1, 40);
\r
734 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
738 static float counter1 = 0;
\r
742 counter1 = 0.1*Rand(1, 40);
\r
743 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
747 static float counter1 = 0;
\r
751 counter1 = 0.1*Rand(1, 20);
\r
752 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
756 static float counter1 = 0;
\r
760 counter1 = 0.1*Rand(1, 30);
\r
761 leftdown = !leftdown;
\r
763 leftclicked = true;
\r
765 leftreleased = true;
\r
769 static float counter1 = 0;
\r
773 counter1 = 0.1*Rand(1, 15);
\r
774 rightdown = !rightdown;
\r
776 rightclicked = true;
\r
778 rightreleased = true;
\r
781 mousepos += mousespeed;
\r
784 s32 Rand(s32 min, s32 max)
\r
786 return (myrand()%(max-min+1))+min;
\r
789 bool keydown[KEY_KEY_CODES_COUNT];
\r
797 bool rightreleased;
\r
800 // These are defined global so that they're not optimized too much.
\r
801 // Can't change them to volatile.
\r
806 std::string tempstring;
\r
807 std::string tempstring2;
\r
812 dstream<<"The following test should take around 20ms."<<std::endl;
\r
813 TimeTaker timer("Testing std::string speed");
\r
814 const u32 jj = 10000;
\r
815 for(u32 j=0; j<jj; j++)
\r
820 for(u32 i=0; i<ii; i++){
\r
821 tempstring2 += "asd";
\r
823 for(u32 i=0; i<ii+1; i++){
\r
824 tempstring += "asd";
\r
825 if(tempstring == tempstring2)
\r
831 dstream<<"All of the following tests should take around 100ms each."
\r
835 TimeTaker timer("Testing floating-point conversion speed");
\r
837 for(u32 i=0; i<4000000; i++){
\r
844 TimeTaker timer("Testing floating-point vector speed");
\r
846 tempv3f1 = v3f(1,2,3);
\r
847 tempv3f2 = v3f(4,5,6);
\r
848 for(u32 i=0; i<10000000; i++){
\r
849 tempf += tempv3f1.dotProduct(tempv3f2);
\r
850 tempv3f2 += v3f(7,8,9);
\r
855 TimeTaker timer("Testing core::map speed");
\r
857 core::map<v2s16, f32> map1;
\r
860 for(s16 y=0; y<ii; y++){
\r
861 for(s16 x=0; x<ii; x++){
\r
862 map1.insert(v2s16(x,y), tempf);
\r
866 for(s16 y=ii-1; y>=0; y--){
\r
867 for(s16 x=0; x<ii; x++){
\r
868 tempf = map1[v2s16(x,y)];
\r
874 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
875 TimeTaker timer("Testing mutex speed");
\r
888 // Do at least 10ms
\r
889 while(timer.getTime() < 10);
\r
891 u32 dtime = timer.stop();
\r
892 u32 per_ms = n / dtime;
\r
893 std::cout<<"Done. "<<dtime<<"ms, "
\r
894 <<per_ms<<"/ms"<<std::endl;
\r
898 void drawMenuBackground(video::IVideoDriver* driver)
\r
900 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
902 video::ITexture *bgtexture =
\r
903 driver->getTexture(porting::getDataPath("mud.png").c_str());
\r
906 s32 texturesize = 128;
\r
907 s32 tiled_y = screensize.Height / texturesize + 1;
\r
908 s32 tiled_x = screensize.Width / texturesize + 1;
\r
910 for(s32 y=0; y<tiled_y; y++)
\r
911 for(s32 x=0; x<tiled_x; x++)
\r
913 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
914 rect += v2s32(x*texturesize, y*texturesize);
\r
915 driver->draw2DImage(bgtexture, rect,
\r
916 core::rect<s32>(core::position2d<s32>(0,0),
\r
917 core::dimension2di(bgtexture->getSize())),
\r
922 video::ITexture *logotexture =
\r
923 driver->getTexture(porting::getDataPath("menulogo.png").c_str());
\r
926 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
927 logotexture->getOriginalSize().Height);
\r
930 video::SColor bgcolor(255,50,50,50);
\r
931 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
932 screensize.Width, screensize.Height);
\r
933 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
935 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
936 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
937 rect -= v2s32(logosize.X/2, 0);
\r
938 driver->draw2DImage(logotexture, rect,
\r
939 core::rect<s32>(core::position2d<s32>(0,0),
\r
940 core::dimension2di(logotexture->getSize())),
\r
945 int main(int argc, char *argv[])
\r
951 // List all allowed options
\r
952 core::map<std::string, ValueSpec> allowed_options;
\r
953 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
954 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
955 "Run server directly"));
\r
956 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
957 "Load configuration from specified file"));
\r
958 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
959 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
960 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
961 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
962 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
963 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
965 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
967 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
971 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
973 if(ret == false || cmd_args.getFlag("help"))
\r
975 dstream<<"Allowed options:"<<std::endl;
\r
976 for(core::map<std::string, ValueSpec>::Iterator
\r
977 i = allowed_options.getIterator();
\r
978 i.atEnd() == false; i++)
\r
980 dstream<<" --"<<i.getNode()->getKey();
\r
981 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
986 dstream<<" <value>";
\r
988 dstream<<std::endl;
\r
990 if(i.getNode()->getValue().help != NULL)
\r
992 dstream<<" "<<i.getNode()->getValue().help
\r
997 return cmd_args.getFlag("help") ? 0 : 1;
\r
1001 Low-level initialization
\r
1004 bool disable_stderr = false;
\r
1006 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1007 disable_stderr = true;
\r
1010 // Initialize debug streams
\r
1011 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1012 // Initialize debug stacks
\r
1013 debug_stacks_init();
\r
1015 DSTACK(__FUNCTION_NAME);
\r
1017 porting::signal_handler_init();
\r
1018 bool &kill = *porting::signal_handler_killstatus();
\r
1020 porting::initializePaths();
\r
1021 // Create user data directory
\r
1022 fs::CreateDir(porting::path_userdata);
\r
1024 // C-style stuff initialization
\r
1025 initializeMaterialProperties();
\r
1028 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1030 // Print startup message
\r
1031 dstream<<DTIME<<"minetest-c55"
\r
1032 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1033 <<", "<<BUILD_INFO
\r
1037 Basic initialization
\r
1040 // Initialize default settings
\r
1041 set_default_settings();
\r
1043 // Set locale. This is for forcing '.' as the decimal point.
\r
1044 std::locale::global(std::locale("C"));
\r
1045 // This enables printing all characters in bitmap font
\r
1046 setlocale(LC_CTYPE, "en_US");
\r
1048 // Initialize sockets
\r
1050 atexit(sockets_cleanup);
\r
1060 // Path of configuration file in use
\r
1061 std::string configpath = "";
\r
1063 if(cmd_args.exists("config"))
\r
1065 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1068 dstream<<"Could not read configuration from \""
\r
1069 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1072 configpath = cmd_args.get("config");
\r
1076 core::array<std::string> filenames;
\r
1077 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1078 #ifdef RUN_IN_PLACE
\r
1079 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1082 for(u32 i=0; i<filenames.size(); i++)
\r
1084 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1087 configpath = filenames[i];
\r
1092 // If no path found, use the first one (menu creates the file)
\r
1093 if(configpath == "")
\r
1094 configpath = filenames[0];
\r
1097 // Initialize random seed
\r
1102 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1104 These are needed for unit tests at least.
\r
1107 // Initial call with g_texturesource not set.
\r
1114 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1115 || cmd_args.getFlag("enable-unittests") == true)
\r
1120 /*for(s16 y=-100; y<100; y++)
\r
1121 for(s16 x=-100; x<100; x++)
\r
1123 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1133 if(cmd_args.exists("port"))
\r
1134 port = cmd_args.getU16("port");
\r
1135 else if(g_settings.exists("port"))
\r
1136 port = g_settings.getU16("port");
\r
1141 std::string map_dir = porting::path_userdata+"/map";
\r
1142 if(cmd_args.exists("map-dir"))
\r
1143 map_dir = cmd_args.get("map-dir");
\r
1144 else if(g_settings.exists("map-dir"))
\r
1145 map_dir = g_settings.get("map-dir");
\r
1147 // Run dedicated server if asked to
\r
1148 if(cmd_args.getFlag("server"))
\r
1150 DSTACK("Dedicated server branch");
\r
1153 Server server(map_dir.c_str());
\r
1154 server.start(port);
\r
1157 dedicated_server_loop(server, kill);
\r
1166 // Address to connect to
\r
1167 std::string address = "";
\r
1169 if(cmd_args.exists("address"))
\r
1171 address = cmd_args.get("address");
\r
1175 address = g_settings.get("address");
\r
1178 std::string playername = g_settings.get("name");
\r
1181 Device initialization
\r
1184 // Resolution selection
\r
1186 bool fullscreen = false;
\r
1187 u16 screenW = g_settings.getU16("screenW");
\r
1188 u16 screenH = g_settings.getU16("screenH");
\r
1190 // Determine driver
\r
1192 video::E_DRIVER_TYPE driverType;
\r
1194 std::string driverstring = g_settings.get("video_driver");
\r
1196 if(driverstring == "null")
\r
1197 driverType = video::EDT_NULL;
\r
1198 else if(driverstring == "software")
\r
1199 driverType = video::EDT_SOFTWARE;
\r
1200 else if(driverstring == "burningsvideo")
\r
1201 driverType = video::EDT_BURNINGSVIDEO;
\r
1202 else if(driverstring == "direct3d8")
\r
1203 driverType = video::EDT_DIRECT3D8;
\r
1204 else if(driverstring == "direct3d9")
\r
1205 driverType = video::EDT_DIRECT3D9;
\r
1206 else if(driverstring == "opengl")
\r
1207 driverType = video::EDT_OPENGL;
\r
1210 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1211 "to opengl"<<std::endl;
\r
1212 driverType = video::EDT_OPENGL;
\r
1216 Create device and exit if creation failed
\r
1219 MyEventReceiver receiver;
\r
1221 IrrlichtDevice *device;
\r
1222 device = createDevice(driverType,
\r
1223 core::dimension2d<u32>(screenW, screenH),
\r
1224 16, fullscreen, false, false, &receiver);
\r
1227 return 1; // could not create selected driver.
\r
1229 // Set device in game parameters
\r
1232 // Create time getter
\r
1233 g_timegetter = new TimeGetter(device);
\r
1235 // Create game callback for menus
\r
1236 g_gamecallback = new MainGameCallback(device);
\r
1238 // Create texture source
\r
1239 g_texturesource = new TextureSource(device);
\r
1242 Speed tests (done after irrlicht is loaded to get timer)
\r
1244 if(cmd_args.getFlag("speedtests"))
\r
1246 dstream<<"Running speed tests"<<std::endl;
\r
1251 device->setResizable(true);
\r
1253 bool random_input = g_settings.getBool("random_input")
\r
1254 || cmd_args.getFlag("random-input");
\r
1255 InputHandler *input = NULL;
\r
1257 input = new RandomInputHandler();
\r
1259 input = new RealInputHandler(device, &receiver);
\r
1262 Continue initialization
\r
1265 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1268 This changes the minimum allowed number of vertices in a VBO.
\r
1271 //driver->setMinHardwareBufferVertexCount(50);
\r
1273 scene::ISceneManager* smgr = device->getSceneManager();
\r
1275 guienv = device->getGUIEnvironment();
\r
1276 gui::IGUISkin* skin = guienv->getSkin();
\r
1277 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1279 skin->setFont(font);
\r
1281 dstream<<"WARNING: Font file was not found."
\r
1282 " Using default font."<<std::endl;
\r
1283 // If font was not found, this will get us one
\r
1284 font = skin->getFont();
\r
1287 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1288 dstream<<"text_height="<<text_height<<std::endl;
\r
1290 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1291 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1292 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1293 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1294 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1295 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1298 Preload some textures and stuff
\r
1301 init_content_inventory_texture_paths();
\r
1302 init_mapnode(); // Second call with g_texturesource set
\r
1310 If an error occurs, this is set to something and the
\r
1311 menu-game loop is restarted. It is then displayed before
\r
1314 std::wstring error_message = L"";
\r
1319 while(device->run() && kill == false)
\r
1321 // This is used for catching disconnects
\r
1326 Clear everything from the GUIEnvironment
\r
1331 We need some kind of a root node to be able to add
\r
1332 custom gui elements directly on the screen.
\r
1333 Otherwise they won't be automatically drawn.
\r
1335 guiroot = guienv->addStaticText(L"",
\r
1336 core::rect<s32>(0, 0, 10000, 10000));
\r
1339 Out-of-game menu loop.
\r
1341 Loop quits when menu returns proper parameters.
\r
1343 while(kill == false)
\r
1345 // Cursor can be non-visible when coming from the game
\r
1346 device->getCursorControl()->setVisible(true);
\r
1347 // Some stuff are left to scene manager when coming from the game
\r
1348 // (map at least?)
\r
1350 // Reset or hide the debug gui texts
\r
1351 /*guitext->setText(L"Minetest-c55");
\r
1352 guitext2->setVisible(false);
\r
1353 guitext_info->setVisible(false);
\r
1354 guitext_chat->setVisible(false);*/
\r
1356 // Initialize menu data
\r
1357 MainMenuData menudata;
\r
1358 menudata.address = narrow_to_wide(address);
\r
1359 menudata.name = narrow_to_wide(playername);
\r
1360 menudata.port = narrow_to_wide(itos(port));
\r
1361 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1362 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1363 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1364 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1366 GUIMainMenu *menu =
\r
1367 new GUIMainMenu(guienv, guiroot, -1,
\r
1368 &g_menumgr, &menudata, g_gamecallback);
\r
1369 menu->allowFocusRemoval(true);
\r
1371 if(error_message != L"")
\r
1373 dstream<<"WARNING: error_message = "
\r
1374 <<wide_to_narrow(error_message)<<std::endl;
\r
1376 GUIMessageMenu *menu2 =
\r
1377 new GUIMessageMenu(guienv, guiroot, -1,
\r
1378 &g_menumgr, error_message.c_str());
\r
1380 error_message = L"";
\r
1383 video::IVideoDriver* driver = device->getVideoDriver();
\r
1385 dstream<<"Created main menu"<<std::endl;
\r
1387 while(device->run() && kill == false)
\r
1389 if(menu->getStatus() == true)
\r
1392 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1393 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1395 drawMenuBackground(driver);
\r
1397 guienv->drawAll();
\r
1399 driver->endScene();
\r
1402 // Break out of menu-game loop to shut down cleanly
\r
1403 if(device->run() == false || kill == true)
\r
1406 dstream<<"Dropping main menu"<<std::endl;
\r
1410 // Delete map if requested
\r
1411 if(menudata.delete_map)
\r
1413 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1415 error_message = L"Delete failed";
\r
1419 playername = wide_to_narrow(menudata.name);
\r
1420 address = wide_to_narrow(menudata.address);
\r
1421 int newport = stoi(wide_to_narrow(menudata.port));
\r
1424 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1425 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1426 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1427 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1429 // Check for valid parameters, restart menu if invalid.
\r
1430 if(playername == "")
\r
1432 error_message = L"Name required.";
\r
1437 g_settings.set("name", playername);
\r
1438 g_settings.set("address", address);
\r
1439 g_settings.set("port", itos(port));
\r
1440 // Update configuration file
\r
1441 if(configpath != "")
\r
1442 g_settings.updateConfigFile(configpath.c_str());
\r
1444 // Continue to game
\r
1448 // Break out of menu-game loop to shut down cleanly
\r
1449 if(device->run() == false)
\r
1452 // Initialize mapnode again to enable changed graphics settings
\r
1472 catch(con::PeerNotFoundException &e)
\r
1474 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1475 error_message = L"Connection error (timed out?)";
\r
1477 catch(SocketException &e)
\r
1479 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1480 error_message = L"Socket error (port already in use?)";
\r
1483 catch(std::exception &e)
\r
1485 std::string narrow_message = "Some exception, what()=\"";
\r
1486 narrow_message += e.what();
\r
1487 narrow_message += "\"";
\r
1488 dstream<<DTIME<<narrow_message<<std::endl;
\r
1489 error_message = narrow_to_wide(narrow_message);
\r
1493 } // Menu-game loop
\r
1498 In the end, delete the Irrlicht device.
\r
1502 END_DEBUG_EXCEPTION_HANDLER
\r
1504 debugstreams_deinit();
\r