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
86 NOTE: If you read the text below, PLEASE DON'T TELL ANYTHING ABOUT IT TO
\r
87 ANYONE, EVEN OF THE EXISTENCE OF IT. I WILL BE VERY DISAPPOINTED IF I SEE
\r
88 PEOPLE ON THE INTERNET TALKING ABOUT THIS AND IT WILL TAKE AT LEAST HALF
\r
89 OF MY MOTIVATION AWAY.
\r
91 At the beginning, there is a sky city (like in CT), in wich is a library
\r
92 (The Important Library), a part of which explodes. Unfortunately the
\r
93 magical Important Book in the library gets thrown out in the wind and flies
\r
94 far away to the dangerous Low Lands. The book is shown to glow in blue when
\r
95 flying in the wind in some animation, when the wind flies it to the
\r
96 horizon. It is early morning and quicky sunrise takes the place of the blue
\r
97 glow. This marks the direction where the book is.
\r
99 The rulers of the sky city choose to let some brave young men go and start
\r
100 a mission to get the book back. The rulers still have some magic so they
\r
101 can let them fall onto the ground without damage, but not further. When a
\r
102 new player spawns, he will just be an another missioner.
\r
104 The Low Lands are a very dangerous and a tough place to be in, but
\r
105 immediately underneath the sky city there is fairly peaceful, but sometimes
\r
106 dangerous creatures come there too, especially at night.
\r
108 The far ancestors of the highlanders lived in the lowlands, but then they
\r
109 learned magic and went to live in the sky. They have really no idea and no
\r
110 tools to survive in the lowlands. One guy is known to have dropped down to
\r
111 the lowlands a long time ago, but it is not known if he survived.
\r
113 The missioners will have to find out how to survive in the lowlands by
\r
114 finding materials and crafting tools and weapons and armor and interesting
\r
115 items. As they build up their capabilities, they can go and explore the
\r
118 The main flying direction of the book is known from the beginning. The book
\r
119 will have ended up in anywhere, but quite far in any case. A few or ten
\r
120 kilometers probably.
\r
122 The book might just lie on the ground but usually some evil castle lord
\r
123 will have hidden it in his castle. There are fairly many peaceful people
\r
124 in the lowlands, and the missioners can talk to them. They will sometimes
\r
125 tell some rumours of the book. "The Important Book? One guy going by told
\r
126 me it was seen falling from the highlands, probably towards the town of
\r
127 Clystaleon." (eh, just a random name I just made up)
\r
129 The map would of course be generated randomly, with some small towns and
\r
130 roads. Roads will occasionally have some road signs.
\r
132 But of course there will be monsters, especially at night. At the location
\r
133 of the book, there will usually be very little chance to survive without
\r
134 diamond armor and weapons, at least when the book has been stored by the
\r
135 evil castle lord. Useful rare minerals and items will be found only in
\r
136 areas of dangerous monsters.
\r
138 There is a blue glow coming from the book, which is a blue circle with a
\r
139 radius of 30m, and which is seen at a distance of 50 to 100m.
\r
141 When found, the book has to be transported back to beneath the sky city.
\r
146 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
147 - The player could go faster by a crafting a boat, or riding an animal
\r
148 - Random NPC traders. what else?
\r
153 - When furnace is destroyed, move items to player's inventory
\r
154 - Add lots of stuff
\r
156 - Growing grass, decaying leaves
\r
157 - This can be done in the active blocks I guess.
\r
158 - Lots of stuff can be done in the active blocks.
\r
159 - Uh, is there an active block list somewhere? I think not. Add it.
\r
160 - Breaking weak structures
\r
161 - This can probably be accomplished in the same way as grass
\r
162 - Player health points
\r
163 - When player dies, throw items on map (needs better item-on-map
\r
165 - Cobble to get mossy if near water
\r
166 - More slots in furnace source list, so that multiple ingredients
\r
170 - The Treasure Guard; a big monster with a hammer
\r
171 - The hammer does great damage, shakes the ground and removes a block
\r
172 - You can drop on top of it, and have some time to attack there
\r
173 before he shakes you off
\r
175 - Maybe the difficulty could come from monsters getting tougher in
\r
176 far-away places, and the player starting to need something from
\r
177 there when time goes by.
\r
178 - The player would have some of that stuff at the beginning, and
\r
179 would need new supplies of it when it runs out
\r
182 - A spread-items-on-map routine for the bomb, and for dying players
\r
185 - Proper sword swing simulation
\r
186 - Player should get damage from colliding to a wall at high speed
\r
191 Build system / running:
\r
192 -----------------------
\r
194 Networking and serialization:
\r
195 -----------------------------
\r
197 TODO: Get rid of GotSplitPacketException
\r
202 TODO: Configuration menu, at least for keys
\r
207 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
209 - That is >500 vertices
\r
210 - This is not easy; all the MapBlocks close to the player would
\r
211 still need to be drawn separately and combining the blocks
\r
212 would have to happen in a background thread
\r
214 SUGG: Make fetching sector's blocks more efficient when rendering
\r
215 sectors that have very large amounts of blocks (on client)
\r
216 - Is this necessary at all?
\r
218 TODO: Flowing water animation
\r
220 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
221 animating them is easier.
\r
223 SUGG: Option for enabling proper alpha channel for textures
\r
224 TODO: A setting for enabling bilinear filtering for textures
\r
226 TODO: Better control of draw_control.wanted_max_blocks
\r
228 TODO: Get player texture (and some others) from the specified texture
\r
231 SUGG: Simple light color information to air
\r
233 TODO: Block mesh generator to tile properly on smooth lighting
\r
241 TODO: Untie client network operations from framerate
\r
242 - Needs some input queues or something
\r
243 - This won't give much performance boost because calculating block
\r
244 meshes takes so long
\r
246 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
248 TODO: Don't update all meshes always on single node changes, but
\r
249 check which ones should be updated
\r
250 - implement Map::updateNodeMeshes() and the usage of it
\r
251 - It will give almost always a 4x boost in mesh update performance.
\r
255 - Tool/weapon visualization
\r
257 FIXME: When disconnected to the menu, memory is not freed properly
\r
262 SUGG: Make an option to the server to disable building and digging near
\r
263 the starting position
\r
265 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
267 * Fix the problem with the server constantly saving one or a few
\r
268 blocks? List the first saved block, maybe it explains.
\r
269 - It is probably caused by oscillating water
\r
270 * Make a small history check to transformLiquids to detect and log
\r
271 continuous oscillations, in such detail that they can be fixed.
\r
276 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
278 SUGG: MovingObject::move and Player::move are basically the same.
\r
280 - NOTE: Player::move is more up-to-date.
\r
281 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
283 SUGG: Server-side objects could be moved based on nodes to enable very
\r
284 lightweight operation and simple AI
\r
289 TODO: Mineral and ground material properties
\r
290 - This way mineral ground toughness can be calculated with just
\r
291 some formula, as well as tool strengths
\r
293 TODO: Flowing water to actually contain flow direction information
\r
294 - There is a space for this - it just has to be implemented.
\r
296 SUGG: Erosion simulation at map generation time
\r
297 - Simulate water flows, which would carve out dirt fast and
\r
298 then turn stone into gravel and sand and relocate it.
\r
299 - How about relocating minerals, too? Coal and gold in
\r
300 downstream sand and gravel would be kind of cool
\r
301 - This would need a better way of handling minerals, mainly
\r
302 to have mineral content as a separate field. the first
\r
303 parameter field is free for this.
\r
304 - Simulate rock falling from cliffs when water has removed
\r
305 enough solid rock from the bottom
\r
307 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
308 of low-res 3d noise and interpolate linearly.
\r
311 * Possibly add some kind of erosion and other stuff
\r
312 * Better water generation (spread it to underwater caverns but don't
\r
313 fill dungeons that don't touch big water masses)
\r
314 * When generating a chunk and the neighboring chunk doesn't have mud
\r
315 and stuff yet and the ground is fairly flat, the mud will flow to
\r
316 the other chunk making nasty straight walls when the other chunk
\r
317 is generated. Fix it. Maybe just a special case if the ground is
\r
322 * Make an "environment metafile" to store at least time of day
\r
323 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
324 - Or maybe move content_features to material.{h,cpp}?
\r
326 Make a system for pregenerating quick information for mapblocks, so
\r
327 that the client can show them as cubes before they are actually sent
\r
330 Making it more portable:
\r
331 ------------------------
\r
332 * Some MSVC: std::sto* are defined without a namespace and collide
\r
333 with the ones in utility.h
\r
335 ======================================================================
\r
341 #pragma message ("Disabling unit tests")
\r
343 #warning "Disabling unit tests"
\r
345 // Disable unit tests
\r
346 #define ENABLE_TESTS 0
\r
348 // Enable unit tests
\r
349 #define ENABLE_TESTS 1
\r
353 #pragma comment(lib, "Irrlicht.lib")
\r
354 //#pragma comment(lib, "jthread.lib")
\r
355 #pragma comment(lib, "zlibwapi.lib")
\r
356 #pragma comment(lib, "Shell32.lib")
\r
357 // This would get rid of the console window
\r
358 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
361 #include <iostream>
\r
363 //#include <jmutexautolock.h>
\r
364 #include <locale.h>
\r
366 #include "common_irrlicht.h"
\r
369 //#include "player.h"
\r
371 #include "server.h"
\r
372 //#include "client.h"
\r
373 #include "constants.h"
\r
374 #include "porting.h"
\r
375 #include "gettime.h"
\r
376 #include "guiMessageMenu.h"
\r
377 #include "filesys.h"
\r
378 #include "config.h"
\r
379 #include "guiMainMenu.h"
\r
380 #include "mineral.h"
\r
381 //#include "noise.h"
\r
382 //#include "tile.h"
\r
383 #include "materials.h"
\r
386 // This makes textures
\r
387 ITextureSource *g_texturesource = NULL;
\r
391 These are loaded from the config file.
\r
394 Settings g_settings;
\r
395 // This is located in defaultsettings.cpp
\r
396 extern void set_default_settings();
\r
406 gui::IGUIEnvironment* guienv = NULL;
\r
407 gui::IGUIStaticText *guiroot = NULL;
\r
409 MainMenuManager g_menumgr;
\r
411 bool noMenuActive()
\r
413 return (g_menumgr.menuCount() == 0);
\r
416 // Passed to menus to allow disconnecting and exiting
\r
418 MainGameCallback *g_gamecallback = NULL;
\r
425 std::ostream *dout_con_ptr = &dummyout;
\r
426 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
427 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
428 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
429 //std::ostream *dout_con_ptr = &dstream;
\r
430 //std::ostream *derr_con_ptr = &dstream;
\r
433 std::ostream *dout_server_ptr = &dstream;
\r
434 std::ostream *derr_server_ptr = &dstream;
\r
437 std::ostream *dout_client_ptr = &dstream;
\r
438 std::ostream *derr_client_ptr = &dstream;
\r
441 gettime.h implementation
\r
444 // A small helper class
\r
448 TimeGetter(IrrlichtDevice *device):
\r
453 if(m_device == NULL)
\r
455 return m_device->getTimer()->getRealTime();
\r
458 IrrlichtDevice *m_device;
\r
461 // A pointer to a global instance of the time getter
\r
462 TimeGetter *g_timegetter = NULL;
\r
466 if(g_timegetter == NULL)
\r
468 return g_timegetter->getTime();
\r
472 Event handler for Irrlicht
\r
474 NOTE: Everything possible should be moved out from here,
\r
475 probably to InputHandler and the_game
\r
478 class MyEventReceiver : public IEventReceiver
\r
481 // This is the one method that we have to implement
\r
482 virtual bool OnEvent(const SEvent& event)
\r
485 React to nothing here if a menu is active
\r
487 if(noMenuActive() == false)
\r
492 // Remember whether each key is down or up
\r
493 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
495 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
497 if(event.KeyInput.PressedDown)
\r
498 keyWasDown[event.KeyInput.Key] = true;
\r
501 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
503 if(noMenuActive() == false)
\r
505 left_active = false;
\r
506 middle_active = false;
\r
507 right_active = false;
\r
511 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
512 left_active = event.MouseInput.isLeftPressed();
\r
513 middle_active = event.MouseInput.isMiddlePressed();
\r
514 right_active = event.MouseInput.isRightPressed();
\r
516 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
518 leftclicked = true;
\r
520 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
522 rightclicked = true;
\r
524 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
526 leftreleased = true;
\r
528 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
530 rightreleased = true;
\r
532 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
534 mouse_wheel += event.MouseInput.Wheel;
\r
542 bool IsKeyDown(EKEY_CODE keyCode) const
\r
544 return keyIsDown[keyCode];
\r
547 // Checks whether a key was down and resets the state
\r
548 bool WasKeyDown(EKEY_CODE keyCode)
\r
550 bool b = keyWasDown[keyCode];
\r
551 keyWasDown[keyCode] = false;
\r
555 s32 getMouseWheel()
\r
557 s32 a = mouse_wheel;
\r
564 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
566 keyIsDown[i] = false;
\r
567 keyWasDown[i] = false;
\r
570 leftclicked = false;
\r
571 rightclicked = false;
\r
572 leftreleased = false;
\r
573 rightreleased = false;
\r
575 left_active = false;
\r
576 middle_active = false;
\r
577 right_active = false;
\r
590 bool rightreleased;
\r
593 bool middle_active;
\r
599 IrrlichtDevice *m_device;
\r
601 // The current state of keys
\r
602 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
603 // Whether a key has been pressed or not
\r
604 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
608 Separated input handler
\r
611 class RealInputHandler : public InputHandler
\r
614 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
616 m_receiver(receiver)
\r
619 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
621 return m_receiver->IsKeyDown(keyCode);
\r
623 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
625 return m_receiver->WasKeyDown(keyCode);
\r
627 virtual v2s32 getMousePos()
\r
629 return m_device->getCursorControl()->getPosition();
\r
631 virtual void setMousePos(s32 x, s32 y)
\r
633 m_device->getCursorControl()->setPosition(x, y);
\r
636 virtual bool getLeftState()
\r
638 return m_receiver->left_active;
\r
640 virtual bool getRightState()
\r
642 return m_receiver->right_active;
\r
645 virtual bool getLeftClicked()
\r
647 return m_receiver->leftclicked;
\r
649 virtual bool getRightClicked()
\r
651 return m_receiver->rightclicked;
\r
653 virtual void resetLeftClicked()
\r
655 m_receiver->leftclicked = false;
\r
657 virtual void resetRightClicked()
\r
659 m_receiver->rightclicked = false;
\r
662 virtual bool getLeftReleased()
\r
664 return m_receiver->leftreleased;
\r
666 virtual bool getRightReleased()
\r
668 return m_receiver->rightreleased;
\r
670 virtual void resetLeftReleased()
\r
672 m_receiver->leftreleased = false;
\r
674 virtual void resetRightReleased()
\r
676 m_receiver->rightreleased = false;
\r
679 virtual s32 getMouseWheel()
\r
681 return m_receiver->getMouseWheel();
\r
686 m_receiver->clearInput();
\r
689 IrrlichtDevice *m_device;
\r
690 MyEventReceiver *m_receiver;
\r
693 class RandomInputHandler : public InputHandler
\r
696 RandomInputHandler()
\r
700 leftclicked = false;
\r
701 rightclicked = false;
\r
702 leftreleased = false;
\r
703 rightreleased = false;
\r
704 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
705 keydown[i] = false;
\r
707 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
709 return keydown[keyCode];
\r
711 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
715 virtual v2s32 getMousePos()
\r
719 virtual void setMousePos(s32 x, s32 y)
\r
721 mousepos = v2s32(x,y);
\r
724 virtual bool getLeftState()
\r
728 virtual bool getRightState()
\r
733 virtual bool getLeftClicked()
\r
735 return leftclicked;
\r
737 virtual bool getRightClicked()
\r
739 return rightclicked;
\r
741 virtual void resetLeftClicked()
\r
743 leftclicked = false;
\r
745 virtual void resetRightClicked()
\r
747 rightclicked = false;
\r
750 virtual bool getLeftReleased()
\r
752 return leftreleased;
\r
754 virtual bool getRightReleased()
\r
756 return rightreleased;
\r
758 virtual void resetLeftReleased()
\r
760 leftreleased = false;
\r
762 virtual void resetRightReleased()
\r
764 rightreleased = false;
\r
767 virtual s32 getMouseWheel()
\r
772 virtual void step(float dtime)
\r
775 static float counter1 = 0;
\r
779 counter1 = 0.1*Rand(1, 40);
\r
780 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
784 static float counter1 = 0;
\r
788 counter1 = 0.1*Rand(1, 40);
\r
789 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
793 static float counter1 = 0;
\r
797 counter1 = 0.1*Rand(1, 40);
\r
798 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
802 static float counter1 = 0;
\r
806 counter1 = 0.1*Rand(1, 40);
\r
807 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
811 static float counter1 = 0;
\r
815 counter1 = 0.1*Rand(1, 20);
\r
816 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
820 static float counter1 = 0;
\r
824 counter1 = 0.1*Rand(1, 30);
\r
825 leftdown = !leftdown;
\r
827 leftclicked = true;
\r
829 leftreleased = true;
\r
833 static float counter1 = 0;
\r
837 counter1 = 0.1*Rand(1, 15);
\r
838 rightdown = !rightdown;
\r
840 rightclicked = true;
\r
842 rightreleased = true;
\r
845 mousepos += mousespeed;
\r
848 s32 Rand(s32 min, s32 max)
\r
850 return (myrand()%(max-min+1))+min;
\r
853 bool keydown[KEY_KEY_CODES_COUNT];
\r
861 bool rightreleased;
\r
864 // These are defined global so that they're not optimized too much.
\r
865 // Can't change them to volatile.
\r
870 std::string tempstring;
\r
871 std::string tempstring2;
\r
876 dstream<<"The following test should take around 20ms."<<std::endl;
\r
877 TimeTaker timer("Testing std::string speed");
\r
878 const u32 jj = 10000;
\r
879 for(u32 j=0; j<jj; j++)
\r
884 for(u32 i=0; i<ii; i++){
\r
885 tempstring2 += "asd";
\r
887 for(u32 i=0; i<ii+1; i++){
\r
888 tempstring += "asd";
\r
889 if(tempstring == tempstring2)
\r
895 dstream<<"All of the following tests should take around 100ms each."
\r
899 TimeTaker timer("Testing floating-point conversion speed");
\r
901 for(u32 i=0; i<4000000; i++){
\r
908 TimeTaker timer("Testing floating-point vector speed");
\r
910 tempv3f1 = v3f(1,2,3);
\r
911 tempv3f2 = v3f(4,5,6);
\r
912 for(u32 i=0; i<10000000; i++){
\r
913 tempf += tempv3f1.dotProduct(tempv3f2);
\r
914 tempv3f2 += v3f(7,8,9);
\r
919 TimeTaker timer("Testing core::map speed");
\r
921 core::map<v2s16, f32> map1;
\r
924 for(s16 y=0; y<ii; y++){
\r
925 for(s16 x=0; x<ii; x++){
\r
926 map1.insert(v2s16(x,y), tempf);
\r
930 for(s16 y=ii-1; y>=0; y--){
\r
931 for(s16 x=0; x<ii; x++){
\r
932 tempf = map1[v2s16(x,y)];
\r
938 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
939 TimeTaker timer("Testing mutex speed");
\r
952 // Do at least 10ms
\r
953 while(timer.getTime() < 10);
\r
955 u32 dtime = timer.stop();
\r
956 u32 per_ms = n / dtime;
\r
957 std::cout<<"Done. "<<dtime<<"ms, "
\r
958 <<per_ms<<"/ms"<<std::endl;
\r
962 void drawMenuBackground(video::IVideoDriver* driver)
\r
964 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
966 video::ITexture *bgtexture =
\r
967 driver->getTexture(porting::getDataPath("mud.png").c_str());
\r
970 s32 texturesize = 128;
\r
971 s32 tiled_y = screensize.Height / texturesize + 1;
\r
972 s32 tiled_x = screensize.Width / texturesize + 1;
\r
974 for(s32 y=0; y<tiled_y; y++)
\r
975 for(s32 x=0; x<tiled_x; x++)
\r
977 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
978 rect += v2s32(x*texturesize, y*texturesize);
\r
979 driver->draw2DImage(bgtexture, rect,
\r
980 core::rect<s32>(core::position2d<s32>(0,0),
\r
981 core::dimension2di(bgtexture->getSize())),
\r
986 video::ITexture *logotexture =
\r
987 driver->getTexture(porting::getDataPath("menulogo.png").c_str());
\r
990 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
991 logotexture->getOriginalSize().Height);
\r
994 video::SColor bgcolor(255,50,50,50);
\r
995 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
996 screensize.Width, screensize.Height);
\r
997 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
999 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
1000 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
1001 rect -= v2s32(logosize.X/2, 0);
\r
1002 driver->draw2DImage(logotexture, rect,
\r
1003 core::rect<s32>(core::position2d<s32>(0,0),
\r
1004 core::dimension2di(logotexture->getSize())),
\r
1005 NULL, NULL, true);
\r
1009 int main(int argc, char *argv[])
\r
1012 Parse command line
\r
1015 // List all allowed options
\r
1016 core::map<std::string, ValueSpec> allowed_options;
\r
1017 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1018 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1019 "Run server directly"));
\r
1020 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1021 "Load configuration from specified file"));
\r
1022 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1023 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1024 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1025 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1026 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1027 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1029 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1031 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1033 Settings cmd_args;
\r
1035 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1037 if(ret == false || cmd_args.getFlag("help"))
\r
1039 dstream<<"Allowed options:"<<std::endl;
\r
1040 for(core::map<std::string, ValueSpec>::Iterator
\r
1041 i = allowed_options.getIterator();
\r
1042 i.atEnd() == false; i++)
\r
1044 dstream<<" --"<<i.getNode()->getKey();
\r
1045 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1050 dstream<<" <value>";
\r
1052 dstream<<std::endl;
\r
1054 if(i.getNode()->getValue().help != NULL)
\r
1056 dstream<<" "<<i.getNode()->getValue().help
\r
1061 return cmd_args.getFlag("help") ? 0 : 1;
\r
1065 Low-level initialization
\r
1068 bool disable_stderr = false;
\r
1070 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1071 disable_stderr = true;
\r
1074 // Initialize debug streams
\r
1075 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1076 // Initialize debug stacks
\r
1077 debug_stacks_init();
\r
1079 DSTACK(__FUNCTION_NAME);
\r
1081 porting::signal_handler_init();
\r
1082 bool &kill = *porting::signal_handler_killstatus();
\r
1084 porting::initializePaths();
\r
1085 // Create user data directory
\r
1086 fs::CreateDir(porting::path_userdata);
\r
1088 // C-style stuff initialization
\r
1089 initializeMaterialProperties();
\r
1092 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1094 // Print startup message
\r
1095 dstream<<DTIME<<"minetest-c55"
\r
1096 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1097 <<", "<<BUILD_INFO
\r
1101 Basic initialization
\r
1104 // Initialize default settings
\r
1105 set_default_settings();
\r
1107 // Set locale. This is for forcing '.' as the decimal point.
\r
1108 std::locale::global(std::locale("C"));
\r
1109 // This enables printing all characters in bitmap font
\r
1110 setlocale(LC_CTYPE, "en_US");
\r
1112 // Initialize sockets
\r
1114 atexit(sockets_cleanup);
\r
1124 // Path of configuration file in use
\r
1125 std::string configpath = "";
\r
1127 if(cmd_args.exists("config"))
\r
1129 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1132 dstream<<"Could not read configuration from \""
\r
1133 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1136 configpath = cmd_args.get("config");
\r
1140 core::array<std::string> filenames;
\r
1141 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1142 #ifdef RUN_IN_PLACE
\r
1143 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1146 for(u32 i=0; i<filenames.size(); i++)
\r
1148 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1151 configpath = filenames[i];
\r
1156 // If no path found, use the first one (menu creates the file)
\r
1157 if(configpath == "")
\r
1158 configpath = filenames[0];
\r
1161 // Initialize random seed
\r
1166 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1168 These are needed for unit tests at least.
\r
1171 // Initial call with g_texturesource not set.
\r
1178 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1179 || cmd_args.getFlag("enable-unittests") == true)
\r
1184 /*for(s16 y=-100; y<100; y++)
\r
1185 for(s16 x=-100; x<100; x++)
\r
1187 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1197 if(cmd_args.exists("port"))
\r
1198 port = cmd_args.getU16("port");
\r
1199 else if(g_settings.exists("port"))
\r
1200 port = g_settings.getU16("port");
\r
1205 std::string map_dir = porting::path_userdata+"/map";
\r
1206 if(cmd_args.exists("map-dir"))
\r
1207 map_dir = cmd_args.get("map-dir");
\r
1208 else if(g_settings.exists("map-dir"))
\r
1209 map_dir = g_settings.get("map-dir");
\r
1211 // Run dedicated server if asked to
\r
1212 if(cmd_args.getFlag("server"))
\r
1214 DSTACK("Dedicated server branch");
\r
1217 Server server(map_dir.c_str());
\r
1218 server.start(port);
\r
1221 dedicated_server_loop(server, kill);
\r
1230 // Address to connect to
\r
1231 std::string address = "";
\r
1233 if(cmd_args.exists("address"))
\r
1235 address = cmd_args.get("address");
\r
1239 address = g_settings.get("address");
\r
1242 std::string playername = g_settings.get("name");
\r
1245 Device initialization
\r
1248 // Resolution selection
\r
1250 bool fullscreen = false;
\r
1251 u16 screenW = g_settings.getU16("screenW");
\r
1252 u16 screenH = g_settings.getU16("screenH");
\r
1254 // Determine driver
\r
1256 video::E_DRIVER_TYPE driverType;
\r
1258 std::string driverstring = g_settings.get("video_driver");
\r
1260 if(driverstring == "null")
\r
1261 driverType = video::EDT_NULL;
\r
1262 else if(driverstring == "software")
\r
1263 driverType = video::EDT_SOFTWARE;
\r
1264 else if(driverstring == "burningsvideo")
\r
1265 driverType = video::EDT_BURNINGSVIDEO;
\r
1266 else if(driverstring == "direct3d8")
\r
1267 driverType = video::EDT_DIRECT3D8;
\r
1268 else if(driverstring == "direct3d9")
\r
1269 driverType = video::EDT_DIRECT3D9;
\r
1270 else if(driverstring == "opengl")
\r
1271 driverType = video::EDT_OPENGL;
\r
1274 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1275 "to opengl"<<std::endl;
\r
1276 driverType = video::EDT_OPENGL;
\r
1280 Create device and exit if creation failed
\r
1283 MyEventReceiver receiver;
\r
1285 IrrlichtDevice *device;
\r
1286 device = createDevice(driverType,
\r
1287 core::dimension2d<u32>(screenW, screenH),
\r
1288 16, fullscreen, false, false, &receiver);
\r
1291 return 1; // could not create selected driver.
\r
1293 // Set device in game parameters
\r
1296 // Create time getter
\r
1297 g_timegetter = new TimeGetter(device);
\r
1299 // Create game callback for menus
\r
1300 g_gamecallback = new MainGameCallback(device);
\r
1302 // Create texture source
\r
1303 g_texturesource = new TextureSource(device);
\r
1306 Speed tests (done after irrlicht is loaded to get timer)
\r
1308 if(cmd_args.getFlag("speedtests"))
\r
1310 dstream<<"Running speed tests"<<std::endl;
\r
1315 device->setResizable(true);
\r
1317 bool random_input = g_settings.getBool("random_input")
\r
1318 || cmd_args.getFlag("random-input");
\r
1319 InputHandler *input = NULL;
\r
1321 input = new RandomInputHandler();
\r
1323 input = new RealInputHandler(device, &receiver);
\r
1326 Continue initialization
\r
1329 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1332 This changes the minimum allowed number of vertices in a VBO.
\r
1335 //driver->setMinHardwareBufferVertexCount(50);
\r
1337 scene::ISceneManager* smgr = device->getSceneManager();
\r
1339 guienv = device->getGUIEnvironment();
\r
1340 gui::IGUISkin* skin = guienv->getSkin();
\r
1341 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1343 skin->setFont(font);
\r
1345 dstream<<"WARNING: Font file was not found."
\r
1346 " Using default font."<<std::endl;
\r
1347 // If font was not found, this will get us one
\r
1348 font = skin->getFont();
\r
1351 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1352 dstream<<"text_height="<<text_height<<std::endl;
\r
1354 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1355 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1356 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1357 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1358 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1359 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1362 Preload some textures and stuff
\r
1365 init_content_inventory_texture_paths();
\r
1366 init_mapnode(); // Second call with g_texturesource set
\r
1374 If an error occurs, this is set to something and the
\r
1375 menu-game loop is restarted. It is then displayed before
\r
1378 std::wstring error_message = L"";
\r
1383 while(device->run() && kill == false)
\r
1385 // This is used for catching disconnects
\r
1390 Clear everything from the GUIEnvironment
\r
1395 We need some kind of a root node to be able to add
\r
1396 custom gui elements directly on the screen.
\r
1397 Otherwise they won't be automatically drawn.
\r
1399 guiroot = guienv->addStaticText(L"",
\r
1400 core::rect<s32>(0, 0, 10000, 10000));
\r
1403 Out-of-game menu loop.
\r
1405 Loop quits when menu returns proper parameters.
\r
1407 while(kill == false)
\r
1409 // Cursor can be non-visible when coming from the game
\r
1410 device->getCursorControl()->setVisible(true);
\r
1411 // Some stuff are left to scene manager when coming from the game
\r
1412 // (map at least?)
\r
1414 // Reset or hide the debug gui texts
\r
1415 /*guitext->setText(L"Minetest-c55");
\r
1416 guitext2->setVisible(false);
\r
1417 guitext_info->setVisible(false);
\r
1418 guitext_chat->setVisible(false);*/
\r
1420 // Initialize menu data
\r
1421 MainMenuData menudata;
\r
1422 menudata.address = narrow_to_wide(address);
\r
1423 menudata.name = narrow_to_wide(playername);
\r
1424 menudata.port = narrow_to_wide(itos(port));
\r
1425 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1426 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1427 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1428 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1430 GUIMainMenu *menu =
\r
1431 new GUIMainMenu(guienv, guiroot, -1,
\r
1432 &g_menumgr, &menudata, g_gamecallback);
\r
1433 menu->allowFocusRemoval(true);
\r
1435 if(error_message != L"")
\r
1437 dstream<<"WARNING: error_message = "
\r
1438 <<wide_to_narrow(error_message)<<std::endl;
\r
1440 GUIMessageMenu *menu2 =
\r
1441 new GUIMessageMenu(guienv, guiroot, -1,
\r
1442 &g_menumgr, error_message.c_str());
\r
1444 error_message = L"";
\r
1447 video::IVideoDriver* driver = device->getVideoDriver();
\r
1449 dstream<<"Created main menu"<<std::endl;
\r
1451 while(device->run() && kill == false)
\r
1453 if(menu->getStatus() == true)
\r
1456 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1457 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1459 drawMenuBackground(driver);
\r
1461 guienv->drawAll();
\r
1463 driver->endScene();
\r
1466 // Break out of menu-game loop to shut down cleanly
\r
1467 if(device->run() == false || kill == true)
\r
1470 dstream<<"Dropping main menu"<<std::endl;
\r
1474 // Delete map if requested
\r
1475 if(menudata.delete_map)
\r
1477 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1479 error_message = L"Delete failed";
\r
1483 playername = wide_to_narrow(menudata.name);
\r
1484 address = wide_to_narrow(menudata.address);
\r
1485 int newport = stoi(wide_to_narrow(menudata.port));
\r
1488 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1489 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1490 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1491 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1493 // Check for valid parameters, restart menu if invalid.
\r
1494 if(playername == "")
\r
1496 error_message = L"Name required.";
\r
1501 g_settings.set("name", playername);
\r
1502 g_settings.set("address", address);
\r
1503 g_settings.set("port", itos(port));
\r
1504 // Update configuration file
\r
1505 if(configpath != "")
\r
1506 g_settings.updateConfigFile(configpath.c_str());
\r
1508 // Continue to game
\r
1512 // Break out of menu-game loop to shut down cleanly
\r
1513 if(device->run() == false)
\r
1516 // Initialize mapnode again to enable changed graphics settings
\r
1536 catch(con::PeerNotFoundException &e)
\r
1538 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1539 error_message = L"Connection error (timed out?)";
\r
1541 catch(SocketException &e)
\r
1543 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1544 error_message = L"Socket error (port already in use?)";
\r
1547 catch(std::exception &e)
\r
1549 std::string narrow_message = "Some exception, what()=\"";
\r
1550 narrow_message += e.what();
\r
1551 narrow_message += "\"";
\r
1552 dstream<<DTIME<<narrow_message<<std::endl;
\r
1553 error_message = narrow_to_wide(narrow_message);
\r
1557 } // Menu-game loop
\r
1562 In the end, delete the Irrlicht device.
\r
1566 END_DEBUG_EXCEPTION_HANDLER
\r
1568 debugstreams_deinit();
\r