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
183 TODO: Untie client network operations from framerate
\r
184 - Needs some input queues or something
\r
185 - This won't give much performance boost because calculating block
\r
186 meshes takes so long
\r
188 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
190 TODO: Don't update all meshes always on single node changes, but
\r
191 check which ones should be updated
\r
192 - implement Map::updateNodeMeshes() and the usage of it
\r
193 - It will give almost always a 4x boost in mesh update performance.
\r
197 - Tool/weapon visualization
\r
199 FIXME: When disconnected to the menu, memory is not freed properly
\r
204 SUGG: Make an option to the server to disable building and digging near
\r
205 the starting position
\r
207 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
209 * Fix the problem with the server constantly saving one or a few
\r
210 blocks? List the first saved block, maybe it explains.
\r
211 - It is probably caused by oscillating water
\r
212 * Make a small history check to transformLiquids to detect and log
\r
213 continuous oscillations, in such detail that they can be fixed.
\r
215 FIXME: The new optimized map sending doesn't sometimes send enough blocks
\r
216 from big caves and such
\r
221 TODO: A list of "active blocks" in which stuff happens.
\r
222 + Add a never-resetted game timer to the server
\r
223 + Add a timestamp value to blocks
\r
224 + The simple rule: All blocks near some player are "active"
\r
225 - Do stuff in real time in active blocks
\r
227 TODO: Make proper hooks in here
\r
228 - Grow grass, delete leaves without a tree
\r
229 - Spawn some mobs based on some rules
\r
230 - Transform cobble to mossy cobble near water
\r
231 - Run a custom script
\r
232 - ...And all kinds of other dynamic stuff
\r
233 + Keep track of when a block becomes active and becomes inactive
\r
234 + When a block goes inactive:
\r
235 + Store objects statically to block
\r
236 + Store timer value as the timestamp
\r
237 + When a block goes active:
\r
238 + Create active objects out of static objects
\r
239 TODO: Make proper hooks in here
\r
240 - Simulate the results of what would have happened if it would have
\r
241 been active for all the time
\r
242 - Grow a lot of grass and so on
\r
243 + Initially it is fine to send information about every active object
\r
244 to every player. Eventually it should be modified to only send info
\r
245 about the nearest ones.
\r
246 + This was left to be done by the old system and it sends only the
\r
252 TODO: Get rid of MapBlockObjects and use only ActiveObjects
\r
253 - Skipping the MapBlockObject data is nasty - there is no "total
\r
254 length" stored; have to make a SkipMBOs function which contains
\r
255 enough of the current code to skip them properly.
\r
257 SUGG: MovingObject::move and Player::move are basically the same.
\r
259 - NOTE: Player::move is more up-to-date.
\r
260 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
261 - NOTE: MovingObject will be deleted (MapBlockObject)
\r
263 TODO: Add a long step function to objects that is called with the time
\r
264 difference when block activates
\r
269 TODO: Mineral and ground material properties
\r
270 - This way mineral ground toughness can be calculated with just
\r
271 some formula, as well as tool strengths
\r
273 TODO: Flowing water to actually contain flow direction information
\r
274 - There is a space for this - it just has to be implemented.
\r
276 SUGG: Erosion simulation at map generation time
\r
277 - Simulate water flows, which would carve out dirt fast and
\r
278 then turn stone into gravel and sand and relocate it.
\r
279 - How about relocating minerals, too? Coal and gold in
\r
280 downstream sand and gravel would be kind of cool
\r
281 - This would need a better way of handling minerals, mainly
\r
282 to have mineral content as a separate field. the first
\r
283 parameter field is free for this.
\r
284 - Simulate rock falling from cliffs when water has removed
\r
285 enough solid rock from the bottom
\r
287 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
288 of low-res 3d noise and interpolate linearly.
\r
291 * Possibly add some kind of erosion and other stuff
\r
292 * Better water generation (spread it to underwater caverns but don't
\r
293 fill dungeons that don't touch big water masses)
\r
294 * When generating a chunk and the neighboring chunk doesn't have mud
\r
295 and stuff yet and the ground is fairly flat, the mud will flow to
\r
296 the other chunk making nasty straight walls when the other chunk
\r
297 is generated. Fix it. Maybe just a special case if the ground is
\r
302 * Move digging property stuff from material.{h,cpp} to mapnode.cpp
\r
303 - ...Or maybe move content_features to material.{h,cpp}?
\r
305 Making it more portable:
\r
306 ------------------------
\r
308 Stuff to do before release:
\r
309 ---------------------------
\r
310 - Make grass grow slower; utilize timestamp difference
\r
312 Stuff to do after release:
\r
313 ---------------------------
\r
314 - Make sure server handles removing grass when a block is placed (etc)
\r
315 - The client should not do it by itself
\r
316 - Block cube placement around player's head
\r
317 - Protocol version field
\r
318 - Consider getting some textures from cisoun's texture pack
\r
320 - Make sure the fence implementation and data format is good
\r
321 - Think about using same bits for material for fences and doors, for
\r
323 - Finish the ActiveBlockModifier stuff and use it for something
\r
324 - Move mineral to param2, increment map serialization version, add conversion
\r
326 ======================================================================
\r
332 #pragma message ("Disabling unit tests")
\r
334 #warning "Disabling unit tests"
\r
336 // Disable unit tests
\r
337 #define ENABLE_TESTS 0
\r
339 // Enable unit tests
\r
340 #define ENABLE_TESTS 1
\r
344 #pragma comment(lib, "Irrlicht.lib")
\r
345 //#pragma comment(lib, "jthread.lib")
\r
346 #pragma comment(lib, "zlibwapi.lib")
\r
347 #pragma comment(lib, "Shell32.lib")
\r
348 // This would get rid of the console window
\r
349 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
352 #include <iostream>
\r
354 //#include <jmutexautolock.h>
\r
355 #include <locale.h>
\r
357 #include "common_irrlicht.h"
\r
360 //#include "player.h"
\r
362 #include "server.h"
\r
363 //#include "client.h"
\r
364 #include "constants.h"
\r
365 #include "porting.h"
\r
366 #include "gettime.h"
\r
367 #include "guiMessageMenu.h"
\r
368 #include "filesys.h"
\r
369 #include "config.h"
\r
370 #include "guiMainMenu.h"
\r
371 #include "mineral.h"
\r
372 //#include "noise.h"
\r
373 //#include "tile.h"
\r
374 #include "materials.h"
\r
376 #include "keycode.h"
\r
378 // This makes textures
\r
379 ITextureSource *g_texturesource = NULL;
\r
383 These are loaded from the config file.
\r
386 Settings g_settings;
\r
387 // This is located in defaultsettings.cpp
\r
388 extern void set_default_settings();
\r
398 gui::IGUIEnvironment* guienv = NULL;
\r
399 gui::IGUIStaticText *guiroot = NULL;
\r
401 MainMenuManager g_menumgr;
\r
403 bool noMenuActive()
\r
405 return (g_menumgr.menuCount() == 0);
\r
408 // Passed to menus to allow disconnecting and exiting
\r
410 MainGameCallback *g_gamecallback = NULL;
\r
417 std::ostream *dout_con_ptr = &dummyout;
\r
418 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
419 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
420 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
421 //std::ostream *dout_con_ptr = &dstream;
\r
422 //std::ostream *derr_con_ptr = &dstream;
\r
425 std::ostream *dout_server_ptr = &dstream;
\r
426 std::ostream *derr_server_ptr = &dstream;
\r
429 std::ostream *dout_client_ptr = &dstream;
\r
430 std::ostream *derr_client_ptr = &dstream;
\r
433 gettime.h implementation
\r
436 // A small helper class
\r
440 TimeGetter(IrrlichtDevice *device):
\r
445 if(m_device == NULL)
\r
447 return m_device->getTimer()->getRealTime();
\r
450 IrrlichtDevice *m_device;
\r
453 // A pointer to a global instance of the time getter
\r
454 TimeGetter *g_timegetter = NULL;
\r
458 if(g_timegetter == NULL)
\r
460 return g_timegetter->getTime();
\r
464 Event handler for Irrlicht
\r
466 NOTE: Everything possible should be moved out from here,
\r
467 probably to InputHandler and the_game
\r
470 class MyEventReceiver : public IEventReceiver
\r
473 // This is the one method that we have to implement
\r
474 virtual bool OnEvent(const SEvent& event)
\r
477 React to nothing here if a menu is active
\r
479 if(noMenuActive() == false)
\r
484 // Remember whether each key is down or up
\r
485 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
487 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
489 if(event.KeyInput.PressedDown)
\r
490 keyWasDown[event.KeyInput.Key] = true;
\r
493 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
495 if(noMenuActive() == false)
\r
497 left_active = false;
\r
498 middle_active = false;
\r
499 right_active = false;
\r
503 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
504 left_active = event.MouseInput.isLeftPressed();
\r
505 middle_active = event.MouseInput.isMiddlePressed();
\r
506 right_active = event.MouseInput.isRightPressed();
\r
508 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
510 leftclicked = true;
\r
512 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
514 rightclicked = true;
\r
516 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
518 leftreleased = true;
\r
520 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
522 rightreleased = true;
\r
524 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
526 mouse_wheel += event.MouseInput.Wheel;
\r
534 bool IsKeyDown(EKEY_CODE keyCode) const
\r
536 return keyIsDown[keyCode];
\r
539 // Checks whether a key was down and resets the state
\r
540 bool WasKeyDown(EKEY_CODE keyCode)
\r
542 bool b = keyWasDown[keyCode];
\r
543 keyWasDown[keyCode] = false;
\r
547 s32 getMouseWheel()
\r
549 s32 a = mouse_wheel;
\r
556 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
558 keyIsDown[i] = false;
\r
559 keyWasDown[i] = false;
\r
562 leftclicked = false;
\r
563 rightclicked = false;
\r
564 leftreleased = false;
\r
565 rightreleased = false;
\r
567 left_active = false;
\r
568 middle_active = false;
\r
569 right_active = false;
\r
582 bool rightreleased;
\r
585 bool middle_active;
\r
591 IrrlichtDevice *m_device;
\r
593 // The current state of keys
\r
594 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
595 // Whether a key has been pressed or not
\r
596 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
600 Separated input handler
\r
603 class RealInputHandler : public InputHandler
\r
606 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
608 m_receiver(receiver)
\r
611 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
613 return m_receiver->IsKeyDown(keyCode);
\r
615 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
617 return m_receiver->WasKeyDown(keyCode);
\r
619 virtual v2s32 getMousePos()
\r
621 return m_device->getCursorControl()->getPosition();
\r
623 virtual void setMousePos(s32 x, s32 y)
\r
625 m_device->getCursorControl()->setPosition(x, y);
\r
628 virtual bool getLeftState()
\r
630 return m_receiver->left_active;
\r
632 virtual bool getRightState()
\r
634 return m_receiver->right_active;
\r
637 virtual bool getLeftClicked()
\r
639 return m_receiver->leftclicked;
\r
641 virtual bool getRightClicked()
\r
643 return m_receiver->rightclicked;
\r
645 virtual void resetLeftClicked()
\r
647 m_receiver->leftclicked = false;
\r
649 virtual void resetRightClicked()
\r
651 m_receiver->rightclicked = false;
\r
654 virtual bool getLeftReleased()
\r
656 return m_receiver->leftreleased;
\r
658 virtual bool getRightReleased()
\r
660 return m_receiver->rightreleased;
\r
662 virtual void resetLeftReleased()
\r
664 m_receiver->leftreleased = false;
\r
666 virtual void resetRightReleased()
\r
668 m_receiver->rightreleased = false;
\r
671 virtual s32 getMouseWheel()
\r
673 return m_receiver->getMouseWheel();
\r
678 m_receiver->clearInput();
\r
681 IrrlichtDevice *m_device;
\r
682 MyEventReceiver *m_receiver;
\r
685 class RandomInputHandler : public InputHandler
\r
688 RandomInputHandler()
\r
692 leftclicked = false;
\r
693 rightclicked = false;
\r
694 leftreleased = false;
\r
695 rightreleased = false;
\r
696 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
697 keydown[i] = false;
\r
699 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
701 return keydown[keyCode];
\r
703 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
707 virtual v2s32 getMousePos()
\r
711 virtual void setMousePos(s32 x, s32 y)
\r
713 mousepos = v2s32(x,y);
\r
716 virtual bool getLeftState()
\r
720 virtual bool getRightState()
\r
725 virtual bool getLeftClicked()
\r
727 return leftclicked;
\r
729 virtual bool getRightClicked()
\r
731 return rightclicked;
\r
733 virtual void resetLeftClicked()
\r
735 leftclicked = false;
\r
737 virtual void resetRightClicked()
\r
739 rightclicked = false;
\r
742 virtual bool getLeftReleased()
\r
744 return leftreleased;
\r
746 virtual bool getRightReleased()
\r
748 return rightreleased;
\r
750 virtual void resetLeftReleased()
\r
752 leftreleased = false;
\r
754 virtual void resetRightReleased()
\r
756 rightreleased = false;
\r
759 virtual s32 getMouseWheel()
\r
764 virtual void step(float dtime)
\r
767 static float counter1 = 0;
\r
771 counter1 = 0.1*Rand(1, 40);
\r
772 keydown[getKeySetting("keymap_jump")] =
\r
773 !keydown[getKeySetting("keymap_jump")];
\r
777 static float counter1 = 0;
\r
781 counter1 = 0.1*Rand(1, 40);
\r
782 keydown[getKeySetting("keymap_special1")] =
\r
783 !keydown[getKeySetting("keymap_special1")];
\r
787 static float counter1 = 0;
\r
791 counter1 = 0.1*Rand(1, 40);
\r
792 keydown[getKeySetting("keymap_forward")] =
\r
793 !keydown[getKeySetting("keymap_forward")];
\r
797 static float counter1 = 0;
\r
801 counter1 = 0.1*Rand(1, 40);
\r
802 keydown[getKeySetting("keymap_left")] =
\r
803 !keydown[getKeySetting("keymap_left")];
\r
807 static float counter1 = 0;
\r
811 counter1 = 0.1*Rand(1, 20);
\r
812 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
816 static float counter1 = 0;
\r
820 counter1 = 0.1*Rand(1, 30);
\r
821 leftdown = !leftdown;
\r
823 leftclicked = true;
\r
825 leftreleased = true;
\r
829 static float counter1 = 0;
\r
833 counter1 = 0.1*Rand(1, 15);
\r
834 rightdown = !rightdown;
\r
836 rightclicked = true;
\r
838 rightreleased = true;
\r
841 mousepos += mousespeed;
\r
844 s32 Rand(s32 min, s32 max)
\r
846 return (myrand()%(max-min+1))+min;
\r
849 bool keydown[KEY_KEY_CODES_COUNT];
\r
857 bool rightreleased;
\r
860 // These are defined global so that they're not optimized too much.
\r
861 // Can't change them to volatile.
\r
866 std::string tempstring;
\r
867 std::string tempstring2;
\r
872 dstream<<"The following test should take around 20ms."<<std::endl;
\r
873 TimeTaker timer("Testing std::string speed");
\r
874 const u32 jj = 10000;
\r
875 for(u32 j=0; j<jj; j++)
\r
880 for(u32 i=0; i<ii; i++){
\r
881 tempstring2 += "asd";
\r
883 for(u32 i=0; i<ii+1; i++){
\r
884 tempstring += "asd";
\r
885 if(tempstring == tempstring2)
\r
891 dstream<<"All of the following tests should take around 100ms each."
\r
895 TimeTaker timer("Testing floating-point conversion speed");
\r
897 for(u32 i=0; i<4000000; i++){
\r
904 TimeTaker timer("Testing floating-point vector speed");
\r
906 tempv3f1 = v3f(1,2,3);
\r
907 tempv3f2 = v3f(4,5,6);
\r
908 for(u32 i=0; i<10000000; i++){
\r
909 tempf += tempv3f1.dotProduct(tempv3f2);
\r
910 tempv3f2 += v3f(7,8,9);
\r
915 TimeTaker timer("Testing core::map speed");
\r
917 core::map<v2s16, f32> map1;
\r
920 for(s16 y=0; y<ii; y++){
\r
921 for(s16 x=0; x<ii; x++){
\r
922 map1.insert(v2s16(x,y), tempf);
\r
926 for(s16 y=ii-1; y>=0; y--){
\r
927 for(s16 x=0; x<ii; x++){
\r
928 tempf = map1[v2s16(x,y)];
\r
934 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
935 TimeTaker timer("Testing mutex speed");
\r
948 // Do at least 10ms
\r
949 while(timer.getTime() < 10);
\r
951 u32 dtime = timer.stop();
\r
952 u32 per_ms = n / dtime;
\r
953 std::cout<<"Done. "<<dtime<<"ms, "
\r
954 <<per_ms<<"/ms"<<std::endl;
\r
958 void drawMenuBackground(video::IVideoDriver* driver)
\r
960 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
962 video::ITexture *bgtexture =
\r
963 driver->getTexture(getTexturePath("mud.png").c_str());
\r
966 s32 texturesize = 128;
\r
967 s32 tiled_y = screensize.Height / texturesize + 1;
\r
968 s32 tiled_x = screensize.Width / texturesize + 1;
\r
970 for(s32 y=0; y<tiled_y; y++)
\r
971 for(s32 x=0; x<tiled_x; x++)
\r
973 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
974 rect += v2s32(x*texturesize, y*texturesize);
\r
975 driver->draw2DImage(bgtexture, rect,
\r
976 core::rect<s32>(core::position2d<s32>(0,0),
\r
977 core::dimension2di(bgtexture->getSize())),
\r
982 video::ITexture *logotexture =
\r
983 driver->getTexture(getTexturePath("menulogo.png").c_str());
\r
986 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
987 logotexture->getOriginalSize().Height);
\r
990 video::SColor bgcolor(255,50,50,50);
\r
991 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
992 screensize.Width, screensize.Height);
\r
993 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
995 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
996 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
997 rect -= v2s32(logosize.X/2, 0);
\r
998 driver->draw2DImage(logotexture, rect,
\r
999 core::rect<s32>(core::position2d<s32>(0,0),
\r
1000 core::dimension2di(logotexture->getSize())),
\r
1001 NULL, NULL, true);
\r
1005 int main(int argc, char *argv[])
\r
1008 Parse command line
\r
1011 // List all allowed options
\r
1012 core::map<std::string, ValueSpec> allowed_options;
\r
1013 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1014 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1015 "Run server directly"));
\r
1016 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1017 "Load configuration from specified file"));
\r
1018 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1019 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1020 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1021 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1022 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1023 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1025 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1027 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1029 Settings cmd_args;
\r
1031 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1033 if(ret == false || cmd_args.getFlag("help"))
\r
1035 dstream<<"Allowed options:"<<std::endl;
\r
1036 for(core::map<std::string, ValueSpec>::Iterator
\r
1037 i = allowed_options.getIterator();
\r
1038 i.atEnd() == false; i++)
\r
1040 dstream<<" --"<<i.getNode()->getKey();
\r
1041 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1046 dstream<<" <value>";
\r
1048 dstream<<std::endl;
\r
1050 if(i.getNode()->getValue().help != NULL)
\r
1052 dstream<<" "<<i.getNode()->getValue().help
\r
1057 return cmd_args.getFlag("help") ? 0 : 1;
\r
1061 Low-level initialization
\r
1064 bool disable_stderr = false;
\r
1066 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1067 disable_stderr = true;
\r
1070 // Initialize debug streams
\r
1071 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1072 // Initialize debug stacks
\r
1073 debug_stacks_init();
\r
1075 DSTACK(__FUNCTION_NAME);
\r
1077 porting::signal_handler_init();
\r
1078 bool &kill = *porting::signal_handler_killstatus();
\r
1080 porting::initializePaths();
\r
1081 // Create user data directory
\r
1082 fs::CreateDir(porting::path_userdata);
\r
1084 // C-style stuff initialization
\r
1085 initializeMaterialProperties();
\r
1088 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1090 // Print startup message
\r
1091 dstream<<DTIME<<"minetest-c55"
\r
1092 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1093 <<", "<<BUILD_INFO
\r
1097 Basic initialization
\r
1100 // Initialize default settings
\r
1101 set_default_settings();
\r
1103 // Set locale. This is for forcing '.' as the decimal point.
\r
1104 std::locale::global(std::locale("C"));
\r
1105 // This enables printing all characters in bitmap font
\r
1106 setlocale(LC_CTYPE, "en_US");
\r
1108 // Initialize sockets
\r
1110 atexit(sockets_cleanup);
\r
1120 // Path of configuration file in use
\r
1121 std::string configpath = "";
\r
1123 if(cmd_args.exists("config"))
\r
1125 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1128 dstream<<"Could not read configuration from \""
\r
1129 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1132 configpath = cmd_args.get("config");
\r
1136 core::array<std::string> filenames;
\r
1137 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1138 #ifdef RUN_IN_PLACE
\r
1139 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1142 for(u32 i=0; i<filenames.size(); i++)
\r
1144 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1147 configpath = filenames[i];
\r
1152 // If no path found, use the first one (menu creates the file)
\r
1153 if(configpath == "")
\r
1154 configpath = filenames[0];
\r
1157 // Initialize random seed
\r
1162 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1164 These are needed for unit tests at least.
\r
1167 // Initial call with g_texturesource not set.
\r
1174 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1175 || cmd_args.getFlag("enable-unittests") == true)
\r
1180 /*for(s16 y=-100; y<100; y++)
\r
1181 for(s16 x=-100; x<100; x++)
\r
1183 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1193 if(cmd_args.exists("port"))
\r
1194 port = cmd_args.getU16("port");
\r
1195 else if(g_settings.exists("port"))
\r
1196 port = g_settings.getU16("port");
\r
1201 std::string map_dir = porting::path_userdata+"/world";
\r
1202 if(cmd_args.exists("map-dir"))
\r
1203 map_dir = cmd_args.get("map-dir");
\r
1204 else if(g_settings.exists("map-dir"))
\r
1205 map_dir = g_settings.get("map-dir");
\r
1207 // Run dedicated server if asked to
\r
1208 if(cmd_args.getFlag("server"))
\r
1210 DSTACK("Dedicated server branch");
\r
1213 Server server(map_dir.c_str());
\r
1214 server.start(port);
\r
1217 dedicated_server_loop(server, kill);
\r
1227 // Address to connect to
\r
1228 std::string address = "";
\r
1230 if(cmd_args.exists("address"))
\r
1232 address = cmd_args.get("address");
\r
1236 address = g_settings.get("address");
\r
1239 std::string playername = g_settings.get("name");
\r
1242 Device initialization
\r
1245 // Resolution selection
\r
1247 bool fullscreen = false;
\r
1248 u16 screenW = g_settings.getU16("screenW");
\r
1249 u16 screenH = g_settings.getU16("screenH");
\r
1251 // Determine driver
\r
1253 video::E_DRIVER_TYPE driverType;
\r
1255 std::string driverstring = g_settings.get("video_driver");
\r
1257 if(driverstring == "null")
\r
1258 driverType = video::EDT_NULL;
\r
1259 else if(driverstring == "software")
\r
1260 driverType = video::EDT_SOFTWARE;
\r
1261 else if(driverstring == "burningsvideo")
\r
1262 driverType = video::EDT_BURNINGSVIDEO;
\r
1263 else if(driverstring == "direct3d8")
\r
1264 driverType = video::EDT_DIRECT3D8;
\r
1265 else if(driverstring == "direct3d9")
\r
1266 driverType = video::EDT_DIRECT3D9;
\r
1267 else if(driverstring == "opengl")
\r
1268 driverType = video::EDT_OPENGL;
\r
1271 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1272 "to opengl"<<std::endl;
\r
1273 driverType = video::EDT_OPENGL;
\r
1277 Create device and exit if creation failed
\r
1280 MyEventReceiver receiver;
\r
1282 IrrlichtDevice *device;
\r
1283 device = createDevice(driverType,
\r
1284 core::dimension2d<u32>(screenW, screenH),
\r
1285 16, fullscreen, false, false, &receiver);
\r
1288 return 1; // could not create selected driver.
\r
1290 // Set device in game parameters
\r
1293 // Create time getter
\r
1294 g_timegetter = new TimeGetter(device);
\r
1296 // Create game callback for menus
\r
1297 g_gamecallback = new MainGameCallback(device);
\r
1299 // Create texture source
\r
1300 g_texturesource = new TextureSource(device);
\r
1303 Speed tests (done after irrlicht is loaded to get timer)
\r
1305 if(cmd_args.getFlag("speedtests"))
\r
1307 dstream<<"Running speed tests"<<std::endl;
\r
1312 device->setResizable(true);
\r
1314 bool random_input = g_settings.getBool("random_input")
\r
1315 || cmd_args.getFlag("random-input");
\r
1316 InputHandler *input = NULL;
\r
1318 input = new RandomInputHandler();
\r
1320 input = new RealInputHandler(device, &receiver);
\r
1323 Continue initialization
\r
1326 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1329 This changes the minimum allowed number of vertices in a VBO.
\r
1332 //driver->setMinHardwareBufferVertexCount(50);
\r
1334 scene::ISceneManager* smgr = device->getSceneManager();
\r
1336 guienv = device->getGUIEnvironment();
\r
1337 gui::IGUISkin* skin = guienv->getSkin();
\r
1338 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
\r
1340 skin->setFont(font);
\r
1342 dstream<<"WARNING: Font file was not found."
\r
1343 " Using default font."<<std::endl;
\r
1344 // If font was not found, this will get us one
\r
1345 font = skin->getFont();
\r
1348 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1349 dstream<<"text_height="<<text_height<<std::endl;
\r
1351 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1352 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1353 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1354 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1355 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1356 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1359 Preload some textures and stuff
\r
1362 init_content_inventory_texture_paths();
\r
1363 init_mapnode(); // Second call with g_texturesource set
\r
1371 If an error occurs, this is set to something and the
\r
1372 menu-game loop is restarted. It is then displayed before
\r
1375 std::wstring error_message = L"";
\r
1377 // The password entered during the menu screen,
\r
1378 std::string password;
\r
1383 while(device->run() && kill == false)
\r
1386 // This is used for catching disconnects
\r
1391 Clear everything from the GUIEnvironment
\r
1396 We need some kind of a root node to be able to add
\r
1397 custom gui elements directly on the screen.
\r
1398 Otherwise they won't be automatically drawn.
\r
1400 guiroot = guienv->addStaticText(L"",
\r
1401 core::rect<s32>(0, 0, 10000, 10000));
\r
1404 Out-of-game menu loop.
\r
1406 Loop quits when menu returns proper parameters.
\r
1408 while(kill == false)
\r
1410 // Cursor can be non-visible when coming from the game
\r
1411 device->getCursorControl()->setVisible(true);
\r
1412 // Some stuff are left to scene manager when coming from the game
\r
1413 // (map at least?)
\r
1415 // Reset or hide the debug gui texts
\r
1416 /*guitext->setText(L"Minetest-c55");
\r
1417 guitext2->setVisible(false);
\r
1418 guitext_info->setVisible(false);
\r
1419 guitext_chat->setVisible(false);*/
\r
1421 // Initialize menu data
\r
1422 MainMenuData menudata;
\r
1423 menudata.address = narrow_to_wide(address);
\r
1424 menudata.name = narrow_to_wide(playername);
\r
1425 menudata.port = narrow_to_wide(itos(port));
\r
1426 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1427 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1428 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1429 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1431 GUIMainMenu *menu =
\r
1432 new GUIMainMenu(guienv, guiroot, -1,
\r
1433 &g_menumgr, &menudata, g_gamecallback);
\r
1434 menu->allowFocusRemoval(true);
\r
1436 if(error_message != L"")
\r
1438 dstream<<"WARNING: error_message = "
\r
1439 <<wide_to_narrow(error_message)<<std::endl;
\r
1441 GUIMessageMenu *menu2 =
\r
1442 new GUIMessageMenu(guienv, guiroot, -1,
\r
1443 &g_menumgr, error_message.c_str());
\r
1445 error_message = L"";
\r
1448 video::IVideoDriver* driver = device->getVideoDriver();
\r
1450 dstream<<"Created main menu"<<std::endl;
\r
1452 while(device->run() && kill == false)
\r
1454 if(menu->getStatus() == true)
\r
1457 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1458 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1460 drawMenuBackground(driver);
\r
1462 guienv->drawAll();
\r
1464 driver->endScene();
\r
1466 // On some computers framerate doesn't seem to be
\r
1467 // automatically limited
\r
1471 // Break out of menu-game loop to shut down cleanly
\r
1472 if(device->run() == false || kill == true)
\r
1475 dstream<<"Dropping main menu"<<std::endl;
\r
1479 // Delete map if requested
\r
1480 if(menudata.delete_map)
\r
1482 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1484 error_message = L"Delete failed";
\r
1488 playername = wide_to_narrow(menudata.name);
\r
1490 password = translatePassword(playername, menudata.password);
\r
1492 address = wide_to_narrow(menudata.address);
\r
1493 int newport = stoi(wide_to_narrow(menudata.port));
\r
1496 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1497 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1498 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1499 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1501 /*// Check for valid parameters, restart menu if invalid.
\r
1502 if(playername == "")
\r
1504 error_message = L"Name required.";
\r
1507 // Check that name has only valid chars
\r
1508 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
\r
1510 error_message = L"Characters allowed: "
\r
1511 +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
\r
1516 g_settings.set("name", playername);
\r
1517 g_settings.set("address", address);
\r
1518 g_settings.set("port", itos(port));
\r
1519 // Update configuration file
\r
1520 if(configpath != "")
\r
1521 g_settings.updateConfigFile(configpath.c_str());
\r
1523 // Continue to game
\r
1527 // Break out of menu-game loop to shut down cleanly
\r
1528 if(device->run() == false)
\r
1531 // Initialize mapnode again to enable changed graphics settings
\r
1552 catch(con::PeerNotFoundException &e)
\r
1554 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1555 error_message = L"Connection error (timed out?)";
\r
1557 catch(SocketException &e)
\r
1559 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1560 error_message = L"Socket error (port already in use?)";
\r
1563 catch(std::exception &e)
\r
1565 std::string narrow_message = "Some exception, what()=\"";
\r
1566 narrow_message += e.what();
\r
1567 narrow_message += "\"";
\r
1568 dstream<<DTIME<<narrow_message<<std::endl;
\r
1569 error_message = narrow_to_wide(narrow_message);
\r
1573 } // Menu-game loop
\r
1578 In the end, delete the Irrlicht device.
\r
1582 END_DEBUG_EXCEPTION_HANDLER
\r
1584 debugstreams_deinit();
\r