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
92 SUGG: Erosion simulation at map generation time
\r
93 - Simulate water flows, which would carve out dirt fast and
\r
94 then turn stone into gravel and sand and relocate it.
\r
95 - How about relocating minerals, too? Coal and gold in
\r
96 downstream sand and gravel would be kind of cool
\r
97 - This would need a better way of handling minerals, mainly
\r
98 to have mineral content as a separate field. the first
\r
99 parameter field is free for this.
\r
100 - Simulate rock falling from cliffs when water has removed
\r
101 enough solid rock from the bottom
\r
106 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
107 - The player could go faster by a crafting a boat, or riding an animal
\r
108 - Random NPC traders. what else?
\r
113 - When furnace is destroyed, move items to player's inventory
\r
114 - Add lots of stuff
\r
116 - Growing grass, decaying leaves
\r
117 - This can be done in the active blocks I guess.
\r
118 - Lots of stuff can be done in the active blocks.
\r
119 - Uh, is there an active block list somewhere? I think not. Add it.
\r
120 - Breaking weak structures
\r
121 - This can probably be accomplished in the same way as grass
\r
122 - Player health points
\r
123 - When player dies, throw items on map (needs better item-on-map
\r
125 - Cobble to get mossy if near water
\r
126 - More slots in furnace source list, so that multiple ingredients
\r
130 - The Treasure Guard; a big monster with a hammer
\r
131 - The hammer does great damage, shakes the ground and removes a block
\r
132 - You can drop on top of it, and have some time to attack there
\r
133 before he shakes you off
\r
135 - Maybe the difficulty could come from monsters getting tougher in
\r
136 far-away places, and the player starting to need something from
\r
137 there when time goes by.
\r
138 - The player would have some of that stuff at the beginning, and
\r
139 would need new supplies of it when it runs out
\r
142 - A spread-items-on-map routine for the bomb, and for dying players
\r
145 - Proper sword swing simulation
\r
146 - Player should get damage from colliding to a wall at high speed
\r
151 Build system / running:
\r
152 -----------------------
\r
154 Networking and serialization:
\r
155 -----------------------------
\r
157 SUGG: Fix address to be ipv6 compatible
\r
165 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
167 - That is >500 vertices
\r
168 - This is not easy; all the MapBlocks close to the player would
\r
169 still need to be drawn separately and combining the blocks
\r
170 would have to happen in a background thread
\r
172 SUGG: Make fetching sector's blocks more efficient when rendering
\r
173 sectors that have very large amounts of blocks (on client)
\r
174 - Is this necessary at all?
\r
176 TODO: Flowing water animation
\r
178 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
179 animating them is easier.
\r
181 SUGG: Option for enabling proper alpha channel for textures
\r
182 TODO: A setting for enabling bilinear filtering for textures
\r
184 TODO: Better control of draw_control.wanted_max_blocks
\r
186 TODO: Further investigate the use of GPU lighting in addition to the
\r
189 SUGG: Somehow make the night less colorful
\r
197 TODO: Untie client network operations from framerate
\r
198 - Needs some input queues or something
\r
199 - This won't give much performance boost because calculating block
\r
200 meshes takes so long
\r
202 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
204 TODO: Don't update all meshes always on single node changes, but
\r
205 check which ones should be updated
\r
206 - implement Map::updateNodeMeshes() and the usage of it
\r
207 - It will give almost always a 4x boost in mesh update performance.
\r
211 - Tool/weapon visualization
\r
213 FIXME: When disconnected to the menu, memory is not freed properly
\r
215 TODO: Investigate how much the mesh generator thread gets used when
\r
216 transferring map data
\r
221 SUGG: Make an option to the server to disable building and digging near
\r
222 the starting position
\r
224 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
226 * Fix the problem with the server constantly saving one or a few
\r
227 blocks? List the first saved block, maybe it explains.
\r
228 - It is probably caused by oscillating water
\r
229 * Make a small history check to transformLiquids to detect and log
\r
230 continuous oscillations, in such detail that they can be fixed.
\r
232 FIXME: The new optimized map sending doesn't sometimes send enough blocks
\r
233 from big caves and such
\r
234 FIXME: Block send distance configuration does not take effect for some reason
\r
236 TODO: Map saving should be done by EmergeThread
\r
238 SUGG: Map unloading based on sector reference is not very good, it keeps
\r
239 unnecessary stuff in memory. I guess. Investigate this.
\r
241 TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set
\r
242 the direction accordingly.
\r
247 TODO: A list of "active blocks" in which stuff happens. (+=done)
\r
248 + Add a never-resetted game timer to the server
\r
249 + Add a timestamp value to blocks
\r
250 + The simple rule: All blocks near some player are "active"
\r
251 - Do stuff in real time in active blocks
\r
253 TODO: Make proper hooks in here
\r
254 - Grow grass, delete leaves without a tree
\r
255 - Spawn some mobs based on some rules
\r
256 - Transform cobble to mossy cobble near water
\r
257 - Run a custom script
\r
258 - ...And all kinds of other dynamic stuff
\r
259 + Keep track of when a block becomes active and becomes inactive
\r
260 + When a block goes inactive:
\r
261 + Store objects statically to block
\r
262 + Store timer value as the timestamp
\r
263 + When a block goes active:
\r
264 + Create active objects out of static objects
\r
265 TODO: Make proper hooks in here
\r
266 - Simulate the results of what would have happened if it would have
\r
267 been active for all the time
\r
268 - Grow a lot of grass and so on
\r
269 + Initially it is fine to send information about every active object
\r
270 to every player. Eventually it should be modified to only send info
\r
271 about the nearest ones.
\r
272 + This was left to be done by the old system and it sends only the
\r
278 TODO: Get rid of MapBlockObjects and use only ActiveObjects
\r
279 - Skipping the MapBlockObject data is nasty - there is no "total
\r
280 length" stored; have to make a SkipMBOs function which contains
\r
281 enough of the current code to skip them properly.
\r
283 SUGG: MovingObject::move and Player::move are basically the same.
\r
285 - NOTE: Player::move is more up-to-date.
\r
286 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
287 - NOTE: MovingObject will be deleted (MapBlockObject)
\r
289 TODO: Add a long step function to objects that is called with the time
\r
290 difference when block activates
\r
295 TODO: Mineral and ground material properties
\r
296 - This way mineral ground toughness can be calculated with just
\r
297 some formula, as well as tool strengths
\r
298 - There are TODOs in appropriate files: material.h, content_mapnode.h
\r
300 TODO: Flowing water to actually contain flow direction information
\r
301 - There is a space for this - it just has to be implemented.
\r
303 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
304 of low-res 3d noise and interpolate linearly.
\r
306 Mapgen v2 (the current one):
\r
307 * Possibly add some kind of erosion and other stuff
\r
308 * Better water generation (spread it to underwater caverns but don't
\r
309 fill dungeons that don't touch big water masses)
\r
310 * When generating a chunk and the neighboring chunk doesn't have mud
\r
311 and stuff yet and the ground is fairly flat, the mud will flow to
\r
312 the other chunk making nasty straight walls when the other chunk
\r
313 is generated. Fix it. Maybe just a special case if the ground is
\r
315 * Consider not updating this one and make a good mainly block-based
\r
318 SUGG: Make two "modified states", one that forces the block to be saved at
\r
319 the next save event, and one that makes the block to be saved at exit
\r
322 TODO: Add a not_fully_generated flag to MapBlock, which would be set for
\r
323 blocks that contain eg. trees from neighboring generations but haven't
\r
324 been generated itself. This is required for the future generator.
\r
328 - Make sure server handles removing grass when a block is placed (etc)
\r
329 - The client should not do it by itself
\r
330 - Block cube placement around player's head
\r
331 - Protocol version field
\r
332 - Consider getting some textures from cisoun's texture pack
\r
334 - Make sure the fence implementation and data format is good
\r
335 - Think about using same bits for material for fences and doors, for
\r
337 - Finish the ActiveBlockModifier stuff and use it for something
\r
338 - Move mineral to param2, increment map serialization version, add conversion
\r
340 TODO: Add a per-sector database to store surface stuff as simple flags/values
\r
343 And at some point make the server send this data to the client too,
\r
344 instead of referring to the noise functions
\r
346 - Surface ground type
\r
349 Making it more portable:
\r
350 ------------------------
\r
352 Stuff to do before release:
\r
353 ---------------------------
\r
355 Fixes to the current release:
\r
356 -----------------------------
\r
358 Stuff to do after release:
\r
359 ---------------------------
\r
361 ======================================================================
\r
367 #pragma message ("Disabling unit tests")
\r
369 #warning "Disabling unit tests"
\r
371 // Disable unit tests
\r
372 #define ENABLE_TESTS 0
\r
374 // Enable unit tests
\r
375 #define ENABLE_TESTS 1
\r
379 #pragma comment(lib, "Irrlicht.lib")
\r
380 //#pragma comment(lib, "jthread.lib")
\r
381 #pragma comment(lib, "zlibwapi.lib")
\r
382 #pragma comment(lib, "Shell32.lib")
\r
383 // This would get rid of the console window
\r
384 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
387 #include <iostream>
\r
389 //#include <jmutexautolock.h>
\r
390 #include <locale.h>
\r
392 #include "common_irrlicht.h"
\r
395 //#include "player.h"
\r
397 #include "server.h"
\r
398 //#include "client.h"
\r
399 #include "constants.h"
\r
400 #include "porting.h"
\r
401 #include "gettime.h"
\r
402 #include "guiMessageMenu.h"
\r
403 #include "filesys.h"
\r
404 #include "config.h"
\r
405 #include "guiMainMenu.h"
\r
406 #include "mineral.h"
\r
407 //#include "noise.h"
\r
408 //#include "tile.h"
\r
409 #include "materials.h"
\r
411 #include "keycode.h"
\r
413 // This makes textures
\r
414 ITextureSource *g_texturesource = NULL;
\r
418 These are loaded from the config file.
\r
421 Settings g_settings;
\r
422 // This is located in defaultsettings.cpp
\r
423 extern void set_default_settings();
\r
426 Profiler g_profiler;
\r
436 gui::IGUIEnvironment* guienv = NULL;
\r
437 gui::IGUIStaticText *guiroot = NULL;
\r
439 MainMenuManager g_menumgr;
\r
441 bool noMenuActive()
\r
443 return (g_menumgr.menuCount() == 0);
\r
446 // Passed to menus to allow disconnecting and exiting
\r
448 MainGameCallback *g_gamecallback = NULL;
\r
455 std::ostream *dout_con_ptr = &dummyout;
\r
456 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
457 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
458 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
459 //std::ostream *dout_con_ptr = &dstream;
\r
460 //std::ostream *derr_con_ptr = &dstream;
\r
463 std::ostream *dout_server_ptr = &dstream;
\r
464 std::ostream *derr_server_ptr = &dstream;
\r
467 std::ostream *dout_client_ptr = &dstream;
\r
468 std::ostream *derr_client_ptr = &dstream;
\r
471 gettime.h implementation
\r
474 // A small helper class
\r
478 virtual u32 getTime() = 0;
\r
481 // A precise irrlicht one
\r
482 class IrrlichtTimeGetter: public TimeGetter
\r
485 IrrlichtTimeGetter(IrrlichtDevice *device):
\r
490 if(m_device == NULL)
\r
492 return m_device->getTimer()->getRealTime();
\r
495 IrrlichtDevice *m_device;
\r
497 // Not so precise one which works without irrlicht
\r
498 class SimpleTimeGetter: public TimeGetter
\r
503 return porting::getTimeMs();
\r
507 // A pointer to a global instance of the time getter
\r
509 TimeGetter *g_timegetter = NULL;
\r
513 if(g_timegetter == NULL)
\r
515 return g_timegetter->getTime();
\r
519 Event handler for Irrlicht
\r
521 NOTE: Everything possible should be moved out from here,
\r
522 probably to InputHandler and the_game
\r
525 class MyEventReceiver : public IEventReceiver
\r
528 // This is the one method that we have to implement
\r
529 virtual bool OnEvent(const SEvent& event)
\r
532 React to nothing here if a menu is active
\r
534 if(noMenuActive() == false)
\r
539 // Remember whether each key is down or up
\r
540 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
542 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
544 if(event.KeyInput.PressedDown)
\r
545 keyWasDown[event.KeyInput.Key] = true;
\r
548 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
550 if(noMenuActive() == false)
\r
552 left_active = false;
\r
553 middle_active = false;
\r
554 right_active = false;
\r
558 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
559 left_active = event.MouseInput.isLeftPressed();
\r
560 middle_active = event.MouseInput.isMiddlePressed();
\r
561 right_active = event.MouseInput.isRightPressed();
\r
563 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
565 leftclicked = true;
\r
567 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
569 rightclicked = true;
\r
571 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
573 leftreleased = true;
\r
575 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
577 rightreleased = true;
\r
579 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
581 mouse_wheel += event.MouseInput.Wheel;
\r
589 bool IsKeyDown(EKEY_CODE keyCode) const
\r
591 return keyIsDown[keyCode];
\r
594 // Checks whether a key was down and resets the state
\r
595 bool WasKeyDown(EKEY_CODE keyCode)
\r
597 bool b = keyWasDown[keyCode];
\r
598 keyWasDown[keyCode] = false;
\r
602 s32 getMouseWheel()
\r
604 s32 a = mouse_wheel;
\r
611 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
613 keyIsDown[i] = false;
\r
614 keyWasDown[i] = false;
\r
617 leftclicked = false;
\r
618 rightclicked = false;
\r
619 leftreleased = false;
\r
620 rightreleased = false;
\r
622 left_active = false;
\r
623 middle_active = false;
\r
624 right_active = false;
\r
637 bool rightreleased;
\r
640 bool middle_active;
\r
646 IrrlichtDevice *m_device;
\r
648 // The current state of keys
\r
649 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
650 // Whether a key has been pressed or not
\r
651 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
655 Separated input handler
\r
658 class RealInputHandler : public InputHandler
\r
661 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
663 m_receiver(receiver)
\r
666 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
668 return m_receiver->IsKeyDown(keyCode);
\r
670 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
672 return m_receiver->WasKeyDown(keyCode);
\r
674 virtual v2s32 getMousePos()
\r
676 return m_device->getCursorControl()->getPosition();
\r
678 virtual void setMousePos(s32 x, s32 y)
\r
680 m_device->getCursorControl()->setPosition(x, y);
\r
683 virtual bool getLeftState()
\r
685 return m_receiver->left_active;
\r
687 virtual bool getRightState()
\r
689 return m_receiver->right_active;
\r
692 virtual bool getLeftClicked()
\r
694 return m_receiver->leftclicked;
\r
696 virtual bool getRightClicked()
\r
698 return m_receiver->rightclicked;
\r
700 virtual void resetLeftClicked()
\r
702 m_receiver->leftclicked = false;
\r
704 virtual void resetRightClicked()
\r
706 m_receiver->rightclicked = false;
\r
709 virtual bool getLeftReleased()
\r
711 return m_receiver->leftreleased;
\r
713 virtual bool getRightReleased()
\r
715 return m_receiver->rightreleased;
\r
717 virtual void resetLeftReleased()
\r
719 m_receiver->leftreleased = false;
\r
721 virtual void resetRightReleased()
\r
723 m_receiver->rightreleased = false;
\r
726 virtual s32 getMouseWheel()
\r
728 return m_receiver->getMouseWheel();
\r
733 m_receiver->clearInput();
\r
736 IrrlichtDevice *m_device;
\r
737 MyEventReceiver *m_receiver;
\r
740 class RandomInputHandler : public InputHandler
\r
743 RandomInputHandler()
\r
747 leftclicked = false;
\r
748 rightclicked = false;
\r
749 leftreleased = false;
\r
750 rightreleased = false;
\r
751 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
752 keydown[i] = false;
\r
754 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
756 return keydown[keyCode];
\r
758 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
762 virtual v2s32 getMousePos()
\r
766 virtual void setMousePos(s32 x, s32 y)
\r
768 mousepos = v2s32(x,y);
\r
771 virtual bool getLeftState()
\r
775 virtual bool getRightState()
\r
780 virtual bool getLeftClicked()
\r
782 return leftclicked;
\r
784 virtual bool getRightClicked()
\r
786 return rightclicked;
\r
788 virtual void resetLeftClicked()
\r
790 leftclicked = false;
\r
792 virtual void resetRightClicked()
\r
794 rightclicked = false;
\r
797 virtual bool getLeftReleased()
\r
799 return leftreleased;
\r
801 virtual bool getRightReleased()
\r
803 return rightreleased;
\r
805 virtual void resetLeftReleased()
\r
807 leftreleased = false;
\r
809 virtual void resetRightReleased()
\r
811 rightreleased = false;
\r
814 virtual s32 getMouseWheel()
\r
819 virtual void step(float dtime)
\r
822 static float counter1 = 0;
\r
826 counter1 = 0.1*Rand(1, 40);
\r
827 keydown[getKeySetting("keymap_jump")] =
\r
828 !keydown[getKeySetting("keymap_jump")];
\r
832 static float counter1 = 0;
\r
836 counter1 = 0.1*Rand(1, 40);
\r
837 keydown[getKeySetting("keymap_special1")] =
\r
838 !keydown[getKeySetting("keymap_special1")];
\r
842 static float counter1 = 0;
\r
846 counter1 = 0.1*Rand(1, 40);
\r
847 keydown[getKeySetting("keymap_forward")] =
\r
848 !keydown[getKeySetting("keymap_forward")];
\r
852 static float counter1 = 0;
\r
856 counter1 = 0.1*Rand(1, 40);
\r
857 keydown[getKeySetting("keymap_left")] =
\r
858 !keydown[getKeySetting("keymap_left")];
\r
862 static float counter1 = 0;
\r
866 counter1 = 0.1*Rand(1, 20);
\r
867 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
871 static float counter1 = 0;
\r
875 counter1 = 0.1*Rand(1, 30);
\r
876 leftdown = !leftdown;
\r
878 leftclicked = true;
\r
880 leftreleased = true;
\r
884 static float counter1 = 0;
\r
888 counter1 = 0.1*Rand(1, 15);
\r
889 rightdown = !rightdown;
\r
891 rightclicked = true;
\r
893 rightreleased = true;
\r
896 mousepos += mousespeed;
\r
899 s32 Rand(s32 min, s32 max)
\r
901 return (myrand()%(max-min+1))+min;
\r
904 bool keydown[KEY_KEY_CODES_COUNT];
\r
912 bool rightreleased;
\r
915 // These are defined global so that they're not optimized too much.
\r
916 // Can't change them to volatile.
\r
921 std::string tempstring;
\r
922 std::string tempstring2;
\r
927 dstream<<"The following test should take around 20ms."<<std::endl;
\r
928 TimeTaker timer("Testing std::string speed");
\r
929 const u32 jj = 10000;
\r
930 for(u32 j=0; j<jj; j++)
\r
935 for(u32 i=0; i<ii; i++){
\r
936 tempstring2 += "asd";
\r
938 for(u32 i=0; i<ii+1; i++){
\r
939 tempstring += "asd";
\r
940 if(tempstring == tempstring2)
\r
946 dstream<<"All of the following tests should take around 100ms each."
\r
950 TimeTaker timer("Testing floating-point conversion speed");
\r
952 for(u32 i=0; i<4000000; i++){
\r
959 TimeTaker timer("Testing floating-point vector speed");
\r
961 tempv3f1 = v3f(1,2,3);
\r
962 tempv3f2 = v3f(4,5,6);
\r
963 for(u32 i=0; i<10000000; i++){
\r
964 tempf += tempv3f1.dotProduct(tempv3f2);
\r
965 tempv3f2 += v3f(7,8,9);
\r
970 TimeTaker timer("Testing core::map speed");
\r
972 core::map<v2s16, f32> map1;
\r
975 for(s16 y=0; y<ii; y++){
\r
976 for(s16 x=0; x<ii; x++){
\r
977 map1.insert(v2s16(x,y), tempf);
\r
981 for(s16 y=ii-1; y>=0; y--){
\r
982 for(s16 x=0; x<ii; x++){
\r
983 tempf = map1[v2s16(x,y)];
\r
989 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
990 TimeTaker timer("Testing mutex speed");
\r
1003 // Do at least 10ms
\r
1004 while(timer.getTime() < 10);
\r
1006 u32 dtime = timer.stop();
\r
1007 u32 per_ms = n / dtime;
\r
1008 std::cout<<"Done. "<<dtime<<"ms, "
\r
1009 <<per_ms<<"/ms"<<std::endl;
\r
1013 void drawMenuBackground(video::IVideoDriver* driver)
\r
1015 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
1017 video::ITexture *bgtexture =
\r
1018 driver->getTexture(getTexturePath("mud.png").c_str());
\r
1021 s32 texturesize = 128;
\r
1022 s32 tiled_y = screensize.Height / texturesize + 1;
\r
1023 s32 tiled_x = screensize.Width / texturesize + 1;
\r
1025 for(s32 y=0; y<tiled_y; y++)
\r
1026 for(s32 x=0; x<tiled_x; x++)
\r
1028 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
1029 rect += v2s32(x*texturesize, y*texturesize);
\r
1030 driver->draw2DImage(bgtexture, rect,
\r
1031 core::rect<s32>(core::position2d<s32>(0,0),
\r
1032 core::dimension2di(bgtexture->getSize())),
\r
1033 NULL, NULL, true);
\r
1037 video::ITexture *logotexture =
\r
1038 driver->getTexture(getTexturePath("menulogo.png").c_str());
\r
1041 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
1042 logotexture->getOriginalSize().Height);
\r
1045 video::SColor bgcolor(255,50,50,50);
\r
1046 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
1047 screensize.Width, screensize.Height);
\r
1048 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
1050 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
1051 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
1052 rect -= v2s32(logosize.X/2, 0);
\r
1053 driver->draw2DImage(logotexture, rect,
\r
1054 core::rect<s32>(core::position2d<s32>(0,0),
\r
1055 core::dimension2di(logotexture->getSize())),
\r
1056 NULL, NULL, true);
\r
1060 int main(int argc, char *argv[])
\r
1066 // Set locale. This is for forcing '.' as the decimal point.
\r
1067 std::locale::global(std::locale("C"));
\r
1068 // This enables printing all characters in bitmap font
\r
1069 setlocale(LC_CTYPE, "en_US");
\r
1072 Parse command line
\r
1075 // List all allowed options
\r
1076 core::map<std::string, ValueSpec> allowed_options;
\r
1077 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1078 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1079 "Run server directly"));
\r
1080 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1081 "Load configuration from specified file"));
\r
1082 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1083 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1084 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1085 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1086 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1087 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1089 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1091 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1093 Settings cmd_args;
\r
1095 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1097 if(ret == false || cmd_args.getFlag("help"))
\r
1099 dstream<<"Allowed options:"<<std::endl;
\r
1100 for(core::map<std::string, ValueSpec>::Iterator
\r
1101 i = allowed_options.getIterator();
\r
1102 i.atEnd() == false; i++)
\r
1104 dstream<<" --"<<i.getNode()->getKey();
\r
1105 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1110 dstream<<" <value>";
\r
1112 dstream<<std::endl;
\r
1114 if(i.getNode()->getValue().help != NULL)
\r
1116 dstream<<" "<<i.getNode()->getValue().help
\r
1121 return cmd_args.getFlag("help") ? 0 : 1;
\r
1125 Low-level initialization
\r
1128 bool disable_stderr = false;
\r
1130 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1131 disable_stderr = true;
\r
1134 porting::signal_handler_init();
\r
1135 bool &kill = *porting::signal_handler_killstatus();
\r
1137 // Initialize porting::path_data and porting::path_userdata
\r
1138 porting::initializePaths();
\r
1140 // Initialize debug streams
\r
1141 #ifdef RUN_IN_PLACE
\r
1142 std::string debugfile = DEBUGFILE;
\r
1144 std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
\r
1146 debugstreams_init(disable_stderr, debugfile.c_str());
\r
1147 // Initialize debug stacks
\r
1148 debug_stacks_init();
\r
1150 DSTACK(__FUNCTION_NAME);
\r
1152 // Create user data directory
\r
1153 fs::CreateDir(porting::path_userdata);
\r
1155 // Init material properties table
\r
1156 //initializeMaterialProperties();
\r
1159 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1161 // Print startup message
\r
1162 dstream<<DTIME<<"minetest-c55"
\r
1163 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1164 <<", "<<BUILD_INFO
\r
1168 Basic initialization
\r
1171 // Initialize default settings
\r
1172 set_default_settings();
\r
1174 // Initialize sockets
\r
1176 atexit(sockets_cleanup);
\r
1182 // Path of configuration file in use
\r
1183 std::string configpath = "";
\r
1185 if(cmd_args.exists("config"))
\r
1187 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1190 dstream<<"Could not read configuration from \""
\r
1191 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1194 configpath = cmd_args.get("config");
\r
1198 core::array<std::string> filenames;
\r
1199 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1200 #ifdef RUN_IN_PLACE
\r
1201 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1204 for(u32 i=0; i<filenames.size(); i++)
\r
1206 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1209 configpath = filenames[i];
\r
1214 // If no path found, use the first one (menu creates the file)
\r
1215 if(configpath == "")
\r
1216 configpath = filenames[0];
\r
1219 // Initialize random seed
\r
1224 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1226 These are needed for unit tests at least.
\r
1229 // Initial call with g_texturesource not set.
\r
1236 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1237 || cmd_args.getFlag("enable-unittests") == true)
\r
1242 /*for(s16 y=-100; y<100; y++)
\r
1243 for(s16 x=-100; x<100; x++)
\r
1245 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1255 if(cmd_args.exists("port"))
\r
1256 port = cmd_args.getU16("port");
\r
1257 else if(g_settings.exists("port"))
\r
1258 port = g_settings.getU16("port");
\r
1263 std::string map_dir = porting::path_userdata+"/world";
\r
1264 if(cmd_args.exists("map-dir"))
\r
1265 map_dir = cmd_args.get("map-dir");
\r
1266 else if(g_settings.exists("map-dir"))
\r
1267 map_dir = g_settings.get("map-dir");
\r
1269 // Run dedicated server if asked to
\r
1270 if(cmd_args.getFlag("server"))
\r
1272 DSTACK("Dedicated server branch");
\r
1274 // Create time getter
\r
1275 g_timegetter = new SimpleTimeGetter();
\r
1278 Server server(map_dir.c_str());
\r
1279 server.start(port);
\r
1282 dedicated_server_loop(server, kill);
\r
1292 // Address to connect to
\r
1293 std::string address = "";
\r
1295 if(cmd_args.exists("address"))
\r
1297 address = cmd_args.get("address");
\r
1301 address = g_settings.get("address");
\r
1304 std::string playername = g_settings.get("name");
\r
1307 Device initialization
\r
1310 // Resolution selection
\r
1312 bool fullscreen = false;
\r
1313 u16 screenW = g_settings.getU16("screenW");
\r
1314 u16 screenH = g_settings.getU16("screenH");
\r
1316 // Determine driver
\r
1318 video::E_DRIVER_TYPE driverType;
\r
1320 std::string driverstring = g_settings.get("video_driver");
\r
1322 if(driverstring == "null")
\r
1323 driverType = video::EDT_NULL;
\r
1324 else if(driverstring == "software")
\r
1325 driverType = video::EDT_SOFTWARE;
\r
1326 else if(driverstring == "burningsvideo")
\r
1327 driverType = video::EDT_BURNINGSVIDEO;
\r
1328 else if(driverstring == "direct3d8")
\r
1329 driverType = video::EDT_DIRECT3D8;
\r
1330 else if(driverstring == "direct3d9")
\r
1331 driverType = video::EDT_DIRECT3D9;
\r
1332 else if(driverstring == "opengl")
\r
1333 driverType = video::EDT_OPENGL;
\r
1336 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1337 "to opengl"<<std::endl;
\r
1338 driverType = video::EDT_OPENGL;
\r
1342 Create device and exit if creation failed
\r
1345 MyEventReceiver receiver;
\r
1347 IrrlichtDevice *device;
\r
1348 device = createDevice(driverType,
\r
1349 core::dimension2d<u32>(screenW, screenH),
\r
1350 16, fullscreen, false, false, &receiver);
\r
1353 return 1; // could not create selected driver.
\r
1355 // Set device in game parameters
\r
1358 // Create time getter
\r
1359 g_timegetter = new IrrlichtTimeGetter(device);
\r
1361 // Create game callback for menus
\r
1362 g_gamecallback = new MainGameCallback(device);
\r
1364 // Create texture source
\r
1365 g_texturesource = new TextureSource(device);
\r
1368 Speed tests (done after irrlicht is loaded to get timer)
\r
1370 if(cmd_args.getFlag("speedtests"))
\r
1372 dstream<<"Running speed tests"<<std::endl;
\r
1377 device->setResizable(true);
\r
1379 bool random_input = g_settings.getBool("random_input")
\r
1380 || cmd_args.getFlag("random-input");
\r
1381 InputHandler *input = NULL;
\r
1383 input = new RandomInputHandler();
\r
1385 input = new RealInputHandler(device, &receiver);
\r
1388 Continue initialization
\r
1391 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1394 This changes the minimum allowed number of vertices in a VBO.
\r
1397 //driver->setMinHardwareBufferVertexCount(50);
\r
1399 scene::ISceneManager* smgr = device->getSceneManager();
\r
1401 guienv = device->getGUIEnvironment();
\r
1402 gui::IGUISkin* skin = guienv->getSkin();
\r
1403 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
\r
1405 skin->setFont(font);
\r
1407 dstream<<"WARNING: Font file was not found."
\r
1408 " Using default font."<<std::endl;
\r
1409 // If font was not found, this will get us one
\r
1410 font = skin->getFont();
\r
1413 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1414 dstream<<"text_height="<<text_height<<std::endl;
\r
1416 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1417 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1418 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1419 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1420 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1421 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1424 Preload some textures and stuff
\r
1427 init_mapnode(); // Second call with g_texturesource set
\r
1435 If an error occurs, this is set to something and the
\r
1436 menu-game loop is restarted. It is then displayed before
\r
1439 std::wstring error_message = L"";
\r
1441 // The password entered during the menu screen,
\r
1442 std::string password;
\r
1447 while(device->run() && kill == false)
\r
1450 // This is used for catching disconnects
\r
1455 Clear everything from the GUIEnvironment
\r
1460 We need some kind of a root node to be able to add
\r
1461 custom gui elements directly on the screen.
\r
1462 Otherwise they won't be automatically drawn.
\r
1464 guiroot = guienv->addStaticText(L"",
\r
1465 core::rect<s32>(0, 0, 10000, 10000));
\r
1468 Out-of-game menu loop.
\r
1470 Loop quits when menu returns proper parameters.
\r
1472 while(kill == false)
\r
1474 // Cursor can be non-visible when coming from the game
\r
1475 device->getCursorControl()->setVisible(true);
\r
1476 // Some stuff are left to scene manager when coming from the game
\r
1477 // (map at least?)
\r
1479 // Reset or hide the debug gui texts
\r
1480 /*guitext->setText(L"Minetest-c55");
\r
1481 guitext2->setVisible(false);
\r
1482 guitext_info->setVisible(false);
\r
1483 guitext_chat->setVisible(false);*/
\r
1485 // Initialize menu data
\r
1486 MainMenuData menudata;
\r
1487 menudata.address = narrow_to_wide(address);
\r
1488 menudata.name = narrow_to_wide(playername);
\r
1489 menudata.port = narrow_to_wide(itos(port));
\r
1490 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1491 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1492 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1493 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1495 GUIMainMenu *menu =
\r
1496 new GUIMainMenu(guienv, guiroot, -1,
\r
1497 &g_menumgr, &menudata, g_gamecallback);
\r
1498 menu->allowFocusRemoval(true);
\r
1500 if(error_message != L"")
\r
1502 dstream<<"WARNING: error_message = "
\r
1503 <<wide_to_narrow(error_message)<<std::endl;
\r
1505 GUIMessageMenu *menu2 =
\r
1506 new GUIMessageMenu(guienv, guiroot, -1,
\r
1507 &g_menumgr, error_message.c_str());
\r
1509 error_message = L"";
\r
1512 video::IVideoDriver* driver = device->getVideoDriver();
\r
1514 dstream<<"Created main menu"<<std::endl;
\r
1516 while(device->run() && kill == false)
\r
1518 if(menu->getStatus() == true)
\r
1521 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1522 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1524 drawMenuBackground(driver);
\r
1526 guienv->drawAll();
\r
1528 driver->endScene();
\r
1530 // On some computers framerate doesn't seem to be
\r
1531 // automatically limited
\r
1535 // Break out of menu-game loop to shut down cleanly
\r
1536 if(device->run() == false || kill == true)
\r
1539 dstream<<"Dropping main menu"<<std::endl;
\r
1543 // Delete map if requested
\r
1544 if(menudata.delete_map)
\r
1546 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1548 error_message = L"Delete failed";
\r
1552 playername = wide_to_narrow(menudata.name);
\r
1554 password = translatePassword(playername, menudata.password);
\r
1556 address = wide_to_narrow(menudata.address);
\r
1557 int newport = stoi(wide_to_narrow(menudata.port));
\r
1560 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1561 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1562 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1563 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1565 // NOTE: These are now checked server side; no need to do it
\r
1566 // here, so let's not do it here.
\r
1567 /*// Check for valid parameters, restart menu if invalid.
\r
1568 if(playername == "")
\r
1570 error_message = L"Name required.";
\r
1573 // Check that name has only valid chars
\r
1574 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
\r
1576 error_message = L"Characters allowed: "
\r
1577 +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
\r
1582 g_settings.set("name", playername);
\r
1583 g_settings.set("address", address);
\r
1584 g_settings.set("port", itos(port));
\r
1585 // Update configuration file
\r
1586 if(configpath != "")
\r
1587 g_settings.updateConfigFile(configpath.c_str());
\r
1589 // Continue to game
\r
1593 // Break out of menu-game loop to shut down cleanly
\r
1594 if(device->run() == false)
\r
1597 // Initialize mapnode again to enable changed graphics settings
\r
1618 catch(con::PeerNotFoundException &e)
\r
1620 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1621 error_message = L"Connection error (timed out?)";
\r
1623 catch(SocketException &e)
\r
1625 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1626 error_message = L"Socket error (port already in use?)";
\r
1629 catch(std::exception &e)
\r
1631 std::string narrow_message = "Some exception, what()=\"";
\r
1632 narrow_message += e.what();
\r
1633 narrow_message += "\"";
\r
1634 dstream<<DTIME<<narrow_message<<std::endl;
\r
1635 error_message = narrow_to_wide(narrow_message);
\r
1639 } // Menu-game loop
\r
1644 In the end, delete the Irrlicht device.
\r
1648 END_DEBUG_EXCEPTION_HANDLER
\r
1650 debugstreams_deinit();
\r