3 Copyright (C) 2010-2011 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 Old, wild and random suggestions that probably won't be done:
\r
31 -------------------------------------------------------------
\r
33 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
35 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
36 - This enables saving many packets and making a faster connection
\r
37 - This also enables server to check if client has received the
\r
38 most recent block sent, for example.
\r
39 SUGG: Add a sane bandwidth throttling system to Connection
\r
41 SUGG: More fine-grained control of client's dumping of blocks from
\r
43 - ...What does this mean in the first place?
\r
45 SUGG: A map editing mode (similar to dedicated server mode)
\r
47 SUGG: Transfer more blocks in a single packet
\r
48 SUGG: A blockdata combiner class, to which blocks are added and at
\r
49 destruction it sends all the stuff in as few packets as possible.
\r
50 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
51 it by sending more stuff in a single packet.
\r
52 - Add a packet queue to RemoteClient, from which packets will be
\r
53 combined with object data packets
\r
54 - This is not exactly trivial: the object data packets are
\r
55 sometimes very big by themselves
\r
56 - This might not give much network performance gain though.
\r
58 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
59 - This is not doable because it is currently hand-made and not
\r
60 based on some mathematical function.
\r
61 - Note: This has been changing lately
\r
63 SUGG: A version number to blocks, which increments when the block is
\r
64 modified (node add/remove, water update, lighting update)
\r
65 - This can then be used to make sure the most recent version of
\r
66 a block has been sent to client, for example
\r
68 SUGG: Make the amount of blocks sending to client and the total
\r
69 amount of blocks dynamically limited. Transferring blocks is the
\r
70 main network eater of this system, so it is the one that has
\r
71 to be throttled so that RTTs stay low.
\r
73 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
74 different directions and then only those drawn that need to be
\r
76 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
79 SUGG: Background music based on cellular automata?
\r
80 http://www.earslap.com/projectslab/otomata
\r
82 SUGG: Simple light color information to air
\r
84 SUGG: Server-side objects could be moved based on nodes to enable very
\r
85 lightweight operation and simple AI
\r
86 - Not practical; client would still need to show smooth movement.
\r
88 SUGG: Make a system for pregenerating quick information for mapblocks, so
\r
89 that the client can show them as cubes before they are actually sent
\r
95 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
96 - The player could go faster by a crafting a boat, or riding an animal
\r
97 - Random NPC traders. what else?
\r
102 - When furnace is destroyed, move items to player's inventory
\r
103 - Add lots of stuff
\r
105 - Growing grass, decaying leaves
\r
106 - This can be done in the active blocks I guess.
\r
107 - Lots of stuff can be done in the active blocks.
\r
108 - Uh, is there an active block list somewhere? I think not. Add it.
\r
109 - Breaking weak structures
\r
110 - This can probably be accomplished in the same way as grass
\r
111 - Player health points
\r
112 - When player dies, throw items on map (needs better item-on-map
\r
114 - Cobble to get mossy if near water
\r
115 - More slots in furnace source list, so that multiple ingredients
\r
119 - The Treasure Guard; a big monster with a hammer
\r
120 - The hammer does great damage, shakes the ground and removes a block
\r
121 - You can drop on top of it, and have some time to attack there
\r
122 before he shakes you off
\r
124 - Maybe the difficulty could come from monsters getting tougher in
\r
125 far-away places, and the player starting to need something from
\r
126 there when time goes by.
\r
127 - The player would have some of that stuff at the beginning, and
\r
128 would need new supplies of it when it runs out
\r
131 - A spread-items-on-map routine for the bomb, and for dying players
\r
134 - Proper sword swing simulation
\r
135 - Player should get damage from colliding to a wall at high speed
\r
140 Build system / running:
\r
141 -----------------------
\r
143 Networking and serialization:
\r
144 -----------------------------
\r
146 SUGG: Fix address to be ipv6 compatible
\r
154 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
156 - That is >500 vertices
\r
157 - This is not easy; all the MapBlocks close to the player would
\r
158 still need to be drawn separately and combining the blocks
\r
159 would have to happen in a background thread
\r
161 SUGG: Make fetching sector's blocks more efficient when rendering
\r
162 sectors that have very large amounts of blocks (on client)
\r
163 - Is this necessary at all?
\r
165 TODO: Flowing water animation
\r
167 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
168 animating them is easier.
\r
170 SUGG: Option for enabling proper alpha channel for textures
\r
171 TODO: A setting for enabling bilinear filtering for textures
\r
173 TODO: Better control of draw_control.wanted_max_blocks
\r
175 TODO: Block mesh generator to tile properly on smooth lighting
\r
177 TODO: Further investigate the use of GPU lighting in addition to the
\r
180 TODO: Quick drawing of huge distances according to heightmap has to be
\r
189 TODO: Untie client network operations from framerate
\r
190 - Needs some input queues or something
\r
191 - This won't give much performance boost because calculating block
\r
192 meshes takes so long
\r
194 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
196 TODO: Don't update all meshes always on single node changes, but
\r
197 check which ones should be updated
\r
198 - implement Map::updateNodeMeshes() and the usage of it
\r
199 - It will give almost always a 4x boost in mesh update performance.
\r
203 - Tool/weapon visualization
\r
205 FIXME: When disconnected to the menu, memory is not freed properly
\r
210 SUGG: Make an option to the server to disable building and digging near
\r
211 the starting position
\r
213 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
215 * Fix the problem with the server constantly saving one or a few
\r
216 blocks? List the first saved block, maybe it explains.
\r
217 - It is probably caused by oscillating water
\r
218 * Make a small history check to transformLiquids to detect and log
\r
219 continuous oscillations, in such detail that they can be fixed.
\r
221 FIXME: The new optimized map sending doesn't sometimes send enough blocks
\r
222 from big caves and such
\r
224 * Take player's walking direction into account in GetNextBlocks
\r
226 TODO: Map saving should be done by EmergeThread
\r
228 SUGG: Map unloading based on sector reference is not very good, it keeps
\r
229 unnecessary stuff in memory. I guess. Investigate this.
\r
231 TODO: FIXME: Make furnaces handle long step() times better; now a 10-day
\r
232 dtime for a bunch of furnaces will take ages
\r
234 TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set
\r
235 the direction accordingly.
\r
240 TODO: A list of "active blocks" in which stuff happens.
\r
241 + Add a never-resetted game timer to the server
\r
242 + Add a timestamp value to blocks
\r
243 + The simple rule: All blocks near some player are "active"
\r
244 - Do stuff in real time in active blocks
\r
246 TODO: Make proper hooks in here
\r
247 - Grow grass, delete leaves without a tree
\r
248 - Spawn some mobs based on some rules
\r
249 - Transform cobble to mossy cobble near water
\r
250 - Run a custom script
\r
251 - ...And all kinds of other dynamic stuff
\r
252 + Keep track of when a block becomes active and becomes inactive
\r
253 + When a block goes inactive:
\r
254 + Store objects statically to block
\r
255 + Store timer value as the timestamp
\r
256 + When a block goes active:
\r
257 + Create active objects out of static objects
\r
258 TODO: Make proper hooks in here
\r
259 - Simulate the results of what would have happened if it would have
\r
260 been active for all the time
\r
261 - Grow a lot of grass and so on
\r
262 + Initially it is fine to send information about every active object
\r
263 to every player. Eventually it should be modified to only send info
\r
264 about the nearest ones.
\r
265 + This was left to be done by the old system and it sends only the
\r
271 TODO: Get rid of MapBlockObjects and use only ActiveObjects
\r
272 - Skipping the MapBlockObject data is nasty - there is no "total
\r
273 length" stored; have to make a SkipMBOs function which contains
\r
274 enough of the current code to skip them properly.
\r
276 SUGG: MovingObject::move and Player::move are basically the same.
\r
278 - NOTE: Player::move is more up-to-date.
\r
279 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
280 - NOTE: MovingObject will be deleted (MapBlockObject)
\r
282 TODO: Add a long step function to objects that is called with the time
\r
283 difference when block activates
\r
288 TODO: Mineral and ground material properties
\r
289 - This way mineral ground toughness can be calculated with just
\r
290 some formula, as well as tool strengths
\r
292 TODO: Flowing water to actually contain flow direction information
\r
293 - There is a space for this - it just has to be implemented.
\r
295 SUGG: Erosion simulation at map generation time
\r
296 - Simulate water flows, which would carve out dirt fast and
\r
297 then turn stone into gravel and sand and relocate it.
\r
298 - How about relocating minerals, too? Coal and gold in
\r
299 downstream sand and gravel would be kind of cool
\r
300 - This would need a better way of handling minerals, mainly
\r
301 to have mineral content as a separate field. the first
\r
302 parameter field is free for this.
\r
303 - Simulate rock falling from cliffs when water has removed
\r
304 enough solid rock from the bottom
\r
306 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
307 of low-res 3d noise and interpolate linearly.
\r
309 Mapgen v2 (the current one):
\r
310 * Possibly add some kind of erosion and other stuff
\r
311 * Better water generation (spread it to underwater caverns but don't
\r
312 fill dungeons that don't touch big water masses)
\r
313 * When generating a chunk and the neighboring chunk doesn't have mud
\r
314 and stuff yet and the ground is fairly flat, the mud will flow to
\r
315 the other chunk making nasty straight walls when the other chunk
\r
316 is generated. Fix it. Maybe just a special case if the ground is
\r
318 * Consider not updating this one and make a good mainly block-based
\r
321 SUGG: Make two "modified states", one that forces the block to be saved at
\r
322 the next save event, and one that makes the block to be saved at exit
\r
325 TODO: Add a not_fully_generated flag to MapBlock, which would be set for
\r
326 blocks that contain eg. trees from neighboring generations but haven't
\r
327 been generated itself. This is required for the future generator.
\r
331 * Move digging property stuff from material.{h,cpp} to mapnode.cpp
\r
332 - ...Or maybe move content_features to material.{h,cpp}?
\r
334 Making it more portable:
\r
335 ------------------------
\r
337 Stuff to do before release:
\r
338 ---------------------------
\r
340 Fixes to the current release:
\r
341 -----------------------------
\r
343 Stuff to do after release:
\r
344 ---------------------------
\r
345 - Make sure server handles removing grass when a block is placed (etc)
\r
346 - The client should not do it by itself
\r
347 - Block cube placement around player's head
\r
348 - Protocol version field
\r
349 - Consider getting some textures from cisoun's texture pack
\r
351 - Make sure the fence implementation and data format is good
\r
352 - Think about using same bits for material for fences and doors, for
\r
354 - Finish the ActiveBlockModifier stuff and use it for something
\r
355 - Move mineral to param2, increment map serialization version, add conversion
\r
357 ======================================================================
\r
363 #pragma message ("Disabling unit tests")
\r
365 #warning "Disabling unit tests"
\r
367 // Disable unit tests
\r
368 #define ENABLE_TESTS 0
\r
370 // Enable unit tests
\r
371 #define ENABLE_TESTS 1
\r
375 #pragma comment(lib, "Irrlicht.lib")
\r
376 //#pragma comment(lib, "jthread.lib")
\r
377 #pragma comment(lib, "zlibwapi.lib")
\r
378 #pragma comment(lib, "Shell32.lib")
\r
379 // This would get rid of the console window
\r
380 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
383 #include <iostream>
\r
385 //#include <jmutexautolock.h>
\r
386 #include <locale.h>
\r
388 #include "common_irrlicht.h"
\r
391 //#include "player.h"
\r
393 #include "server.h"
\r
394 //#include "client.h"
\r
395 #include "constants.h"
\r
396 #include "porting.h"
\r
397 #include "gettime.h"
\r
398 #include "guiMessageMenu.h"
\r
399 #include "filesys.h"
\r
400 #include "config.h"
\r
401 #include "guiMainMenu.h"
\r
402 #include "mineral.h"
\r
403 //#include "noise.h"
\r
404 //#include "tile.h"
\r
405 #include "materials.h"
\r
407 #include "keycode.h"
\r
409 // This makes textures
\r
410 ITextureSource *g_texturesource = NULL;
\r
414 These are loaded from the config file.
\r
417 Settings g_settings;
\r
418 // This is located in defaultsettings.cpp
\r
419 extern void set_default_settings();
\r
422 Profiler g_profiler;
\r
432 gui::IGUIEnvironment* guienv = NULL;
\r
433 gui::IGUIStaticText *guiroot = NULL;
\r
435 MainMenuManager g_menumgr;
\r
437 bool noMenuActive()
\r
439 return (g_menumgr.menuCount() == 0);
\r
442 // Passed to menus to allow disconnecting and exiting
\r
444 MainGameCallback *g_gamecallback = NULL;
\r
451 std::ostream *dout_con_ptr = &dummyout;
\r
452 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
453 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
454 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
455 //std::ostream *dout_con_ptr = &dstream;
\r
456 //std::ostream *derr_con_ptr = &dstream;
\r
459 std::ostream *dout_server_ptr = &dstream;
\r
460 std::ostream *derr_server_ptr = &dstream;
\r
463 std::ostream *dout_client_ptr = &dstream;
\r
464 std::ostream *derr_client_ptr = &dstream;
\r
467 gettime.h implementation
\r
470 // A small helper class
\r
474 virtual u32 getTime() = 0;
\r
477 // A precise irrlicht one
\r
478 class IrrlichtTimeGetter: public TimeGetter
\r
481 IrrlichtTimeGetter(IrrlichtDevice *device):
\r
486 if(m_device == NULL)
\r
488 return m_device->getTimer()->getRealTime();
\r
491 IrrlichtDevice *m_device;
\r
493 // Not so precise one which works without irrlicht
\r
494 class SimpleTimeGetter: public TimeGetter
\r
499 return porting::getTimeMs();
\r
503 // A pointer to a global instance of the time getter
\r
505 TimeGetter *g_timegetter = NULL;
\r
509 if(g_timegetter == NULL)
\r
511 return g_timegetter->getTime();
\r
515 Event handler for Irrlicht
\r
517 NOTE: Everything possible should be moved out from here,
\r
518 probably to InputHandler and the_game
\r
521 class MyEventReceiver : public IEventReceiver
\r
524 // This is the one method that we have to implement
\r
525 virtual bool OnEvent(const SEvent& event)
\r
528 React to nothing here if a menu is active
\r
530 if(noMenuActive() == false)
\r
535 // Remember whether each key is down or up
\r
536 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
538 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
540 if(event.KeyInput.PressedDown)
\r
541 keyWasDown[event.KeyInput.Key] = true;
\r
544 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
546 if(noMenuActive() == false)
\r
548 left_active = false;
\r
549 middle_active = false;
\r
550 right_active = false;
\r
554 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
555 left_active = event.MouseInput.isLeftPressed();
\r
556 middle_active = event.MouseInput.isMiddlePressed();
\r
557 right_active = event.MouseInput.isRightPressed();
\r
559 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
561 leftclicked = true;
\r
563 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
565 rightclicked = true;
\r
567 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
569 leftreleased = true;
\r
571 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
573 rightreleased = true;
\r
575 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
577 mouse_wheel += event.MouseInput.Wheel;
\r
585 bool IsKeyDown(EKEY_CODE keyCode) const
\r
587 return keyIsDown[keyCode];
\r
590 // Checks whether a key was down and resets the state
\r
591 bool WasKeyDown(EKEY_CODE keyCode)
\r
593 bool b = keyWasDown[keyCode];
\r
594 keyWasDown[keyCode] = false;
\r
598 s32 getMouseWheel()
\r
600 s32 a = mouse_wheel;
\r
607 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
609 keyIsDown[i] = false;
\r
610 keyWasDown[i] = false;
\r
613 leftclicked = false;
\r
614 rightclicked = false;
\r
615 leftreleased = false;
\r
616 rightreleased = false;
\r
618 left_active = false;
\r
619 middle_active = false;
\r
620 right_active = false;
\r
633 bool rightreleased;
\r
636 bool middle_active;
\r
642 IrrlichtDevice *m_device;
\r
644 // The current state of keys
\r
645 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
646 // Whether a key has been pressed or not
\r
647 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
651 Separated input handler
\r
654 class RealInputHandler : public InputHandler
\r
657 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
659 m_receiver(receiver)
\r
662 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
664 return m_receiver->IsKeyDown(keyCode);
\r
666 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
668 return m_receiver->WasKeyDown(keyCode);
\r
670 virtual v2s32 getMousePos()
\r
672 return m_device->getCursorControl()->getPosition();
\r
674 virtual void setMousePos(s32 x, s32 y)
\r
676 m_device->getCursorControl()->setPosition(x, y);
\r
679 virtual bool getLeftState()
\r
681 return m_receiver->left_active;
\r
683 virtual bool getRightState()
\r
685 return m_receiver->right_active;
\r
688 virtual bool getLeftClicked()
\r
690 return m_receiver->leftclicked;
\r
692 virtual bool getRightClicked()
\r
694 return m_receiver->rightclicked;
\r
696 virtual void resetLeftClicked()
\r
698 m_receiver->leftclicked = false;
\r
700 virtual void resetRightClicked()
\r
702 m_receiver->rightclicked = false;
\r
705 virtual bool getLeftReleased()
\r
707 return m_receiver->leftreleased;
\r
709 virtual bool getRightReleased()
\r
711 return m_receiver->rightreleased;
\r
713 virtual void resetLeftReleased()
\r
715 m_receiver->leftreleased = false;
\r
717 virtual void resetRightReleased()
\r
719 m_receiver->rightreleased = false;
\r
722 virtual s32 getMouseWheel()
\r
724 return m_receiver->getMouseWheel();
\r
729 m_receiver->clearInput();
\r
732 IrrlichtDevice *m_device;
\r
733 MyEventReceiver *m_receiver;
\r
736 class RandomInputHandler : public InputHandler
\r
739 RandomInputHandler()
\r
743 leftclicked = false;
\r
744 rightclicked = false;
\r
745 leftreleased = false;
\r
746 rightreleased = false;
\r
747 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
748 keydown[i] = false;
\r
750 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
752 return keydown[keyCode];
\r
754 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
758 virtual v2s32 getMousePos()
\r
762 virtual void setMousePos(s32 x, s32 y)
\r
764 mousepos = v2s32(x,y);
\r
767 virtual bool getLeftState()
\r
771 virtual bool getRightState()
\r
776 virtual bool getLeftClicked()
\r
778 return leftclicked;
\r
780 virtual bool getRightClicked()
\r
782 return rightclicked;
\r
784 virtual void resetLeftClicked()
\r
786 leftclicked = false;
\r
788 virtual void resetRightClicked()
\r
790 rightclicked = false;
\r
793 virtual bool getLeftReleased()
\r
795 return leftreleased;
\r
797 virtual bool getRightReleased()
\r
799 return rightreleased;
\r
801 virtual void resetLeftReleased()
\r
803 leftreleased = false;
\r
805 virtual void resetRightReleased()
\r
807 rightreleased = false;
\r
810 virtual s32 getMouseWheel()
\r
815 virtual void step(float dtime)
\r
818 static float counter1 = 0;
\r
822 counter1 = 0.1*Rand(1, 40);
\r
823 keydown[getKeySetting("keymap_jump")] =
\r
824 !keydown[getKeySetting("keymap_jump")];
\r
828 static float counter1 = 0;
\r
832 counter1 = 0.1*Rand(1, 40);
\r
833 keydown[getKeySetting("keymap_special1")] =
\r
834 !keydown[getKeySetting("keymap_special1")];
\r
838 static float counter1 = 0;
\r
842 counter1 = 0.1*Rand(1, 40);
\r
843 keydown[getKeySetting("keymap_forward")] =
\r
844 !keydown[getKeySetting("keymap_forward")];
\r
848 static float counter1 = 0;
\r
852 counter1 = 0.1*Rand(1, 40);
\r
853 keydown[getKeySetting("keymap_left")] =
\r
854 !keydown[getKeySetting("keymap_left")];
\r
858 static float counter1 = 0;
\r
862 counter1 = 0.1*Rand(1, 20);
\r
863 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
867 static float counter1 = 0;
\r
871 counter1 = 0.1*Rand(1, 30);
\r
872 leftdown = !leftdown;
\r
874 leftclicked = true;
\r
876 leftreleased = true;
\r
880 static float counter1 = 0;
\r
884 counter1 = 0.1*Rand(1, 15);
\r
885 rightdown = !rightdown;
\r
887 rightclicked = true;
\r
889 rightreleased = true;
\r
892 mousepos += mousespeed;
\r
895 s32 Rand(s32 min, s32 max)
\r
897 return (myrand()%(max-min+1))+min;
\r
900 bool keydown[KEY_KEY_CODES_COUNT];
\r
908 bool rightreleased;
\r
911 // These are defined global so that they're not optimized too much.
\r
912 // Can't change them to volatile.
\r
917 std::string tempstring;
\r
918 std::string tempstring2;
\r
923 dstream<<"The following test should take around 20ms."<<std::endl;
\r
924 TimeTaker timer("Testing std::string speed");
\r
925 const u32 jj = 10000;
\r
926 for(u32 j=0; j<jj; j++)
\r
931 for(u32 i=0; i<ii; i++){
\r
932 tempstring2 += "asd";
\r
934 for(u32 i=0; i<ii+1; i++){
\r
935 tempstring += "asd";
\r
936 if(tempstring == tempstring2)
\r
942 dstream<<"All of the following tests should take around 100ms each."
\r
946 TimeTaker timer("Testing floating-point conversion speed");
\r
948 for(u32 i=0; i<4000000; i++){
\r
955 TimeTaker timer("Testing floating-point vector speed");
\r
957 tempv3f1 = v3f(1,2,3);
\r
958 tempv3f2 = v3f(4,5,6);
\r
959 for(u32 i=0; i<10000000; i++){
\r
960 tempf += tempv3f1.dotProduct(tempv3f2);
\r
961 tempv3f2 += v3f(7,8,9);
\r
966 TimeTaker timer("Testing core::map speed");
\r
968 core::map<v2s16, f32> map1;
\r
971 for(s16 y=0; y<ii; y++){
\r
972 for(s16 x=0; x<ii; x++){
\r
973 map1.insert(v2s16(x,y), tempf);
\r
977 for(s16 y=ii-1; y>=0; y--){
\r
978 for(s16 x=0; x<ii; x++){
\r
979 tempf = map1[v2s16(x,y)];
\r
985 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
986 TimeTaker timer("Testing mutex speed");
\r
999 // Do at least 10ms
\r
1000 while(timer.getTime() < 10);
\r
1002 u32 dtime = timer.stop();
\r
1003 u32 per_ms = n / dtime;
\r
1004 std::cout<<"Done. "<<dtime<<"ms, "
\r
1005 <<per_ms<<"/ms"<<std::endl;
\r
1009 void drawMenuBackground(video::IVideoDriver* driver)
\r
1011 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
1013 video::ITexture *bgtexture =
\r
1014 driver->getTexture(getTexturePath("mud.png").c_str());
\r
1017 s32 texturesize = 128;
\r
1018 s32 tiled_y = screensize.Height / texturesize + 1;
\r
1019 s32 tiled_x = screensize.Width / texturesize + 1;
\r
1021 for(s32 y=0; y<tiled_y; y++)
\r
1022 for(s32 x=0; x<tiled_x; x++)
\r
1024 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
1025 rect += v2s32(x*texturesize, y*texturesize);
\r
1026 driver->draw2DImage(bgtexture, rect,
\r
1027 core::rect<s32>(core::position2d<s32>(0,0),
\r
1028 core::dimension2di(bgtexture->getSize())),
\r
1029 NULL, NULL, true);
\r
1033 video::ITexture *logotexture =
\r
1034 driver->getTexture(getTexturePath("menulogo.png").c_str());
\r
1037 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
1038 logotexture->getOriginalSize().Height);
\r
1041 video::SColor bgcolor(255,50,50,50);
\r
1042 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
1043 screensize.Width, screensize.Height);
\r
1044 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
1046 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
1047 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
1048 rect -= v2s32(logosize.X/2, 0);
\r
1049 driver->draw2DImage(logotexture, rect,
\r
1050 core::rect<s32>(core::position2d<s32>(0,0),
\r
1051 core::dimension2di(logotexture->getSize())),
\r
1052 NULL, NULL, true);
\r
1056 int main(int argc, char *argv[])
\r
1062 // Set locale. This is for forcing '.' as the decimal point.
\r
1063 std::locale::global(std::locale("C"));
\r
1064 // This enables printing all characters in bitmap font
\r
1065 setlocale(LC_CTYPE, "en_US");
\r
1068 Parse command line
\r
1071 // List all allowed options
\r
1072 core::map<std::string, ValueSpec> allowed_options;
\r
1073 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1074 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1075 "Run server directly"));
\r
1076 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1077 "Load configuration from specified file"));
\r
1078 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1079 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1080 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1081 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1082 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1083 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1085 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1087 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1089 Settings cmd_args;
\r
1091 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1093 if(ret == false || cmd_args.getFlag("help"))
\r
1095 dstream<<"Allowed options:"<<std::endl;
\r
1096 for(core::map<std::string, ValueSpec>::Iterator
\r
1097 i = allowed_options.getIterator();
\r
1098 i.atEnd() == false; i++)
\r
1100 dstream<<" --"<<i.getNode()->getKey();
\r
1101 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1106 dstream<<" <value>";
\r
1108 dstream<<std::endl;
\r
1110 if(i.getNode()->getValue().help != NULL)
\r
1112 dstream<<" "<<i.getNode()->getValue().help
\r
1117 return cmd_args.getFlag("help") ? 0 : 1;
\r
1121 Low-level initialization
\r
1124 bool disable_stderr = false;
\r
1126 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1127 disable_stderr = true;
\r
1130 porting::signal_handler_init();
\r
1131 bool &kill = *porting::signal_handler_killstatus();
\r
1133 // Initialize porting::path_data and porting::path_userdata
\r
1134 porting::initializePaths();
\r
1136 // Initialize debug streams
\r
1137 #ifdef RUN_IN_PLACE
\r
1138 std::string debugfile = DEBUGFILE;
\r
1140 std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
\r
1142 debugstreams_init(disable_stderr, debugfile.c_str());
\r
1143 // Initialize debug stacks
\r
1144 debug_stacks_init();
\r
1146 DSTACK(__FUNCTION_NAME);
\r
1148 // Create user data directory
\r
1149 fs::CreateDir(porting::path_userdata);
\r
1151 // Init material properties table
\r
1152 //initializeMaterialProperties();
\r
1155 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1157 // Print startup message
\r
1158 dstream<<DTIME<<"minetest-c55"
\r
1159 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1160 <<", "<<BUILD_INFO
\r
1164 Basic initialization
\r
1167 // Initialize default settings
\r
1168 set_default_settings();
\r
1170 // Initialize sockets
\r
1172 atexit(sockets_cleanup);
\r
1178 // Path of configuration file in use
\r
1179 std::string configpath = "";
\r
1181 if(cmd_args.exists("config"))
\r
1183 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1186 dstream<<"Could not read configuration from \""
\r
1187 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1190 configpath = cmd_args.get("config");
\r
1194 core::array<std::string> filenames;
\r
1195 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1196 #ifdef RUN_IN_PLACE
\r
1197 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1200 for(u32 i=0; i<filenames.size(); i++)
\r
1202 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1205 configpath = filenames[i];
\r
1210 // If no path found, use the first one (menu creates the file)
\r
1211 if(configpath == "")
\r
1212 configpath = filenames[0];
\r
1215 // Initialize random seed
\r
1220 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1222 These are needed for unit tests at least.
\r
1225 // Initial call with g_texturesource not set.
\r
1232 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1233 || cmd_args.getFlag("enable-unittests") == true)
\r
1238 /*for(s16 y=-100; y<100; y++)
\r
1239 for(s16 x=-100; x<100; x++)
\r
1241 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1251 if(cmd_args.exists("port"))
\r
1252 port = cmd_args.getU16("port");
\r
1253 else if(g_settings.exists("port"))
\r
1254 port = g_settings.getU16("port");
\r
1259 std::string map_dir = porting::path_userdata+"/world";
\r
1260 if(cmd_args.exists("map-dir"))
\r
1261 map_dir = cmd_args.get("map-dir");
\r
1262 else if(g_settings.exists("map-dir"))
\r
1263 map_dir = g_settings.get("map-dir");
\r
1265 // Run dedicated server if asked to
\r
1266 if(cmd_args.getFlag("server"))
\r
1268 DSTACK("Dedicated server branch");
\r
1270 // Create time getter
\r
1271 g_timegetter = new SimpleTimeGetter();
\r
1274 Server server(map_dir.c_str());
\r
1275 server.start(port);
\r
1278 dedicated_server_loop(server, kill);
\r
1288 // Address to connect to
\r
1289 std::string address = "";
\r
1291 if(cmd_args.exists("address"))
\r
1293 address = cmd_args.get("address");
\r
1297 address = g_settings.get("address");
\r
1300 std::string playername = g_settings.get("name");
\r
1303 Device initialization
\r
1306 // Resolution selection
\r
1308 bool fullscreen = false;
\r
1309 u16 screenW = g_settings.getU16("screenW");
\r
1310 u16 screenH = g_settings.getU16("screenH");
\r
1312 // Determine driver
\r
1314 video::E_DRIVER_TYPE driverType;
\r
1316 std::string driverstring = g_settings.get("video_driver");
\r
1318 if(driverstring == "null")
\r
1319 driverType = video::EDT_NULL;
\r
1320 else if(driverstring == "software")
\r
1321 driverType = video::EDT_SOFTWARE;
\r
1322 else if(driverstring == "burningsvideo")
\r
1323 driverType = video::EDT_BURNINGSVIDEO;
\r
1324 else if(driverstring == "direct3d8")
\r
1325 driverType = video::EDT_DIRECT3D8;
\r
1326 else if(driverstring == "direct3d9")
\r
1327 driverType = video::EDT_DIRECT3D9;
\r
1328 else if(driverstring == "opengl")
\r
1329 driverType = video::EDT_OPENGL;
\r
1332 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1333 "to opengl"<<std::endl;
\r
1334 driverType = video::EDT_OPENGL;
\r
1338 Create device and exit if creation failed
\r
1341 MyEventReceiver receiver;
\r
1343 IrrlichtDevice *device;
\r
1344 device = createDevice(driverType,
\r
1345 core::dimension2d<u32>(screenW, screenH),
\r
1346 16, fullscreen, false, false, &receiver);
\r
1349 return 1; // could not create selected driver.
\r
1351 // Set device in game parameters
\r
1354 // Create time getter
\r
1355 g_timegetter = new IrrlichtTimeGetter(device);
\r
1357 // Create game callback for menus
\r
1358 g_gamecallback = new MainGameCallback(device);
\r
1360 // Create texture source
\r
1361 g_texturesource = new TextureSource(device);
\r
1364 Speed tests (done after irrlicht is loaded to get timer)
\r
1366 if(cmd_args.getFlag("speedtests"))
\r
1368 dstream<<"Running speed tests"<<std::endl;
\r
1373 device->setResizable(true);
\r
1375 bool random_input = g_settings.getBool("random_input")
\r
1376 || cmd_args.getFlag("random-input");
\r
1377 InputHandler *input = NULL;
\r
1379 input = new RandomInputHandler();
\r
1381 input = new RealInputHandler(device, &receiver);
\r
1384 Continue initialization
\r
1387 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1390 This changes the minimum allowed number of vertices in a VBO.
\r
1393 //driver->setMinHardwareBufferVertexCount(50);
\r
1395 scene::ISceneManager* smgr = device->getSceneManager();
\r
1397 guienv = device->getGUIEnvironment();
\r
1398 gui::IGUISkin* skin = guienv->getSkin();
\r
1399 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
\r
1401 skin->setFont(font);
\r
1403 dstream<<"WARNING: Font file was not found."
\r
1404 " Using default font."<<std::endl;
\r
1405 // If font was not found, this will get us one
\r
1406 font = skin->getFont();
\r
1409 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1410 dstream<<"text_height="<<text_height<<std::endl;
\r
1412 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1413 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1414 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1415 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1416 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1417 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1420 Preload some textures and stuff
\r
1423 init_mapnode(); // Second call with g_texturesource set
\r
1431 If an error occurs, this is set to something and the
\r
1432 menu-game loop is restarted. It is then displayed before
\r
1435 std::wstring error_message = L"";
\r
1437 // The password entered during the menu screen,
\r
1438 std::string password;
\r
1443 while(device->run() && kill == false)
\r
1446 // This is used for catching disconnects
\r
1451 Clear everything from the GUIEnvironment
\r
1456 We need some kind of a root node to be able to add
\r
1457 custom gui elements directly on the screen.
\r
1458 Otherwise they won't be automatically drawn.
\r
1460 guiroot = guienv->addStaticText(L"",
\r
1461 core::rect<s32>(0, 0, 10000, 10000));
\r
1464 Out-of-game menu loop.
\r
1466 Loop quits when menu returns proper parameters.
\r
1468 while(kill == false)
\r
1470 // Cursor can be non-visible when coming from the game
\r
1471 device->getCursorControl()->setVisible(true);
\r
1472 // Some stuff are left to scene manager when coming from the game
\r
1473 // (map at least?)
\r
1475 // Reset or hide the debug gui texts
\r
1476 /*guitext->setText(L"Minetest-c55");
\r
1477 guitext2->setVisible(false);
\r
1478 guitext_info->setVisible(false);
\r
1479 guitext_chat->setVisible(false);*/
\r
1481 // Initialize menu data
\r
1482 MainMenuData menudata;
\r
1483 menudata.address = narrow_to_wide(address);
\r
1484 menudata.name = narrow_to_wide(playername);
\r
1485 menudata.port = narrow_to_wide(itos(port));
\r
1486 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1487 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1488 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1489 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1491 GUIMainMenu *menu =
\r
1492 new GUIMainMenu(guienv, guiroot, -1,
\r
1493 &g_menumgr, &menudata, g_gamecallback);
\r
1494 menu->allowFocusRemoval(true);
\r
1496 if(error_message != L"")
\r
1498 dstream<<"WARNING: error_message = "
\r
1499 <<wide_to_narrow(error_message)<<std::endl;
\r
1501 GUIMessageMenu *menu2 =
\r
1502 new GUIMessageMenu(guienv, guiroot, -1,
\r
1503 &g_menumgr, error_message.c_str());
\r
1505 error_message = L"";
\r
1508 video::IVideoDriver* driver = device->getVideoDriver();
\r
1510 dstream<<"Created main menu"<<std::endl;
\r
1512 while(device->run() && kill == false)
\r
1514 if(menu->getStatus() == true)
\r
1517 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1518 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1520 drawMenuBackground(driver);
\r
1522 guienv->drawAll();
\r
1524 driver->endScene();
\r
1526 // On some computers framerate doesn't seem to be
\r
1527 // automatically limited
\r
1531 // Break out of menu-game loop to shut down cleanly
\r
1532 if(device->run() == false || kill == true)
\r
1535 dstream<<"Dropping main menu"<<std::endl;
\r
1539 // Delete map if requested
\r
1540 if(menudata.delete_map)
\r
1542 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1544 error_message = L"Delete failed";
\r
1548 playername = wide_to_narrow(menudata.name);
\r
1550 password = translatePassword(playername, menudata.password);
\r
1552 address = wide_to_narrow(menudata.address);
\r
1553 int newport = stoi(wide_to_narrow(menudata.port));
\r
1556 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1557 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1558 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1559 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1561 // NOTE: These are now checked server side; no need to do it
\r
1562 // here, so let's not do it here.
\r
1563 /*// Check for valid parameters, restart menu if invalid.
\r
1564 if(playername == "")
\r
1566 error_message = L"Name required.";
\r
1569 // Check that name has only valid chars
\r
1570 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
\r
1572 error_message = L"Characters allowed: "
\r
1573 +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
\r
1578 g_settings.set("name", playername);
\r
1579 g_settings.set("address", address);
\r
1580 g_settings.set("port", itos(port));
\r
1581 // Update configuration file
\r
1582 if(configpath != "")
\r
1583 g_settings.updateConfigFile(configpath.c_str());
\r
1585 // Continue to game
\r
1589 // Break out of menu-game loop to shut down cleanly
\r
1590 if(device->run() == false)
\r
1593 // Initialize mapnode again to enable changed graphics settings
\r
1614 catch(con::PeerNotFoundException &e)
\r
1616 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1617 error_message = L"Connection error (timed out?)";
\r
1619 catch(SocketException &e)
\r
1621 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1622 error_message = L"Socket error (port already in use?)";
\r
1625 catch(std::exception &e)
\r
1627 std::string narrow_message = "Some exception, what()=\"";
\r
1628 narrow_message += e.what();
\r
1629 narrow_message += "\"";
\r
1630 dstream<<DTIME<<narrow_message<<std::endl;
\r
1631 error_message = narrow_to_wide(narrow_message);
\r
1635 } // Menu-game loop
\r
1640 In the end, delete the Irrlicht device.
\r
1644 END_DEBUG_EXCEPTION_HANDLER
\r
1646 debugstreams_deinit();
\r