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
231 TODO: A list of "active blocks" in which stuff happens.
\r
232 + Add a never-resetted game timer to the server
\r
233 + Add a timestamp value to blocks
\r
234 + The simple rule: All blocks near some player are "active"
\r
235 - Do stuff in real time in active blocks
\r
237 TODO: Make proper hooks in here
\r
238 - Grow grass, delete leaves without a tree
\r
239 - Spawn some mobs based on some rules
\r
240 - Transform cobble to mossy cobble near water
\r
241 - Run a custom script
\r
242 - ...And all kinds of other dynamic stuff
\r
243 + Keep track of when a block becomes active and becomes inactive
\r
244 + When a block goes inactive:
\r
245 + Store objects statically to block
\r
246 + Store timer value as the timestamp
\r
247 + When a block goes active:
\r
248 + Create active objects out of static objects
\r
249 TODO: Make proper hooks in here
\r
250 - Simulate the results of what would have happened if it would have
\r
251 been active for all the time
\r
252 - Grow a lot of grass and so on
\r
253 + Initially it is fine to send information about every active object
\r
254 to every player. Eventually it should be modified to only send info
\r
255 about the nearest ones.
\r
256 + This was left to be done by the old system and it sends only the
\r
262 TODO: Get rid of MapBlockObjects and use only ActiveObjects
\r
263 - Skipping the MapBlockObject data is nasty - there is no "total
\r
264 length" stored; have to make a SkipMBOs function which contains
\r
265 enough of the current code to skip them properly.
\r
267 SUGG: MovingObject::move and Player::move are basically the same.
\r
269 - NOTE: Player::move is more up-to-date.
\r
270 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
271 - NOTE: MovingObject will be deleted (MapBlockObject)
\r
273 TODO: Add a long step function to objects that is called with the time
\r
274 difference when block activates
\r
279 TODO: Mineral and ground material properties
\r
280 - This way mineral ground toughness can be calculated with just
\r
281 some formula, as well as tool strengths
\r
283 TODO: Flowing water to actually contain flow direction information
\r
284 - There is a space for this - it just has to be implemented.
\r
286 SUGG: Erosion simulation at map generation time
\r
287 - Simulate water flows, which would carve out dirt fast and
\r
288 then turn stone into gravel and sand and relocate it.
\r
289 - How about relocating minerals, too? Coal and gold in
\r
290 downstream sand and gravel would be kind of cool
\r
291 - This would need a better way of handling minerals, mainly
\r
292 to have mineral content as a separate field. the first
\r
293 parameter field is free for this.
\r
294 - Simulate rock falling from cliffs when water has removed
\r
295 enough solid rock from the bottom
\r
297 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
298 of low-res 3d noise and interpolate linearly.
\r
301 * Possibly add some kind of erosion and other stuff
\r
302 * Better water generation (spread it to underwater caverns but don't
\r
303 fill dungeons that don't touch big water masses)
\r
304 * When generating a chunk and the neighboring chunk doesn't have mud
\r
305 and stuff yet and the ground is fairly flat, the mud will flow to
\r
306 the other chunk making nasty straight walls when the other chunk
\r
307 is generated. Fix it. Maybe just a special case if the ground is
\r
312 * Move digging property stuff from material.{h,cpp} to mapnode.cpp
\r
313 - ...Or maybe move content_features to material.{h,cpp}?
\r
315 Making it more portable:
\r
316 ------------------------
\r
318 Stuff to do before release:
\r
319 ---------------------------
\r
321 Fixes to the current release:
\r
322 -----------------------------
\r
324 Stuff to do after release:
\r
325 ---------------------------
\r
326 - Make sure server handles removing grass when a block is placed (etc)
\r
327 - The client should not do it by itself
\r
328 - Block cube placement around player's head
\r
329 - Protocol version field
\r
330 - Consider getting some textures from cisoun's texture pack
\r
332 - Make sure the fence implementation and data format is good
\r
333 - Think about using same bits for material for fences and doors, for
\r
335 - Finish the ActiveBlockModifier stuff and use it for something
\r
336 - Move mineral to param2, increment map serialization version, add conversion
\r
338 ======================================================================
\r
344 #pragma message ("Disabling unit tests")
\r
346 #warning "Disabling unit tests"
\r
348 // Disable unit tests
\r
349 #define ENABLE_TESTS 0
\r
351 // Enable unit tests
\r
352 #define ENABLE_TESTS 1
\r
356 #pragma comment(lib, "Irrlicht.lib")
\r
357 //#pragma comment(lib, "jthread.lib")
\r
358 #pragma comment(lib, "zlibwapi.lib")
\r
359 #pragma comment(lib, "Shell32.lib")
\r
360 // This would get rid of the console window
\r
361 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
364 #include <iostream>
\r
366 //#include <jmutexautolock.h>
\r
367 #include <locale.h>
\r
369 #include "common_irrlicht.h"
\r
372 //#include "player.h"
\r
374 #include "server.h"
\r
375 //#include "client.h"
\r
376 #include "constants.h"
\r
377 #include "porting.h"
\r
378 #include "gettime.h"
\r
379 #include "guiMessageMenu.h"
\r
380 #include "filesys.h"
\r
381 #include "config.h"
\r
382 #include "guiMainMenu.h"
\r
383 #include "mineral.h"
\r
384 //#include "noise.h"
\r
385 //#include "tile.h"
\r
386 #include "materials.h"
\r
388 #include "keycode.h"
\r
390 // This makes textures
\r
391 ITextureSource *g_texturesource = NULL;
\r
395 These are loaded from the config file.
\r
398 Settings g_settings;
\r
399 // This is located in defaultsettings.cpp
\r
400 extern void set_default_settings();
\r
403 Profiler g_profiler;
\r
413 gui::IGUIEnvironment* guienv = NULL;
\r
414 gui::IGUIStaticText *guiroot = NULL;
\r
416 MainMenuManager g_menumgr;
\r
418 bool noMenuActive()
\r
420 return (g_menumgr.menuCount() == 0);
\r
423 // Passed to menus to allow disconnecting and exiting
\r
425 MainGameCallback *g_gamecallback = NULL;
\r
432 std::ostream *dout_con_ptr = &dummyout;
\r
433 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
434 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
435 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
436 //std::ostream *dout_con_ptr = &dstream;
\r
437 //std::ostream *derr_con_ptr = &dstream;
\r
440 std::ostream *dout_server_ptr = &dstream;
\r
441 std::ostream *derr_server_ptr = &dstream;
\r
444 std::ostream *dout_client_ptr = &dstream;
\r
445 std::ostream *derr_client_ptr = &dstream;
\r
448 gettime.h implementation
\r
451 // A small helper class
\r
455 virtual u32 getTime() = 0;
\r
458 // A precise irrlicht one
\r
459 class IrrlichtTimeGetter: public TimeGetter
\r
462 IrrlichtTimeGetter(IrrlichtDevice *device):
\r
467 if(m_device == NULL)
\r
469 return m_device->getTimer()->getRealTime();
\r
472 IrrlichtDevice *m_device;
\r
474 // Not so precise one which works without irrlicht
\r
475 class SimpleTimeGetter: public TimeGetter
\r
480 return porting::getTimeMs();
\r
484 // A pointer to a global instance of the time getter
\r
486 TimeGetter *g_timegetter = NULL;
\r
490 if(g_timegetter == NULL)
\r
492 return g_timegetter->getTime();
\r
496 Event handler for Irrlicht
\r
498 NOTE: Everything possible should be moved out from here,
\r
499 probably to InputHandler and the_game
\r
502 class MyEventReceiver : public IEventReceiver
\r
505 // This is the one method that we have to implement
\r
506 virtual bool OnEvent(const SEvent& event)
\r
509 React to nothing here if a menu is active
\r
511 if(noMenuActive() == false)
\r
516 // Remember whether each key is down or up
\r
517 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
519 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
521 if(event.KeyInput.PressedDown)
\r
522 keyWasDown[event.KeyInput.Key] = true;
\r
525 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
527 if(noMenuActive() == false)
\r
529 left_active = false;
\r
530 middle_active = false;
\r
531 right_active = false;
\r
535 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
536 left_active = event.MouseInput.isLeftPressed();
\r
537 middle_active = event.MouseInput.isMiddlePressed();
\r
538 right_active = event.MouseInput.isRightPressed();
\r
540 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
542 leftclicked = true;
\r
544 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
546 rightclicked = true;
\r
548 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
550 leftreleased = true;
\r
552 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
554 rightreleased = true;
\r
556 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
558 mouse_wheel += event.MouseInput.Wheel;
\r
566 bool IsKeyDown(EKEY_CODE keyCode) const
\r
568 return keyIsDown[keyCode];
\r
571 // Checks whether a key was down and resets the state
\r
572 bool WasKeyDown(EKEY_CODE keyCode)
\r
574 bool b = keyWasDown[keyCode];
\r
575 keyWasDown[keyCode] = false;
\r
579 s32 getMouseWheel()
\r
581 s32 a = mouse_wheel;
\r
588 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
590 keyIsDown[i] = false;
\r
591 keyWasDown[i] = false;
\r
594 leftclicked = false;
\r
595 rightclicked = false;
\r
596 leftreleased = false;
\r
597 rightreleased = false;
\r
599 left_active = false;
\r
600 middle_active = false;
\r
601 right_active = false;
\r
614 bool rightreleased;
\r
617 bool middle_active;
\r
623 IrrlichtDevice *m_device;
\r
625 // The current state of keys
\r
626 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
627 // Whether a key has been pressed or not
\r
628 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
632 Separated input handler
\r
635 class RealInputHandler : public InputHandler
\r
638 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
640 m_receiver(receiver)
\r
643 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
645 return m_receiver->IsKeyDown(keyCode);
\r
647 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
649 return m_receiver->WasKeyDown(keyCode);
\r
651 virtual v2s32 getMousePos()
\r
653 return m_device->getCursorControl()->getPosition();
\r
655 virtual void setMousePos(s32 x, s32 y)
\r
657 m_device->getCursorControl()->setPosition(x, y);
\r
660 virtual bool getLeftState()
\r
662 return m_receiver->left_active;
\r
664 virtual bool getRightState()
\r
666 return m_receiver->right_active;
\r
669 virtual bool getLeftClicked()
\r
671 return m_receiver->leftclicked;
\r
673 virtual bool getRightClicked()
\r
675 return m_receiver->rightclicked;
\r
677 virtual void resetLeftClicked()
\r
679 m_receiver->leftclicked = false;
\r
681 virtual void resetRightClicked()
\r
683 m_receiver->rightclicked = false;
\r
686 virtual bool getLeftReleased()
\r
688 return m_receiver->leftreleased;
\r
690 virtual bool getRightReleased()
\r
692 return m_receiver->rightreleased;
\r
694 virtual void resetLeftReleased()
\r
696 m_receiver->leftreleased = false;
\r
698 virtual void resetRightReleased()
\r
700 m_receiver->rightreleased = false;
\r
703 virtual s32 getMouseWheel()
\r
705 return m_receiver->getMouseWheel();
\r
710 m_receiver->clearInput();
\r
713 IrrlichtDevice *m_device;
\r
714 MyEventReceiver *m_receiver;
\r
717 class RandomInputHandler : public InputHandler
\r
720 RandomInputHandler()
\r
724 leftclicked = false;
\r
725 rightclicked = false;
\r
726 leftreleased = false;
\r
727 rightreleased = false;
\r
728 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
729 keydown[i] = false;
\r
731 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
733 return keydown[keyCode];
\r
735 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
739 virtual v2s32 getMousePos()
\r
743 virtual void setMousePos(s32 x, s32 y)
\r
745 mousepos = v2s32(x,y);
\r
748 virtual bool getLeftState()
\r
752 virtual bool getRightState()
\r
757 virtual bool getLeftClicked()
\r
759 return leftclicked;
\r
761 virtual bool getRightClicked()
\r
763 return rightclicked;
\r
765 virtual void resetLeftClicked()
\r
767 leftclicked = false;
\r
769 virtual void resetRightClicked()
\r
771 rightclicked = false;
\r
774 virtual bool getLeftReleased()
\r
776 return leftreleased;
\r
778 virtual bool getRightReleased()
\r
780 return rightreleased;
\r
782 virtual void resetLeftReleased()
\r
784 leftreleased = false;
\r
786 virtual void resetRightReleased()
\r
788 rightreleased = false;
\r
791 virtual s32 getMouseWheel()
\r
796 virtual void step(float dtime)
\r
799 static float counter1 = 0;
\r
803 counter1 = 0.1*Rand(1, 40);
\r
804 keydown[getKeySetting("keymap_jump")] =
\r
805 !keydown[getKeySetting("keymap_jump")];
\r
809 static float counter1 = 0;
\r
813 counter1 = 0.1*Rand(1, 40);
\r
814 keydown[getKeySetting("keymap_special1")] =
\r
815 !keydown[getKeySetting("keymap_special1")];
\r
819 static float counter1 = 0;
\r
823 counter1 = 0.1*Rand(1, 40);
\r
824 keydown[getKeySetting("keymap_forward")] =
\r
825 !keydown[getKeySetting("keymap_forward")];
\r
829 static float counter1 = 0;
\r
833 counter1 = 0.1*Rand(1, 40);
\r
834 keydown[getKeySetting("keymap_left")] =
\r
835 !keydown[getKeySetting("keymap_left")];
\r
839 static float counter1 = 0;
\r
843 counter1 = 0.1*Rand(1, 20);
\r
844 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
848 static float counter1 = 0;
\r
852 counter1 = 0.1*Rand(1, 30);
\r
853 leftdown = !leftdown;
\r
855 leftclicked = true;
\r
857 leftreleased = true;
\r
861 static float counter1 = 0;
\r
865 counter1 = 0.1*Rand(1, 15);
\r
866 rightdown = !rightdown;
\r
868 rightclicked = true;
\r
870 rightreleased = true;
\r
873 mousepos += mousespeed;
\r
876 s32 Rand(s32 min, s32 max)
\r
878 return (myrand()%(max-min+1))+min;
\r
881 bool keydown[KEY_KEY_CODES_COUNT];
\r
889 bool rightreleased;
\r
892 // These are defined global so that they're not optimized too much.
\r
893 // Can't change them to volatile.
\r
898 std::string tempstring;
\r
899 std::string tempstring2;
\r
904 dstream<<"The following test should take around 20ms."<<std::endl;
\r
905 TimeTaker timer("Testing std::string speed");
\r
906 const u32 jj = 10000;
\r
907 for(u32 j=0; j<jj; j++)
\r
912 for(u32 i=0; i<ii; i++){
\r
913 tempstring2 += "asd";
\r
915 for(u32 i=0; i<ii+1; i++){
\r
916 tempstring += "asd";
\r
917 if(tempstring == tempstring2)
\r
923 dstream<<"All of the following tests should take around 100ms each."
\r
927 TimeTaker timer("Testing floating-point conversion speed");
\r
929 for(u32 i=0; i<4000000; i++){
\r
936 TimeTaker timer("Testing floating-point vector speed");
\r
938 tempv3f1 = v3f(1,2,3);
\r
939 tempv3f2 = v3f(4,5,6);
\r
940 for(u32 i=0; i<10000000; i++){
\r
941 tempf += tempv3f1.dotProduct(tempv3f2);
\r
942 tempv3f2 += v3f(7,8,9);
\r
947 TimeTaker timer("Testing core::map speed");
\r
949 core::map<v2s16, f32> map1;
\r
952 for(s16 y=0; y<ii; y++){
\r
953 for(s16 x=0; x<ii; x++){
\r
954 map1.insert(v2s16(x,y), tempf);
\r
958 for(s16 y=ii-1; y>=0; y--){
\r
959 for(s16 x=0; x<ii; x++){
\r
960 tempf = map1[v2s16(x,y)];
\r
966 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
967 TimeTaker timer("Testing mutex speed");
\r
980 // Do at least 10ms
\r
981 while(timer.getTime() < 10);
\r
983 u32 dtime = timer.stop();
\r
984 u32 per_ms = n / dtime;
\r
985 std::cout<<"Done. "<<dtime<<"ms, "
\r
986 <<per_ms<<"/ms"<<std::endl;
\r
990 void drawMenuBackground(video::IVideoDriver* driver)
\r
992 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
994 video::ITexture *bgtexture =
\r
995 driver->getTexture(getTexturePath("mud.png").c_str());
\r
998 s32 texturesize = 128;
\r
999 s32 tiled_y = screensize.Height / texturesize + 1;
\r
1000 s32 tiled_x = screensize.Width / texturesize + 1;
\r
1002 for(s32 y=0; y<tiled_y; y++)
\r
1003 for(s32 x=0; x<tiled_x; x++)
\r
1005 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
1006 rect += v2s32(x*texturesize, y*texturesize);
\r
1007 driver->draw2DImage(bgtexture, rect,
\r
1008 core::rect<s32>(core::position2d<s32>(0,0),
\r
1009 core::dimension2di(bgtexture->getSize())),
\r
1010 NULL, NULL, true);
\r
1014 video::ITexture *logotexture =
\r
1015 driver->getTexture(getTexturePath("menulogo.png").c_str());
\r
1018 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
1019 logotexture->getOriginalSize().Height);
\r
1022 video::SColor bgcolor(255,50,50,50);
\r
1023 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
1024 screensize.Width, screensize.Height);
\r
1025 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
1027 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
1028 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
1029 rect -= v2s32(logosize.X/2, 0);
\r
1030 driver->draw2DImage(logotexture, rect,
\r
1031 core::rect<s32>(core::position2d<s32>(0,0),
\r
1032 core::dimension2di(logotexture->getSize())),
\r
1033 NULL, NULL, true);
\r
1037 int main(int argc, char *argv[])
\r
1043 // Set locale. This is for forcing '.' as the decimal point.
\r
1044 std::locale::global(std::locale("C"));
\r
1045 // This enables printing all characters in bitmap font
\r
1046 setlocale(LC_CTYPE, "en_US");
\r
1049 Parse command line
\r
1052 // List all allowed options
\r
1053 core::map<std::string, ValueSpec> allowed_options;
\r
1054 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1055 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1056 "Run server directly"));
\r
1057 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1058 "Load configuration from specified file"));
\r
1059 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1060 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1061 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1062 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1063 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1064 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1066 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1068 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1070 Settings cmd_args;
\r
1072 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1074 if(ret == false || cmd_args.getFlag("help"))
\r
1076 dstream<<"Allowed options:"<<std::endl;
\r
1077 for(core::map<std::string, ValueSpec>::Iterator
\r
1078 i = allowed_options.getIterator();
\r
1079 i.atEnd() == false; i++)
\r
1081 dstream<<" --"<<i.getNode()->getKey();
\r
1082 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1087 dstream<<" <value>";
\r
1089 dstream<<std::endl;
\r
1091 if(i.getNode()->getValue().help != NULL)
\r
1093 dstream<<" "<<i.getNode()->getValue().help
\r
1098 return cmd_args.getFlag("help") ? 0 : 1;
\r
1102 Low-level initialization
\r
1105 bool disable_stderr = false;
\r
1107 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1108 disable_stderr = true;
\r
1111 porting::signal_handler_init();
\r
1112 bool &kill = *porting::signal_handler_killstatus();
\r
1114 // Initialize porting::path_data and porting::path_userdata
\r
1115 porting::initializePaths();
\r
1117 // Initialize debug streams
\r
1118 #ifdef RUN_IN_PLACE
\r
1119 std::string debugfile = DEBUGFILE;
\r
1121 std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
\r
1123 debugstreams_init(disable_stderr, debugfile.c_str());
\r
1124 // Initialize debug stacks
\r
1125 debug_stacks_init();
\r
1127 DSTACK(__FUNCTION_NAME);
\r
1129 // Create user data directory
\r
1130 fs::CreateDir(porting::path_userdata);
\r
1132 // Init material properties table
\r
1133 initializeMaterialProperties();
\r
1136 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1138 // Print startup message
\r
1139 dstream<<DTIME<<"minetest-c55"
\r
1140 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1141 <<", "<<BUILD_INFO
\r
1145 Basic initialization
\r
1148 // Initialize default settings
\r
1149 set_default_settings();
\r
1151 // Initialize sockets
\r
1153 atexit(sockets_cleanup);
\r
1159 // Path of configuration file in use
\r
1160 std::string configpath = "";
\r
1162 if(cmd_args.exists("config"))
\r
1164 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1167 dstream<<"Could not read configuration from \""
\r
1168 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1171 configpath = cmd_args.get("config");
\r
1175 core::array<std::string> filenames;
\r
1176 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1177 #ifdef RUN_IN_PLACE
\r
1178 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1181 for(u32 i=0; i<filenames.size(); i++)
\r
1183 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1186 configpath = filenames[i];
\r
1191 // If no path found, use the first one (menu creates the file)
\r
1192 if(configpath == "")
\r
1193 configpath = filenames[0];
\r
1196 // Initialize random seed
\r
1201 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1203 These are needed for unit tests at least.
\r
1206 // Initial call with g_texturesource not set.
\r
1213 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1214 || cmd_args.getFlag("enable-unittests") == true)
\r
1219 /*for(s16 y=-100; y<100; y++)
\r
1220 for(s16 x=-100; x<100; x++)
\r
1222 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1232 if(cmd_args.exists("port"))
\r
1233 port = cmd_args.getU16("port");
\r
1234 else if(g_settings.exists("port"))
\r
1235 port = g_settings.getU16("port");
\r
1240 std::string map_dir = porting::path_userdata+"/world";
\r
1241 if(cmd_args.exists("map-dir"))
\r
1242 map_dir = cmd_args.get("map-dir");
\r
1243 else if(g_settings.exists("map-dir"))
\r
1244 map_dir = g_settings.get("map-dir");
\r
1246 // Run dedicated server if asked to
\r
1247 if(cmd_args.getFlag("server"))
\r
1249 DSTACK("Dedicated server branch");
\r
1251 // Create time getter
\r
1252 g_timegetter = new SimpleTimeGetter();
\r
1255 Server server(map_dir.c_str());
\r
1256 server.start(port);
\r
1259 dedicated_server_loop(server, kill);
\r
1269 // Address to connect to
\r
1270 std::string address = "";
\r
1272 if(cmd_args.exists("address"))
\r
1274 address = cmd_args.get("address");
\r
1278 address = g_settings.get("address");
\r
1281 std::string playername = g_settings.get("name");
\r
1284 Device initialization
\r
1287 // Resolution selection
\r
1289 bool fullscreen = false;
\r
1290 u16 screenW = g_settings.getU16("screenW");
\r
1291 u16 screenH = g_settings.getU16("screenH");
\r
1293 // Determine driver
\r
1295 video::E_DRIVER_TYPE driverType;
\r
1297 std::string driverstring = g_settings.get("video_driver");
\r
1299 if(driverstring == "null")
\r
1300 driverType = video::EDT_NULL;
\r
1301 else if(driverstring == "software")
\r
1302 driverType = video::EDT_SOFTWARE;
\r
1303 else if(driverstring == "burningsvideo")
\r
1304 driverType = video::EDT_BURNINGSVIDEO;
\r
1305 else if(driverstring == "direct3d8")
\r
1306 driverType = video::EDT_DIRECT3D8;
\r
1307 else if(driverstring == "direct3d9")
\r
1308 driverType = video::EDT_DIRECT3D9;
\r
1309 else if(driverstring == "opengl")
\r
1310 driverType = video::EDT_OPENGL;
\r
1313 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1314 "to opengl"<<std::endl;
\r
1315 driverType = video::EDT_OPENGL;
\r
1319 Create device and exit if creation failed
\r
1322 MyEventReceiver receiver;
\r
1324 IrrlichtDevice *device;
\r
1325 device = createDevice(driverType,
\r
1326 core::dimension2d<u32>(screenW, screenH),
\r
1327 16, fullscreen, false, false, &receiver);
\r
1330 return 1; // could not create selected driver.
\r
1332 // Set device in game parameters
\r
1335 // Create time getter
\r
1336 g_timegetter = new IrrlichtTimeGetter(device);
\r
1338 // Create game callback for menus
\r
1339 g_gamecallback = new MainGameCallback(device);
\r
1341 // Create texture source
\r
1342 g_texturesource = new TextureSource(device);
\r
1345 Speed tests (done after irrlicht is loaded to get timer)
\r
1347 if(cmd_args.getFlag("speedtests"))
\r
1349 dstream<<"Running speed tests"<<std::endl;
\r
1354 device->setResizable(true);
\r
1356 bool random_input = g_settings.getBool("random_input")
\r
1357 || cmd_args.getFlag("random-input");
\r
1358 InputHandler *input = NULL;
\r
1360 input = new RandomInputHandler();
\r
1362 input = new RealInputHandler(device, &receiver);
\r
1365 Continue initialization
\r
1368 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1371 This changes the minimum allowed number of vertices in a VBO.
\r
1374 //driver->setMinHardwareBufferVertexCount(50);
\r
1376 scene::ISceneManager* smgr = device->getSceneManager();
\r
1378 guienv = device->getGUIEnvironment();
\r
1379 gui::IGUISkin* skin = guienv->getSkin();
\r
1380 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
\r
1382 skin->setFont(font);
\r
1384 dstream<<"WARNING: Font file was not found."
\r
1385 " Using default font."<<std::endl;
\r
1386 // If font was not found, this will get us one
\r
1387 font = skin->getFont();
\r
1390 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1391 dstream<<"text_height="<<text_height<<std::endl;
\r
1393 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1394 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1395 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1396 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1397 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1398 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1401 Preload some textures and stuff
\r
1404 init_content_inventory_texture_paths();
\r
1405 init_mapnode(); // Second call with g_texturesource set
\r
1413 If an error occurs, this is set to something and the
\r
1414 menu-game loop is restarted. It is then displayed before
\r
1417 std::wstring error_message = L"";
\r
1419 // The password entered during the menu screen,
\r
1420 std::string password;
\r
1425 while(device->run() && kill == false)
\r
1428 // This is used for catching disconnects
\r
1433 Clear everything from the GUIEnvironment
\r
1438 We need some kind of a root node to be able to add
\r
1439 custom gui elements directly on the screen.
\r
1440 Otherwise they won't be automatically drawn.
\r
1442 guiroot = guienv->addStaticText(L"",
\r
1443 core::rect<s32>(0, 0, 10000, 10000));
\r
1446 Out-of-game menu loop.
\r
1448 Loop quits when menu returns proper parameters.
\r
1450 while(kill == false)
\r
1452 // Cursor can be non-visible when coming from the game
\r
1453 device->getCursorControl()->setVisible(true);
\r
1454 // Some stuff are left to scene manager when coming from the game
\r
1455 // (map at least?)
\r
1457 // Reset or hide the debug gui texts
\r
1458 /*guitext->setText(L"Minetest-c55");
\r
1459 guitext2->setVisible(false);
\r
1460 guitext_info->setVisible(false);
\r
1461 guitext_chat->setVisible(false);*/
\r
1463 // Initialize menu data
\r
1464 MainMenuData menudata;
\r
1465 menudata.address = narrow_to_wide(address);
\r
1466 menudata.name = narrow_to_wide(playername);
\r
1467 menudata.port = narrow_to_wide(itos(port));
\r
1468 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1469 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1470 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1471 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1473 GUIMainMenu *menu =
\r
1474 new GUIMainMenu(guienv, guiroot, -1,
\r
1475 &g_menumgr, &menudata, g_gamecallback);
\r
1476 menu->allowFocusRemoval(true);
\r
1478 if(error_message != L"")
\r
1480 dstream<<"WARNING: error_message = "
\r
1481 <<wide_to_narrow(error_message)<<std::endl;
\r
1483 GUIMessageMenu *menu2 =
\r
1484 new GUIMessageMenu(guienv, guiroot, -1,
\r
1485 &g_menumgr, error_message.c_str());
\r
1487 error_message = L"";
\r
1490 video::IVideoDriver* driver = device->getVideoDriver();
\r
1492 dstream<<"Created main menu"<<std::endl;
\r
1494 while(device->run() && kill == false)
\r
1496 if(menu->getStatus() == true)
\r
1499 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1500 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1502 drawMenuBackground(driver);
\r
1504 guienv->drawAll();
\r
1506 driver->endScene();
\r
1508 // On some computers framerate doesn't seem to be
\r
1509 // automatically limited
\r
1513 // Break out of menu-game loop to shut down cleanly
\r
1514 if(device->run() == false || kill == true)
\r
1517 dstream<<"Dropping main menu"<<std::endl;
\r
1521 // Delete map if requested
\r
1522 if(menudata.delete_map)
\r
1524 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1526 error_message = L"Delete failed";
\r
1530 playername = wide_to_narrow(menudata.name);
\r
1532 password = translatePassword(playername, menudata.password);
\r
1534 address = wide_to_narrow(menudata.address);
\r
1535 int newport = stoi(wide_to_narrow(menudata.port));
\r
1538 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1539 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1540 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1541 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1543 // NOTE: These are now checked server side; no need to do it
\r
1544 // here, so let's not do it here.
\r
1545 /*// Check for valid parameters, restart menu if invalid.
\r
1546 if(playername == "")
\r
1548 error_message = L"Name required.";
\r
1551 // Check that name has only valid chars
\r
1552 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
\r
1554 error_message = L"Characters allowed: "
\r
1555 +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
\r
1560 g_settings.set("name", playername);
\r
1561 g_settings.set("address", address);
\r
1562 g_settings.set("port", itos(port));
\r
1563 // Update configuration file
\r
1564 if(configpath != "")
\r
1565 g_settings.updateConfigFile(configpath.c_str());
\r
1567 // Continue to game
\r
1571 // Break out of menu-game loop to shut down cleanly
\r
1572 if(device->run() == false)
\r
1575 // Initialize mapnode again to enable changed graphics settings
\r
1596 catch(con::PeerNotFoundException &e)
\r
1598 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1599 error_message = L"Connection error (timed out?)";
\r
1601 catch(SocketException &e)
\r
1603 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1604 error_message = L"Socket error (port already in use?)";
\r
1607 catch(std::exception &e)
\r
1609 std::string narrow_message = "Some exception, what()=\"";
\r
1610 narrow_message += e.what();
\r
1611 narrow_message += "\"";
\r
1612 dstream<<DTIME<<narrow_message<<std::endl;
\r
1613 error_message = narrow_to_wide(narrow_message);
\r
1617 } // Menu-game loop
\r
1622 In the end, delete the Irrlicht device.
\r
1626 END_DEBUG_EXCEPTION_HANDLER
\r
1628 debugstreams_deinit();
\r