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
266 TODO: Mineral and ground material properties
\r
267 - This way mineral ground toughness can be calculated with just
\r
268 some formula, as well as tool strengths
\r
270 TODO: Flowing water to actually contain flow direction information
\r
271 - There is a space for this - it just has to be implemented.
\r
273 SUGG: Erosion simulation at map generation time
\r
274 - Simulate water flows, which would carve out dirt fast and
\r
275 then turn stone into gravel and sand and relocate it.
\r
276 - How about relocating minerals, too? Coal and gold in
\r
277 downstream sand and gravel would be kind of cool
\r
278 - This would need a better way of handling minerals, mainly
\r
279 to have mineral content as a separate field. the first
\r
280 parameter field is free for this.
\r
281 - Simulate rock falling from cliffs when water has removed
\r
282 enough solid rock from the bottom
\r
284 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
285 of low-res 3d noise and interpolate linearly.
\r
288 * Possibly add some kind of erosion and other stuff
\r
289 * Better water generation (spread it to underwater caverns but don't
\r
290 fill dungeons that don't touch big water masses)
\r
291 * When generating a chunk and the neighboring chunk doesn't have mud
\r
292 and stuff yet and the ground is fairly flat, the mud will flow to
\r
293 the other chunk making nasty straight walls when the other chunk
\r
294 is generated. Fix it. Maybe just a special case if the ground is
\r
299 * Move digging property stuff from material.{h,cpp} to mapnode.cpp
\r
300 - ...Or maybe move content_features to material.{h,cpp}?
\r
302 Making it more portable:
\r
303 ------------------------
\r
305 Stuff to do before release:
\r
306 ---------------------------
\r
307 - Player default privileges and default password
\r
309 - Some simple block-based dynamic stuff in the world (finish the
\r
310 ActiveBlockModifier stuff)
\r
311 - Protocol version field
\r
312 - Consider getting some textures from cisoun's texture pack
\r
313 - Add a long step function to objects that is called with the time
\r
314 difference when block activates
\r
316 ======================================================================
\r
322 #pragma message ("Disabling unit tests")
\r
324 #warning "Disabling unit tests"
\r
326 // Disable unit tests
\r
327 #define ENABLE_TESTS 0
\r
329 // Enable unit tests
\r
330 #define ENABLE_TESTS 1
\r
334 #pragma comment(lib, "Irrlicht.lib")
\r
335 //#pragma comment(lib, "jthread.lib")
\r
336 #pragma comment(lib, "zlibwapi.lib")
\r
337 #pragma comment(lib, "Shell32.lib")
\r
338 // This would get rid of the console window
\r
339 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
342 #include <iostream>
\r
344 //#include <jmutexautolock.h>
\r
345 #include <locale.h>
\r
347 #include "common_irrlicht.h"
\r
350 //#include "player.h"
\r
352 #include "server.h"
\r
353 //#include "client.h"
\r
354 #include "constants.h"
\r
355 #include "porting.h"
\r
356 #include "gettime.h"
\r
357 #include "guiMessageMenu.h"
\r
358 #include "filesys.h"
\r
359 #include "config.h"
\r
360 #include "guiMainMenu.h"
\r
361 #include "mineral.h"
\r
362 //#include "noise.h"
\r
363 //#include "tile.h"
\r
364 #include "materials.h"
\r
366 #include "keycode.h"
\r
368 // This makes textures
\r
369 ITextureSource *g_texturesource = NULL;
\r
373 These are loaded from the config file.
\r
376 Settings g_settings;
\r
377 // This is located in defaultsettings.cpp
\r
378 extern void set_default_settings();
\r
388 gui::IGUIEnvironment* guienv = NULL;
\r
389 gui::IGUIStaticText *guiroot = NULL;
\r
391 MainMenuManager g_menumgr;
\r
393 bool noMenuActive()
\r
395 return (g_menumgr.menuCount() == 0);
\r
398 // Passed to menus to allow disconnecting and exiting
\r
400 MainGameCallback *g_gamecallback = NULL;
\r
407 std::ostream *dout_con_ptr = &dummyout;
\r
408 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
409 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
410 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
411 //std::ostream *dout_con_ptr = &dstream;
\r
412 //std::ostream *derr_con_ptr = &dstream;
\r
415 std::ostream *dout_server_ptr = &dstream;
\r
416 std::ostream *derr_server_ptr = &dstream;
\r
419 std::ostream *dout_client_ptr = &dstream;
\r
420 std::ostream *derr_client_ptr = &dstream;
\r
423 gettime.h implementation
\r
426 // A small helper class
\r
430 TimeGetter(IrrlichtDevice *device):
\r
435 if(m_device == NULL)
\r
437 return m_device->getTimer()->getRealTime();
\r
440 IrrlichtDevice *m_device;
\r
443 // A pointer to a global instance of the time getter
\r
444 TimeGetter *g_timegetter = NULL;
\r
448 if(g_timegetter == NULL)
\r
450 return g_timegetter->getTime();
\r
454 Event handler for Irrlicht
\r
456 NOTE: Everything possible should be moved out from here,
\r
457 probably to InputHandler and the_game
\r
460 class MyEventReceiver : public IEventReceiver
\r
463 // This is the one method that we have to implement
\r
464 virtual bool OnEvent(const SEvent& event)
\r
467 React to nothing here if a menu is active
\r
469 if(noMenuActive() == false)
\r
474 // Remember whether each key is down or up
\r
475 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
477 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
479 if(event.KeyInput.PressedDown)
\r
480 keyWasDown[event.KeyInput.Key] = true;
\r
483 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
485 if(noMenuActive() == false)
\r
487 left_active = false;
\r
488 middle_active = false;
\r
489 right_active = false;
\r
493 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
494 left_active = event.MouseInput.isLeftPressed();
\r
495 middle_active = event.MouseInput.isMiddlePressed();
\r
496 right_active = event.MouseInput.isRightPressed();
\r
498 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
500 leftclicked = true;
\r
502 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
504 rightclicked = true;
\r
506 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
508 leftreleased = true;
\r
510 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
512 rightreleased = true;
\r
514 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
516 mouse_wheel += event.MouseInput.Wheel;
\r
524 bool IsKeyDown(EKEY_CODE keyCode) const
\r
526 return keyIsDown[keyCode];
\r
529 // Checks whether a key was down and resets the state
\r
530 bool WasKeyDown(EKEY_CODE keyCode)
\r
532 bool b = keyWasDown[keyCode];
\r
533 keyWasDown[keyCode] = false;
\r
537 s32 getMouseWheel()
\r
539 s32 a = mouse_wheel;
\r
546 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
\r
548 keyIsDown[i] = false;
\r
549 keyWasDown[i] = false;
\r
552 leftclicked = false;
\r
553 rightclicked = false;
\r
554 leftreleased = false;
\r
555 rightreleased = false;
\r
557 left_active = false;
\r
558 middle_active = false;
\r
559 right_active = false;
\r
572 bool rightreleased;
\r
575 bool middle_active;
\r
581 IrrlichtDevice *m_device;
\r
583 // The current state of keys
\r
584 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
585 // Whether a key has been pressed or not
\r
586 bool keyWasDown[KEY_KEY_CODES_COUNT];
\r
590 Separated input handler
\r
593 class RealInputHandler : public InputHandler
\r
596 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
598 m_receiver(receiver)
\r
601 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
603 return m_receiver->IsKeyDown(keyCode);
\r
605 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
607 return m_receiver->WasKeyDown(keyCode);
\r
609 virtual v2s32 getMousePos()
\r
611 return m_device->getCursorControl()->getPosition();
\r
613 virtual void setMousePos(s32 x, s32 y)
\r
615 m_device->getCursorControl()->setPosition(x, y);
\r
618 virtual bool getLeftState()
\r
620 return m_receiver->left_active;
\r
622 virtual bool getRightState()
\r
624 return m_receiver->right_active;
\r
627 virtual bool getLeftClicked()
\r
629 return m_receiver->leftclicked;
\r
631 virtual bool getRightClicked()
\r
633 return m_receiver->rightclicked;
\r
635 virtual void resetLeftClicked()
\r
637 m_receiver->leftclicked = false;
\r
639 virtual void resetRightClicked()
\r
641 m_receiver->rightclicked = false;
\r
644 virtual bool getLeftReleased()
\r
646 return m_receiver->leftreleased;
\r
648 virtual bool getRightReleased()
\r
650 return m_receiver->rightreleased;
\r
652 virtual void resetLeftReleased()
\r
654 m_receiver->leftreleased = false;
\r
656 virtual void resetRightReleased()
\r
658 m_receiver->rightreleased = false;
\r
661 virtual s32 getMouseWheel()
\r
663 return m_receiver->getMouseWheel();
\r
668 m_receiver->clearInput();
\r
671 IrrlichtDevice *m_device;
\r
672 MyEventReceiver *m_receiver;
\r
675 class RandomInputHandler : public InputHandler
\r
678 RandomInputHandler()
\r
682 leftclicked = false;
\r
683 rightclicked = false;
\r
684 leftreleased = false;
\r
685 rightreleased = false;
\r
686 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
687 keydown[i] = false;
\r
689 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
691 return keydown[keyCode];
\r
693 virtual bool wasKeyDown(EKEY_CODE keyCode)
\r
697 virtual v2s32 getMousePos()
\r
701 virtual void setMousePos(s32 x, s32 y)
\r
703 mousepos = v2s32(x,y);
\r
706 virtual bool getLeftState()
\r
710 virtual bool getRightState()
\r
715 virtual bool getLeftClicked()
\r
717 return leftclicked;
\r
719 virtual bool getRightClicked()
\r
721 return rightclicked;
\r
723 virtual void resetLeftClicked()
\r
725 leftclicked = false;
\r
727 virtual void resetRightClicked()
\r
729 rightclicked = false;
\r
732 virtual bool getLeftReleased()
\r
734 return leftreleased;
\r
736 virtual bool getRightReleased()
\r
738 return rightreleased;
\r
740 virtual void resetLeftReleased()
\r
742 leftreleased = false;
\r
744 virtual void resetRightReleased()
\r
746 rightreleased = false;
\r
749 virtual s32 getMouseWheel()
\r
754 virtual void step(float dtime)
\r
757 static float counter1 = 0;
\r
761 counter1 = 0.1*Rand(1, 40);
\r
762 keydown[getKeySetting("keymap_jump")] =
\r
763 !keydown[getKeySetting("keymap_jump")];
\r
767 static float counter1 = 0;
\r
771 counter1 = 0.1*Rand(1, 40);
\r
772 keydown[getKeySetting("keymap_special1")] =
\r
773 !keydown[getKeySetting("keymap_special1")];
\r
777 static float counter1 = 0;
\r
781 counter1 = 0.1*Rand(1, 40);
\r
782 keydown[getKeySetting("keymap_forward")] =
\r
783 !keydown[getKeySetting("keymap_forward")];
\r
787 static float counter1 = 0;
\r
791 counter1 = 0.1*Rand(1, 40);
\r
792 keydown[getKeySetting("keymap_left")] =
\r
793 !keydown[getKeySetting("keymap_left")];
\r
797 static float counter1 = 0;
\r
801 counter1 = 0.1*Rand(1, 20);
\r
802 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
806 static float counter1 = 0;
\r
810 counter1 = 0.1*Rand(1, 30);
\r
811 leftdown = !leftdown;
\r
813 leftclicked = true;
\r
815 leftreleased = true;
\r
819 static float counter1 = 0;
\r
823 counter1 = 0.1*Rand(1, 15);
\r
824 rightdown = !rightdown;
\r
826 rightclicked = true;
\r
828 rightreleased = true;
\r
831 mousepos += mousespeed;
\r
834 s32 Rand(s32 min, s32 max)
\r
836 return (myrand()%(max-min+1))+min;
\r
839 bool keydown[KEY_KEY_CODES_COUNT];
\r
847 bool rightreleased;
\r
850 // These are defined global so that they're not optimized too much.
\r
851 // Can't change them to volatile.
\r
856 std::string tempstring;
\r
857 std::string tempstring2;
\r
862 dstream<<"The following test should take around 20ms."<<std::endl;
\r
863 TimeTaker timer("Testing std::string speed");
\r
864 const u32 jj = 10000;
\r
865 for(u32 j=0; j<jj; j++)
\r
870 for(u32 i=0; i<ii; i++){
\r
871 tempstring2 += "asd";
\r
873 for(u32 i=0; i<ii+1; i++){
\r
874 tempstring += "asd";
\r
875 if(tempstring == tempstring2)
\r
881 dstream<<"All of the following tests should take around 100ms each."
\r
885 TimeTaker timer("Testing floating-point conversion speed");
\r
887 for(u32 i=0; i<4000000; i++){
\r
894 TimeTaker timer("Testing floating-point vector speed");
\r
896 tempv3f1 = v3f(1,2,3);
\r
897 tempv3f2 = v3f(4,5,6);
\r
898 for(u32 i=0; i<10000000; i++){
\r
899 tempf += tempv3f1.dotProduct(tempv3f2);
\r
900 tempv3f2 += v3f(7,8,9);
\r
905 TimeTaker timer("Testing core::map speed");
\r
907 core::map<v2s16, f32> map1;
\r
910 for(s16 y=0; y<ii; y++){
\r
911 for(s16 x=0; x<ii; x++){
\r
912 map1.insert(v2s16(x,y), tempf);
\r
916 for(s16 y=ii-1; y>=0; y--){
\r
917 for(s16 x=0; x<ii; x++){
\r
918 tempf = map1[v2s16(x,y)];
\r
924 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
925 TimeTaker timer("Testing mutex speed");
\r
938 // Do at least 10ms
\r
939 while(timer.getTime() < 10);
\r
941 u32 dtime = timer.stop();
\r
942 u32 per_ms = n / dtime;
\r
943 std::cout<<"Done. "<<dtime<<"ms, "
\r
944 <<per_ms<<"/ms"<<std::endl;
\r
948 void drawMenuBackground(video::IVideoDriver* driver)
\r
950 core::dimension2d<u32> screensize = driver->getScreenSize();
\r
952 video::ITexture *bgtexture =
\r
953 driver->getTexture(getTexturePath("mud.png").c_str());
\r
956 s32 texturesize = 128;
\r
957 s32 tiled_y = screensize.Height / texturesize + 1;
\r
958 s32 tiled_x = screensize.Width / texturesize + 1;
\r
960 for(s32 y=0; y<tiled_y; y++)
\r
961 for(s32 x=0; x<tiled_x; x++)
\r
963 core::rect<s32> rect(0,0,texturesize,texturesize);
\r
964 rect += v2s32(x*texturesize, y*texturesize);
\r
965 driver->draw2DImage(bgtexture, rect,
\r
966 core::rect<s32>(core::position2d<s32>(0,0),
\r
967 core::dimension2di(bgtexture->getSize())),
\r
972 video::ITexture *logotexture =
\r
973 driver->getTexture(getTexturePath("menulogo.png").c_str());
\r
976 v2s32 logosize(logotexture->getOriginalSize().Width,
\r
977 logotexture->getOriginalSize().Height);
\r
980 video::SColor bgcolor(255,50,50,50);
\r
981 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
\r
982 screensize.Width, screensize.Height);
\r
983 driver->draw2DRectangle(bgcolor, bgrect, NULL);
\r
985 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
\r
986 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
\r
987 rect -= v2s32(logosize.X/2, 0);
\r
988 driver->draw2DImage(logotexture, rect,
\r
989 core::rect<s32>(core::position2d<s32>(0,0),
\r
990 core::dimension2di(logotexture->getSize())),
\r
995 int main(int argc, char *argv[])
\r
1001 // List all allowed options
\r
1002 core::map<std::string, ValueSpec> allowed_options;
\r
1003 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1004 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1005 "Run server directly"));
\r
1006 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1007 "Load configuration from specified file"));
\r
1008 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1009 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1010 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1011 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1012 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1013 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1015 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1017 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1019 Settings cmd_args;
\r
1021 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1023 if(ret == false || cmd_args.getFlag("help"))
\r
1025 dstream<<"Allowed options:"<<std::endl;
\r
1026 for(core::map<std::string, ValueSpec>::Iterator
\r
1027 i = allowed_options.getIterator();
\r
1028 i.atEnd() == false; i++)
\r
1030 dstream<<" --"<<i.getNode()->getKey();
\r
1031 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1036 dstream<<" <value>";
\r
1038 dstream<<std::endl;
\r
1040 if(i.getNode()->getValue().help != NULL)
\r
1042 dstream<<" "<<i.getNode()->getValue().help
\r
1047 return cmd_args.getFlag("help") ? 0 : 1;
\r
1051 Low-level initialization
\r
1054 bool disable_stderr = false;
\r
1056 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1057 disable_stderr = true;
\r
1060 // Initialize debug streams
\r
1061 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1062 // Initialize debug stacks
\r
1063 debug_stacks_init();
\r
1065 DSTACK(__FUNCTION_NAME);
\r
1067 porting::signal_handler_init();
\r
1068 bool &kill = *porting::signal_handler_killstatus();
\r
1070 porting::initializePaths();
\r
1071 // Create user data directory
\r
1072 fs::CreateDir(porting::path_userdata);
\r
1074 // C-style stuff initialization
\r
1075 initializeMaterialProperties();
\r
1078 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1080 // Print startup message
\r
1081 dstream<<DTIME<<"minetest-c55"
\r
1082 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1083 <<", "<<BUILD_INFO
\r
1087 Basic initialization
\r
1090 // Initialize default settings
\r
1091 set_default_settings();
\r
1093 // Set locale. This is for forcing '.' as the decimal point.
\r
1094 std::locale::global(std::locale("C"));
\r
1095 // This enables printing all characters in bitmap font
\r
1096 setlocale(LC_CTYPE, "en_US");
\r
1098 // Initialize sockets
\r
1100 atexit(sockets_cleanup);
\r
1110 // Path of configuration file in use
\r
1111 std::string configpath = "";
\r
1113 if(cmd_args.exists("config"))
\r
1115 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1118 dstream<<"Could not read configuration from \""
\r
1119 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1122 configpath = cmd_args.get("config");
\r
1126 core::array<std::string> filenames;
\r
1127 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1128 #ifdef RUN_IN_PLACE
\r
1129 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1132 for(u32 i=0; i<filenames.size(); i++)
\r
1134 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1137 configpath = filenames[i];
\r
1142 // If no path found, use the first one (menu creates the file)
\r
1143 if(configpath == "")
\r
1144 configpath = filenames[0];
\r
1147 // Initialize random seed
\r
1152 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1154 These are needed for unit tests at least.
\r
1157 // Initial call with g_texturesource not set.
\r
1164 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1165 || cmd_args.getFlag("enable-unittests") == true)
\r
1170 /*for(s16 y=-100; y<100; y++)
\r
1171 for(s16 x=-100; x<100; x++)
\r
1173 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1183 if(cmd_args.exists("port"))
\r
1184 port = cmd_args.getU16("port");
\r
1185 else if(g_settings.exists("port"))
\r
1186 port = g_settings.getU16("port");
\r
1191 std::string map_dir = porting::path_userdata+"/map";
\r
1192 if(cmd_args.exists("map-dir"))
\r
1193 map_dir = cmd_args.get("map-dir");
\r
1194 else if(g_settings.exists("map-dir"))
\r
1195 map_dir = g_settings.get("map-dir");
\r
1197 // Run dedicated server if asked to
\r
1198 if(cmd_args.getFlag("server"))
\r
1200 DSTACK("Dedicated server branch");
\r
1203 Server server(map_dir.c_str());
\r
1204 server.start(port);
\r
1207 dedicated_server_loop(server, kill);
\r
1217 // Address to connect to
\r
1218 std::string address = "";
\r
1220 if(cmd_args.exists("address"))
\r
1222 address = cmd_args.get("address");
\r
1226 address = g_settings.get("address");
\r
1229 std::string playername = g_settings.get("name");
\r
1232 Device initialization
\r
1235 // Resolution selection
\r
1237 bool fullscreen = false;
\r
1238 u16 screenW = g_settings.getU16("screenW");
\r
1239 u16 screenH = g_settings.getU16("screenH");
\r
1241 // Determine driver
\r
1243 video::E_DRIVER_TYPE driverType;
\r
1245 std::string driverstring = g_settings.get("video_driver");
\r
1247 if(driverstring == "null")
\r
1248 driverType = video::EDT_NULL;
\r
1249 else if(driverstring == "software")
\r
1250 driverType = video::EDT_SOFTWARE;
\r
1251 else if(driverstring == "burningsvideo")
\r
1252 driverType = video::EDT_BURNINGSVIDEO;
\r
1253 else if(driverstring == "direct3d8")
\r
1254 driverType = video::EDT_DIRECT3D8;
\r
1255 else if(driverstring == "direct3d9")
\r
1256 driverType = video::EDT_DIRECT3D9;
\r
1257 else if(driverstring == "opengl")
\r
1258 driverType = video::EDT_OPENGL;
\r
1261 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1262 "to opengl"<<std::endl;
\r
1263 driverType = video::EDT_OPENGL;
\r
1267 Create device and exit if creation failed
\r
1270 MyEventReceiver receiver;
\r
1272 IrrlichtDevice *device;
\r
1273 device = createDevice(driverType,
\r
1274 core::dimension2d<u32>(screenW, screenH),
\r
1275 16, fullscreen, false, false, &receiver);
\r
1278 return 1; // could not create selected driver.
\r
1280 // Set device in game parameters
\r
1283 // Create time getter
\r
1284 g_timegetter = new TimeGetter(device);
\r
1286 // Create game callback for menus
\r
1287 g_gamecallback = new MainGameCallback(device);
\r
1289 // Create texture source
\r
1290 g_texturesource = new TextureSource(device);
\r
1293 Speed tests (done after irrlicht is loaded to get timer)
\r
1295 if(cmd_args.getFlag("speedtests"))
\r
1297 dstream<<"Running speed tests"<<std::endl;
\r
1302 device->setResizable(true);
\r
1304 bool random_input = g_settings.getBool("random_input")
\r
1305 || cmd_args.getFlag("random-input");
\r
1306 InputHandler *input = NULL;
\r
1308 input = new RandomInputHandler();
\r
1310 input = new RealInputHandler(device, &receiver);
\r
1313 Continue initialization
\r
1316 //video::IVideoDriver* driver = device->getVideoDriver();
\r
1319 This changes the minimum allowed number of vertices in a VBO.
\r
1322 //driver->setMinHardwareBufferVertexCount(50);
\r
1324 scene::ISceneManager* smgr = device->getSceneManager();
\r
1326 guienv = device->getGUIEnvironment();
\r
1327 gui::IGUISkin* skin = guienv->getSkin();
\r
1328 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
\r
1330 skin->setFont(font);
\r
1332 dstream<<"WARNING: Font file was not found."
\r
1333 " Using default font."<<std::endl;
\r
1334 // If font was not found, this will get us one
\r
1335 font = skin->getFont();
\r
1338 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1339 dstream<<"text_height="<<text_height<<std::endl;
\r
1341 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1342 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1343 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1344 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1345 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1346 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1349 Preload some textures and stuff
\r
1352 init_content_inventory_texture_paths();
\r
1353 init_mapnode(); // Second call with g_texturesource set
\r
1361 If an error occurs, this is set to something and the
\r
1362 menu-game loop is restarted. It is then displayed before
\r
1365 std::wstring error_message = L"";
\r
1367 // The password entered during the menu screen,
\r
1368 std::string password;
\r
1373 while(device->run() && kill == false)
\r
1376 // This is used for catching disconnects
\r
1381 Clear everything from the GUIEnvironment
\r
1386 We need some kind of a root node to be able to add
\r
1387 custom gui elements directly on the screen.
\r
1388 Otherwise they won't be automatically drawn.
\r
1390 guiroot = guienv->addStaticText(L"",
\r
1391 core::rect<s32>(0, 0, 10000, 10000));
\r
1394 Out-of-game menu loop.
\r
1396 Loop quits when menu returns proper parameters.
\r
1398 while(kill == false)
\r
1400 // Cursor can be non-visible when coming from the game
\r
1401 device->getCursorControl()->setVisible(true);
\r
1402 // Some stuff are left to scene manager when coming from the game
\r
1403 // (map at least?)
\r
1405 // Reset or hide the debug gui texts
\r
1406 /*guitext->setText(L"Minetest-c55");
\r
1407 guitext2->setVisible(false);
\r
1408 guitext_info->setVisible(false);
\r
1409 guitext_chat->setVisible(false);*/
\r
1411 // Initialize menu data
\r
1412 MainMenuData menudata;
\r
1413 menudata.address = narrow_to_wide(address);
\r
1414 menudata.name = narrow_to_wide(playername);
\r
1415 menudata.port = narrow_to_wide(itos(port));
\r
1416 menudata.fancy_trees = g_settings.getBool("new_style_leaves");
\r
1417 menudata.smooth_lighting = g_settings.getBool("smooth_lighting");
\r
1418 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1419 menudata.enable_damage = g_settings.getBool("enable_damage");
\r
1421 GUIMainMenu *menu =
\r
1422 new GUIMainMenu(guienv, guiroot, -1,
\r
1423 &g_menumgr, &menudata, g_gamecallback);
\r
1424 menu->allowFocusRemoval(true);
\r
1426 if(error_message != L"")
\r
1428 dstream<<"WARNING: error_message = "
\r
1429 <<wide_to_narrow(error_message)<<std::endl;
\r
1431 GUIMessageMenu *menu2 =
\r
1432 new GUIMessageMenu(guienv, guiroot, -1,
\r
1433 &g_menumgr, error_message.c_str());
\r
1435 error_message = L"";
\r
1438 video::IVideoDriver* driver = device->getVideoDriver();
\r
1440 dstream<<"Created main menu"<<std::endl;
\r
1442 while(device->run() && kill == false)
\r
1444 if(menu->getStatus() == true)
\r
1447 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1448 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1450 drawMenuBackground(driver);
\r
1452 guienv->drawAll();
\r
1454 driver->endScene();
\r
1456 // On some computers framerate doesn't seem to be
\r
1457 // automatically limited
\r
1461 // Break out of menu-game loop to shut down cleanly
\r
1462 if(device->run() == false || kill == true)
\r
1465 dstream<<"Dropping main menu"<<std::endl;
\r
1469 // Delete map if requested
\r
1470 if(menudata.delete_map)
\r
1472 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1474 error_message = L"Delete failed";
\r
1478 playername = wide_to_narrow(menudata.name);
\r
1480 password = translatePassword(playername, menudata.password);
\r
1482 address = wide_to_narrow(menudata.address);
\r
1483 int newport = stoi(wide_to_narrow(menudata.port));
\r
1486 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));
\r
1487 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));
\r
1488 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1489 g_settings.set("enable_damage", itos(menudata.enable_damage));
\r
1491 // Check for valid parameters, restart menu if invalid.
\r
1492 if(playername == "")
\r
1494 error_message = L"Name required.";
\r
1499 g_settings.set("name", playername);
\r
1500 g_settings.set("address", address);
\r
1501 g_settings.set("port", itos(port));
\r
1502 // Update configuration file
\r
1503 if(configpath != "")
\r
1504 g_settings.updateConfigFile(configpath.c_str());
\r
1506 // Continue to game
\r
1510 // Break out of menu-game loop to shut down cleanly
\r
1511 if(device->run() == false)
\r
1514 // Initialize mapnode again to enable changed graphics settings
\r
1535 catch(con::PeerNotFoundException &e)
\r
1537 dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;
\r
1538 error_message = L"Connection error (timed out?)";
\r
1540 catch(SocketException &e)
\r
1542 dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;
\r
1543 error_message = L"Socket error (port already in use?)";
\r
1546 catch(std::exception &e)
\r
1548 std::string narrow_message = "Some exception, what()=\"";
\r
1549 narrow_message += e.what();
\r
1550 narrow_message += "\"";
\r
1551 dstream<<DTIME<<narrow_message<<std::endl;
\r
1552 error_message = narrow_to_wide(narrow_message);
\r
1556 } // Menu-game loop
\r
1561 In the end, delete the Irrlicht device.
\r
1565 END_DEBUG_EXCEPTION_HANDLER
\r
1567 debugstreams_deinit();
\r