3 Copyright (C) 2010 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: VBO cannot be turned on for fast-changing stuff because there
\r
25 is an apparanet memory leak in irrlicht when using it (not sure)
\r
26 - It is not a memory leak but some kind of a buffer.
\r
28 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
29 NOTE: Global locale is now set at initialization
\r
31 Random suggeestions:
\r
32 --------------------
\r
34 SUGG: Fix address to be ipv6 compatible
\r
36 NOTE: When a new sector is generated, it may change the ground level
\r
37 of it's and it's neighbors border that two blocks that are
\r
38 above and below each other and that are generated before and
\r
39 after the sector heightmap generation (order doesn't matter),
\r
40 can have a small gap between each other at the border.
\r
41 SUGG: Use same technique for sector heightmaps as what we're
\r
42 using for UnlimitedHeightmap? (getting all neighbors
\r
45 SUGG: Transfer more blocks in a single packet
\r
46 SUGG: A blockdata combiner class, to which blocks are added and at
\r
47 destruction it sends all the stuff in as few packets as possible.
\r
49 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
50 SUGG: Fetch stuff mainly from the viewing direction
\r
52 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
53 - This enables saving many packets and making a faster connection
\r
54 - This also enables server to check if client has received the
\r
55 most recent block sent, for example.
\r
56 SUGG: Add a sane bandwidth throttling system to Connection
\r
58 SUGG: More fine-grained control of client's dumping of blocks from
\r
60 - ...What does this mean in the first place?
\r
62 SUGG: A map editing mode (similar to dedicated server mode)
\r
64 SUGG: Add a time value to the param of footstepped grass and check it
\r
65 against a global timer when a block is accessed, to make old
\r
68 SUGG: Make a copy of close-range environment on client for showing
\r
69 on screen, with minimal mutexes to slow down the main loop
\r
71 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
72 it by sending more stuff in a single packet.
\r
73 - Add a packet queue to RemoteClient, from which packets will be
\r
74 combined with object data packets
\r
75 - This is not exactly trivial: the object data packets are
\r
76 sometimes very big by themselves
\r
78 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
79 - This will allow saving ages of rats on disk but not sending
\r
82 SUGG: MovingObject::move and Player::move are basically the same.
\r
84 - NOTE: Player::move is more up-to-date.
\r
86 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
87 - This is not doable because it is currently hand-made and not
\r
88 based on some mathematical function.
\r
89 - Note: This has been changing lately
\r
91 SUGG: A version number to blocks, which increments when the block is
\r
92 modified (node add/remove, water update, lighting update)
\r
93 - This can then be used to make sure the most recent version of
\r
94 a block has been sent to client
\r
96 SUGG: Make the amount of blocks sending to client and the total
\r
97 amount of blocks dynamically limited. Transferring blocks is the
\r
98 main network eater of this system, so it is the one that has
\r
99 to be throttled so that RTTs stay low.
\r
101 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
102 different directions and then only those drawn that need to be
\r
104 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
110 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
112 - The player could go faster by a crafting a boat, or riding an animal
\r
114 - Random NPC traders. what else?
\r
119 Build system / running:
\r
120 -----------------------
\r
122 Networking and serialization:
\r
123 -----------------------------
\r
125 TODO: Get rid of GotSplitPacketException
\r
130 TODO: Add gui option to remove map
\r
132 TODO: Configuration menu, at least for keys
\r
137 TODO: Optimize day/night mesh updating somehow
\r
138 - create copies of all textures for all lighting values and only
\r
139 change texture for material?
\r
140 - Umm... the collecting of the faces is the slow part
\r
141 -> what about just changing the color values of the existing
\r
142 meshbuffers? It should go quite fast.
\r
143 - This is not easy; There'd need to be a buffer somewhere
\r
144 that would contain the night and day lighting values.
\r
145 - Actually if FastFaces would be stored, they could
\r
148 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
150 - That is >500 vertices
\r
151 - This is not easy; all the MapBlocks close to the player would
\r
152 still need to be drawn separately and combining the blocks
\r
153 would have to happen in a background thread
\r
155 TODO: Make fetching sector's blocks more efficient when rendering
\r
156 sectors that have very large amounts of blocks (on client)
\r
157 - Is this necessary at all?
\r
159 TODO: Flowing water animation
\r
161 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
163 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
164 animating them is easier.
\r
172 TODO: Untie client network operations from framerate
\r
173 - Needs some input queues or something
\r
175 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
177 SUGG: Don't update all meshes always on single node changes, but
\r
178 check which ones should be updated
\r
179 - implement Map::updateNodeMeshes()
\r
181 TODO: Remove IrrlichtWrapper
\r
186 TODO: When player dies, throw items on map
\r
188 SUGG: Make an option to the server to disable building and digging near
\r
189 the starting position
\r
191 TODO: Copy the text of the last picked sign to inventory in creative
\r
194 TODO: Check what goes wrong with caching map to disk (Kray)
\r
197 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
199 * Fix the problem with the server constantly saving one or a few
\r
200 blocks? List the first saved block, maybe it explains.
\r
201 - It is probably caused by oscillating water
\r
202 * Make a small history check to transformLiquids to detect and log
\r
203 continuous oscillations, in such detail that they can be fixed.
\r
208 TODO: There has to be some better way to handle static objects than to
\r
209 send them all the time. This affects signs and item objects.
\r
210 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
211 need an additional metadata field for the texts
\r
212 - This is also needed for item container chests
\r
214 Block object server side:
\r
215 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
216 - For all blocks in the buffer, objects are stepped(). This
\r
217 means they are active.
\r
218 - TODO: A global active buffer is needed for the server
\r
219 - TODO: A timestamp to blocks
\r
220 - TODO: All blocks going in and out of the buffer are recorded.
\r
221 - TODO: For outgoing blocks, timestamp is written.
\r
222 - TODO: For incoming blocks, time difference is calculated and
\r
223 objects are stepped according to it.
\r
225 - When an active object goes far from a player, either delete
\r
226 it or store it statically.
\r
227 - When a statically stored active object comes near a player,
\r
228 recreate the active object
\r
230 * Continue making the scripting system:
\r
231 * Make updateNodeMesh for a less verbose mesh update on add/removenode
\r
232 * Switch to using a safe way for the self and env pointers
\r
233 * Make some global environment hooks, like node placed and general
\r
235 * Add a global Lua spawn handler and such
\r
236 * Get rid of MapBlockObjects
\r
237 * Other players could be sent to clients as LuaCAOs
\r
242 TODO: Mineral and ground material properties
\r
243 - This way mineral ground toughness can be calculated with just
\r
244 some formula, as well as tool strengths
\r
246 TODO: Flowing water to actually contain flow direction information
\r
248 FEATURE: Create a system that allows a huge amount of different "map
\r
249 generator modules/filters"
\r
251 FEATURE: Erosion simulation at map generation time
\r
252 - Simulate water flows, which would carve out dirt fast and
\r
253 then turn stone into gravel and sand and relocate it.
\r
254 - How about relocating minerals, too? Coal and gold in
\r
255 downstream sand and gravel would be kind of cool
\r
256 - This would need a better way of handling minerals, mainly
\r
257 to have mineral content as a separate field. the first
\r
258 parameter field is free for this.
\r
259 - Simulate rock falling from cliffs when water has removed
\r
260 enough solid rock from the bottom
\r
262 Mapgen v2 (not doing):
\r
263 * only_from_disk might not work anymore - check and fix it.
\r
264 * Make the generator to run in background and not blocking block
\r
265 placement and transfer
\r
266 * Add some kind of erosion and other stuff that now is possible
\r
267 * Make client to fetch stuff asynchronously
\r
268 - Needs method SyncProcessData
\r
269 * Better water generation (spread it to underwater caverns but don't
\r
270 fill dungeons that don't touch big water masses)
\r
271 * When generating a chunk and the neighboring chunk doesn't have mud
\r
272 and stuff yet and the ground is fairly flat, the mud will flow to
\r
273 the other chunk making nasty straight walls when the other chunk
\r
274 is generated. Fix it.
\r
276 Mapgen v4 (not doing):
\r
277 * only_from_disk might not work anymore - check and fix it.
\r
278 * Make the generator to run in background and not blocking block
\r
279 placement and transfer
\r
280 * Make chunks to be tiled vertically too
\r
283 Mapgen v3 (not doing):
\r
284 * Generate trees better
\r
285 - Add a "trees_added" flag to sector, or something
\r
286 * How 'bout making turbulence controlled so that for a given 2d position
\r
287 it can be completely turned off, and is usually so. This way generation
\r
288 can be sped up a lot.
\r
289 * Add a way to generate a block partly, so that trees are not added, like
\r
291 * Add mud "discretely", not by guessing from the noise
\r
294 * This will be the final way.
\r
295 * Generate blocks in the same way as chunks, by copying a VoxelManipulator
\r
296 from the map that is one block larger in all directions.
\r
300 * Make an "environment metafile" to store at least time of day
\r
301 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
302 - Or maybe move content_features to material.{h,cpp}?
\r
304 Make a system for pregenerating quick information for mapblocks, so
\r
305 that the client can show them as cubes before they are actually sent
\r
307 * Optimize VoxelManipulator lighting implementation by using indices
\r
308 in place of coordinates?
\r
310 Making it more portable:
\r
311 ------------------------
\r
312 * Some MSVC: std::sto* are defined without a namespace and collide
\r
313 with the ones in utility.h
\r
315 ======================================================================
\r
320 Setting this to 1 enables a special camera mode that forces
\r
321 the renderers to think that the camera statically points from
\r
322 the starting place to a static direction.
\r
324 This allows one to move around with the player and see what
\r
325 is actually drawn behind solid things and behind the player.
\r
327 #define FIELD_OF_VIEW_TEST 0
\r
331 #pragma message ("Disabling unit tests")
\r
333 #warning "Disabling unit tests"
\r
335 // Disable unit tests
\r
336 #define ENABLE_TESTS 0
\r
338 // Enable unit tests
\r
339 #define ENABLE_TESTS 1
\r
343 #pragma comment(lib, "Irrlicht.lib")
\r
344 //#pragma comment(lib, "jthread.lib")
\r
345 #pragma comment(lib, "zlibwapi.lib")
\r
346 #pragma comment(lib, "Shell32.lib")
\r
347 // This would get rid of the console window
\r
348 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
351 #include <iostream>
\r
353 #include <jmutexautolock.h>
\r
354 #include <locale.h>
\r
356 #include "common_irrlicht.h"
\r
359 #include "player.h"
\r
361 //#include "environment.h"
\r
362 #include "server.h"
\r
363 #include "client.h"
\r
364 //#include "serialization.h"
\r
365 #include "constants.h"
\r
366 //#include "strfnd.h"
\r
367 #include "porting.h"
\r
368 #include "irrlichtwrapper.h"
\r
369 #include "gettime.h"
\r
370 #include "porting.h"
\r
371 #include "guiPauseMenu.h"
\r
372 #include "guiInventoryMenu.h"
\r
373 #include "guiTextInputMenu.h"
\r
374 #include "materials.h"
\r
375 #include "guiMessageMenu.h"
\r
376 #include "filesys.h"
\r
377 #include "config.h"
\r
378 #include "guiMainMenu.h"
\r
379 #include "mineral.h"
\r
383 // TODO: Remove this
\r
384 IrrlichtWrapper *g_irrlicht = NULL;
\r
386 // This makes textures
\r
387 ITextureSource *g_texturesource = NULL;
\r
389 MapDrawControl draw_control;
\r
393 These are loaded from the config file.
\r
396 Settings g_settings;
\r
398 extern void set_default_settings();
\r
404 IrrlichtDevice *g_device = NULL;
\r
405 Client *g_client = NULL;
\r
407 /*const s16 quickinv_size = 40;
\r
408 const s16 quickinv_padding = 8;
\r
409 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
410 const s16 quickinv_outer_padding = 4;
\r
411 const s16 quickinv_itemcount = 8;*/
\r
413 const s32 hotbar_itemcount = 8;
\r
414 const s32 hotbar_imagesize = 36;
\r
420 gui::IGUIEnvironment* guienv = NULL;
\r
421 gui::IGUIStaticText *guiroot = NULL;
\r
423 class MainMenuManager : public IMenuManager
\r
426 virtual void createdMenu(GUIModalMenu *menu)
\r
428 for(core::list<GUIModalMenu*>::Iterator
\r
429 i = m_stack.begin();
\r
430 i != m_stack.end(); i++)
\r
432 assert(*i != menu);
\r
435 if(m_stack.size() != 0)
\r
436 (*m_stack.getLast())->setVisible(false);
\r
437 m_stack.push_back(menu);
\r
440 virtual void deletingMenu(GUIModalMenu *menu)
\r
442 // Remove all entries if there are duplicates
\r
443 bool removed_entry;
\r
445 removed_entry = false;
\r
446 for(core::list<GUIModalMenu*>::Iterator
\r
447 i = m_stack.begin();
\r
448 i != m_stack.end(); i++)
\r
453 removed_entry = true;
\r
457 }while(removed_entry);
\r
459 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
460 assert(*i == menu);
\r
461 m_stack.erase(i);*/
\r
463 if(m_stack.size() != 0)
\r
464 (*m_stack.getLast())->setVisible(true);
\r
469 return m_stack.size();
\r
472 core::list<GUIModalMenu*> m_stack;
\r
475 MainMenuManager g_menumgr;
\r
477 bool noMenuActive()
\r
479 return (g_menumgr.menuCount() == 0);
\r
482 bool g_disconnect_requested = false;
\r
484 class MainGameCallback : public IGameCallback
\r
487 virtual void exitToOS()
\r
489 g_device->closeDevice();
\r
492 virtual void disconnect()
\r
494 g_disconnect_requested = true;
\r
498 MainGameCallback g_gamecallback;
\r
500 // Inventory actions from the menu are buffered here before sending
\r
501 Queue<InventoryAction*> inventory_action_queue;
\r
502 // This is a copy of the inventory that the client's environment has
\r
503 Inventory local_inventory;
\r
505 u16 g_selected_item = 0;
\r
507 bool g_show_map_plot = false;
\r
508 bool g_refresh_map_plot = false;
\r
515 std::ostream *dout_con_ptr = &dummyout;
\r
516 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
517 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
518 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
519 //std::ostream *dout_con_ptr = &dstream;
\r
520 //std::ostream *derr_con_ptr = &dstream;
\r
523 std::ostream *dout_server_ptr = &dstream;
\r
524 std::ostream *derr_server_ptr = &dstream;
\r
527 std::ostream *dout_client_ptr = &dstream;
\r
528 std::ostream *derr_client_ptr = &dstream;
\r
531 gettime.h implementation
\r
537 Use irrlicht because it is more precise than porting.h's
\r
540 if(g_irrlicht == NULL)
\r
542 return g_irrlicht->getTime();
\r
549 struct TextDestSign : public TextDest
\r
551 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
553 m_blockpos = blockpos;
\r
557 void gotText(std::wstring text)
\r
559 std::string ntext = wide_to_narrow(text);
\r
560 dstream<<"Changing text of a sign object: "
\r
561 <<ntext<<std::endl;
\r
562 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
570 struct TextDestChat : public TextDest
\r
572 TextDestChat(Client *client)
\r
576 void gotText(std::wstring text)
\r
578 // Discard empty line
\r
582 // Parse command (server command starts with "/#")
\r
583 if(text[0] == L'/' && text[1] != L'#')
\r
585 std::wstring reply = L"Local: ";
\r
587 reply += L"Local commands not yet supported. "
\r
588 L"Server prefix is \"/#\".";
\r
590 m_client->addChatMessage(reply);
\r
595 m_client->sendChatMessage(text);
\r
597 m_client->addChatMessage(text);
\r
603 class MyEventReceiver : public IEventReceiver
\r
606 // This is the one method that we have to implement
\r
607 virtual bool OnEvent(const SEvent& event)
\r
610 React to nothing here if a menu is active
\r
612 if(noMenuActive() == false)
\r
618 // Remember whether each key is down or up
\r
619 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
621 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
623 if(event.KeyInput.PressedDown)
\r
625 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
626 if(g_show_map_plot)
\r
628 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
629 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
631 g_show_map_plot = false;
\r
640 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
642 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
644 dstream<<DTIME<<"MyEventReceiver: "
\r
645 <<"Launching pause menu"<<std::endl;
\r
646 // It will delete itself by itself
\r
647 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
648 &g_menumgr))->drop();
\r
651 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
653 dstream<<DTIME<<"MyEventReceiver: "
\r
654 <<"Launching inventory"<<std::endl;
\r
655 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
656 &local_inventory, &inventory_action_queue,
\r
657 &g_menumgr))->drop();
\r
660 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
662 TextDest *dest = new TextDestChat(g_client);
\r
664 (new GUITextInputMenu(guienv, guiroot, -1,
\r
671 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
672 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
674 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
675 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
677 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
678 g_selected_item = s1-1;
\r
679 dstream<<DTIME<<"Selected item: "
\r
680 <<g_selected_item<<std::endl;
\r
683 // Viewing range selection
\r
684 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
686 if(draw_control.range_all)
\r
688 draw_control.range_all = false;
\r
689 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
693 draw_control.range_all = true;
\r
694 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
698 // Print debug stacks
\r
699 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
701 dstream<<"-----------------------------------------"
\r
703 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
704 dstream<<"-----------------------------------------"
\r
706 debug_stacks_print();
\r
710 if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
712 dstream<<"Map plot requested"<<std::endl;
\r
713 g_show_map_plot = !g_show_map_plot;
\r
714 if(g_show_map_plot)
\r
715 g_refresh_map_plot = true;
\r
721 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
723 if(noMenuActive() == false)
\r
725 left_active = false;
\r
726 middle_active = false;
\r
727 right_active = false;
\r
731 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
732 left_active = event.MouseInput.isLeftPressed();
\r
733 middle_active = event.MouseInput.isMiddlePressed();
\r
734 right_active = event.MouseInput.isRightPressed();
\r
736 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
738 leftclicked = true;
\r
740 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
742 rightclicked = true;
\r
744 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
746 leftreleased = true;
\r
748 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
750 rightreleased = true;
\r
752 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
754 /*dstream<<"event.MouseInput.Wheel="
\r
755 <<event.MouseInput.Wheel<<std::endl;*/
\r
757 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
758 hotbar_itemcount-1);
\r
759 if(event.MouseInput.Wheel < 0)
\r
761 if(g_selected_item < max_item)
\r
764 g_selected_item = 0;
\r
766 else if(event.MouseInput.Wheel > 0)
\r
768 if(g_selected_item > 0)
\r
771 g_selected_item = max_item;
\r
780 // This is used to check whether a key is being held down
\r
781 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
783 return keyIsDown[keyCode];
\r
788 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
789 keyIsDown[i] = false;
\r
791 leftclicked = false;
\r
792 rightclicked = false;
\r
793 leftreleased = false;
\r
794 rightreleased = false;
\r
796 left_active = false;
\r
797 middle_active = false;
\r
798 right_active = false;
\r
809 bool rightreleased;
\r
812 bool middle_active;
\r
816 // We use this array to store the current state of each key
\r
817 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
820 IrrlichtDevice *m_device;
\r
829 virtual ~InputHandler()
\r
833 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
835 virtual v2s32 getMousePos() = 0;
\r
836 virtual void setMousePos(s32 x, s32 y) = 0;
\r
838 virtual bool getLeftState() = 0;
\r
839 virtual bool getRightState() = 0;
\r
841 virtual bool getLeftClicked() = 0;
\r
842 virtual bool getRightClicked() = 0;
\r
843 virtual void resetLeftClicked() = 0;
\r
844 virtual void resetRightClicked() = 0;
\r
846 virtual bool getLeftReleased() = 0;
\r
847 virtual bool getRightReleased() = 0;
\r
848 virtual void resetLeftReleased() = 0;
\r
849 virtual void resetRightReleased() = 0;
\r
851 virtual void step(float dtime) {};
\r
853 virtual void clear() {};
\r
856 InputHandler *g_input = NULL;
\r
858 class RealInputHandler : public InputHandler
\r
861 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
863 m_receiver(receiver)
\r
866 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
868 return m_receiver->IsKeyDown(keyCode);
\r
870 virtual v2s32 getMousePos()
\r
872 return m_device->getCursorControl()->getPosition();
\r
874 virtual void setMousePos(s32 x, s32 y)
\r
876 m_device->getCursorControl()->setPosition(x, y);
\r
879 virtual bool getLeftState()
\r
881 return m_receiver->left_active;
\r
883 virtual bool getRightState()
\r
885 return m_receiver->right_active;
\r
888 virtual bool getLeftClicked()
\r
890 return m_receiver->leftclicked;
\r
892 virtual bool getRightClicked()
\r
894 return m_receiver->rightclicked;
\r
896 virtual void resetLeftClicked()
\r
898 m_receiver->leftclicked = false;
\r
900 virtual void resetRightClicked()
\r
902 m_receiver->rightclicked = false;
\r
905 virtual bool getLeftReleased()
\r
907 return m_receiver->leftreleased;
\r
909 virtual bool getRightReleased()
\r
911 return m_receiver->rightreleased;
\r
913 virtual void resetLeftReleased()
\r
915 m_receiver->leftreleased = false;
\r
917 virtual void resetRightReleased()
\r
919 m_receiver->rightreleased = false;
\r
924 resetRightClicked();
\r
925 resetLeftClicked();
\r
928 IrrlichtDevice *m_device;
\r
929 MyEventReceiver *m_receiver;
\r
932 class RandomInputHandler : public InputHandler
\r
935 RandomInputHandler()
\r
939 leftclicked = false;
\r
940 rightclicked = false;
\r
941 leftreleased = false;
\r
942 rightreleased = false;
\r
943 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
944 keydown[i] = false;
\r
946 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
948 return keydown[keyCode];
\r
950 virtual v2s32 getMousePos()
\r
954 virtual void setMousePos(s32 x, s32 y)
\r
956 mousepos = v2s32(x,y);
\r
959 virtual bool getLeftState()
\r
963 virtual bool getRightState()
\r
968 virtual bool getLeftClicked()
\r
970 return leftclicked;
\r
972 virtual bool getRightClicked()
\r
974 return rightclicked;
\r
976 virtual void resetLeftClicked()
\r
978 leftclicked = false;
\r
980 virtual void resetRightClicked()
\r
982 rightclicked = false;
\r
985 virtual bool getLeftReleased()
\r
987 return leftreleased;
\r
989 virtual bool getRightReleased()
\r
991 return rightreleased;
\r
993 virtual void resetLeftReleased()
\r
995 leftreleased = false;
\r
997 virtual void resetRightReleased()
\r
999 rightreleased = false;
\r
1002 virtual void step(float dtime)
\r
1005 static float counter1 = 0;
\r
1006 counter1 -= dtime;
\r
1007 if(counter1 < 0.0)
\r
1009 counter1 = 0.1*Rand(1, 40);
\r
1010 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
1014 static float counter1 = 0;
\r
1015 counter1 -= dtime;
\r
1016 if(counter1 < 0.0)
\r
1018 counter1 = 0.1*Rand(1, 40);
\r
1019 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1023 static float counter1 = 0;
\r
1024 counter1 -= dtime;
\r
1025 if(counter1 < 0.0)
\r
1027 counter1 = 0.1*Rand(1, 40);
\r
1028 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1032 static float counter1 = 0;
\r
1033 counter1 -= dtime;
\r
1034 if(counter1 < 0.0)
\r
1036 counter1 = 0.1*Rand(1, 40);
\r
1037 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1041 static float counter1 = 0;
\r
1042 counter1 -= dtime;
\r
1043 if(counter1 < 0.0)
\r
1045 counter1 = 0.1*Rand(1, 20);
\r
1046 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1050 static float counter1 = 0;
\r
1051 counter1 -= dtime;
\r
1052 if(counter1 < 0.0)
\r
1054 counter1 = 0.1*Rand(1, 30);
\r
1055 leftdown = !leftdown;
\r
1057 leftclicked = true;
\r
1059 leftreleased = true;
\r
1063 static float counter1 = 0;
\r
1064 counter1 -= dtime;
\r
1065 if(counter1 < 0.0)
\r
1067 counter1 = 0.1*Rand(1, 15);
\r
1068 rightdown = !rightdown;
\r
1070 rightclicked = true;
\r
1072 rightreleased = true;
\r
1075 mousepos += mousespeed;
\r
1078 s32 Rand(s32 min, s32 max)
\r
1080 return (myrand()%(max-min+1))+min;
\r
1083 bool keydown[KEY_KEY_CODES_COUNT];
\r
1089 bool rightclicked;
\r
1090 bool leftreleased;
\r
1091 bool rightreleased;
\r
1094 void updateViewingRange(f32 frametime_in, Client *client)
\r
1096 if(draw_control.range_all == true)
\r
1099 static f32 added_frametime = 0;
\r
1100 static s16 added_frames = 0;
\r
1102 added_frametime += frametime_in;
\r
1103 added_frames += 1;
\r
1105 // Actually this counter kind of sucks because frametime is busytime
\r
1106 static f32 counter = 0;
\r
1107 counter -= frametime_in;
\r
1113 /*dstream<<__FUNCTION_NAME
\r
1114 <<": Collected "<<added_frames<<" frames, total of "
\r
1115 <<added_frametime<<"s."<<std::endl;*/
\r
1117 /*dstream<<"draw_control.blocks_drawn="
\r
1118 <<draw_control.blocks_drawn
\r
1119 <<", draw_control.blocks_would_have_drawn="
\r
1120 <<draw_control.blocks_would_have_drawn
\r
1123 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1124 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1126 draw_control.wanted_min_range = range_min;
\r
1127 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1129 float block_draw_ratio = 1.0;
\r
1130 if(draw_control.blocks_would_have_drawn != 0)
\r
1132 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1133 / (float)draw_control.blocks_would_have_drawn;
\r
1136 // Calculate the average frametime in the case that all wanted
\r
1137 // blocks had been drawn
\r
1138 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1140 added_frametime = 0.0;
\r
1143 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1144 float wanted_frametime = 1.0 / wanted_fps;
\r
1146 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1147 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1149 // If needed frametime change is small, just return
\r
1150 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1152 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1156 float range = draw_control.wanted_range;
\r
1157 float new_range = range;
\r
1159 static s16 range_old = 0;
\r
1160 static f32 frametime_old = 0;
\r
1162 float d_range = range - range_old;
\r
1163 f32 d_frametime = frametime - frametime_old;
\r
1164 // A sane default of 30ms per 50 nodes of range
\r
1165 static f32 time_per_range = 30. / 50;
\r
1168 time_per_range = d_frametime / d_range;
\r
1171 // The minimum allowed calculated frametime-range derivative:
\r
1172 // Practically this sets the maximum speed of changing the range.
\r
1173 // The lower this value, the higher the maximum changing speed.
\r
1174 // A low value here results in wobbly range (0.001)
\r
1175 // A high value here results in slow changing range (0.0025)
\r
1176 // SUGG: This could be dynamically adjusted so that when
\r
1177 // the camera is turning, this is lower
\r
1178 //float min_time_per_range = 0.0015;
\r
1179 float min_time_per_range = 0.0010;
\r
1180 //float min_time_per_range = 0.05 / range;
\r
1181 if(time_per_range < min_time_per_range)
\r
1183 time_per_range = min_time_per_range;
\r
1184 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1188 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1191 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1192 // Dampen the change a bit to kill oscillations
\r
1193 //wanted_range_change *= 0.9;
\r
1194 //wanted_range_change *= 0.75;
\r
1195 wanted_range_change *= 0.5;
\r
1196 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1198 // If needed range change is very small, just return
\r
1199 if(fabs(wanted_range_change) < 0.001)
\r
1201 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1205 new_range += wanted_range_change;
\r
1206 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1208 //float new_range_unclamped = new_range;
\r
1209 if(new_range < range_min)
\r
1210 new_range = range_min;
\r
1211 if(new_range > range_max)
\r
1212 new_range = range_max;
\r
1214 /*if(new_range != new_range_unclamped)
\r
1215 dstream<<", clamped to "<<new_range<<std::endl;
\r
1217 dstream<<std::endl;*/
\r
1219 draw_control.wanted_range = new_range;
\r
1221 range_old = new_range;
\r
1222 frametime_old = frametime;
\r
1225 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1226 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1227 Inventory *inventory)
\r
1229 InventoryList *mainlist = inventory->getList("main");
\r
1230 if(mainlist == NULL)
\r
1232 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1236 s32 padding = imgsize/12;
\r
1237 //s32 height = imgsize + padding*2;
\r
1238 s32 width = itemcount*(imgsize+padding*2);
\r
1240 // Position of upper left corner of bar
\r
1241 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1243 // Draw background color
\r
1244 /*core::rect<s32> barrect(0,0,width,height);
\r
1246 video::SColor bgcolor(255,128,128,128);
\r
1247 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1249 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1251 for(s32 i=0; i<itemcount; i++)
\r
1253 InventoryItem *item = mainlist->getItem(i);
\r
1255 core::rect<s32> rect = imgrect + pos
\r
1256 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1258 if(g_selected_item == i)
\r
1260 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1261 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1262 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1267 video::SColor bgcolor2(128,0,0,0);
\r
1268 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1273 drawInventoryItem(driver, font, item, rect, NULL);
\r
1278 video::ITexture *g_map_plot_texture = NULL;
\r
1279 float g_map_plot_texture_scale = 4;
\r
1281 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1287 core::dimension2d<u32> dim(640,480);
\r
1288 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1290 for(u32 y=0; y<dim.Height; y++)
\r
1291 for(u32 x=0; x<dim.Width; x++)
\r
1293 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1294 pf *= g_map_plot_texture_scale;
\r
1296 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1299 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1300 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1302 // Screen coordinates that are based on multiples of
\r
1303 // 1000/g_map_plot_texture_scale and never negative
\r
1304 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1305 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1306 // Offset to center of image
\r
1307 ux -= dim.Width/2;
\r
1308 uy -= dim.Height/2;
\r
1310 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1311 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1312 c.set(255, 255, 255, 255);
\r
1313 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1314 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1315 c.set(255, 160, 160, 160);
\r
1316 else if(h < WATER_LEVEL - 0.5) // Water
\r
1317 c.set(255, 50, 50, 255);
\r
1319 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1320 || (h < WATER_LEVEL + 2
\r
1321 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1325 h = 1.0 - exp(-h);
\r
1327 video::SColor c1(255,237,201,175);
\r
1328 //video::SColor c2(255,20,20,20);
\r
1329 video::SColor c2(255,150,0,0);
\r
1330 c = c2.getInterpolated(c1, h);
\r
1336 h = 1.0 - exp(-h);
\r
1338 video::SColor c1(255,110,185,90);
\r
1339 //video::SColor c2(255,20,20,20);
\r
1340 video::SColor c2(255,150,0,0);
\r
1341 c = c2.getInterpolated(c1, h);
\r
1346 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1350 h = 1.0 - exp(-h);
\r
1352 video::SColor c1(255,237,201,175);
\r
1353 //video::SColor c2(255,20,20,20);
\r
1354 video::SColor c2(255,150,0,0);
\r
1355 c = c2.getInterpolated(c1, h);
\r
1359 else if(h < WATER_LEVEL + 2
\r
1360 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1361 c.set(255, 237, 201, 175);
\r
1363 else if(h < WATER_LEVEL + 10)
\r
1364 c.set(255, 50, 150, 50); // Green
\r
1365 else if(h < WATER_LEVEL + 20)
\r
1366 c.set(255, 110, 185, 50); // Yellowish green
\r
1367 else if(h < WATER_LEVEL + 40)
\r
1368 c.set(255, 180, 210, 50); // Greenish yellow
\r
1369 else if(h < WATER_LEVEL + 60)
\r
1370 c.set(255, 220, 220, 50); // Yellow
\r
1371 else if(h < WATER_LEVEL + 80)
\r
1372 c.set(255, 200, 200, 110); // Yellowish white
\r
1373 else if(h < WATER_LEVEL + 100)
\r
1374 c.set(255, 190, 190, 190); // Grey
\r
1376 c.set(255, 255, 255, 255); // White
\r
1384 h = 1.0 - exp(-h);
\r
1386 video::SColor c1(255,200,200,50);
\r
1387 video::SColor c2(255,0,150,0);
\r
1388 c = c1.getInterpolated(c2, h);
\r
1390 /*u32 a = (u32)(h*255);
\r
1394 c.set(255, a, a, a);*/
\r
1398 if(h >= WATER_LEVEL - 0.5
\r
1399 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1401 video::SColor c1(255,237,201,175);
\r
1402 c = c.getInterpolated(c1, 0.5);
\r
1406 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1409 video::SColor c1(255,255,0,0);
\r
1410 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1413 img->setPixel(x, y, c);
\r
1415 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1417 assert(g_map_plot_texture);
\r
1427 ChatLine(const std::wstring &a_text):
\r
1433 std::wstring text;
\r
1436 // These are defined global so that they're not optimized too much.
\r
1437 // Can't change them to volatile.
\r
1442 std::string tempstring;
\r
1443 std::string tempstring2;
\r
1448 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1449 TimeTaker timer("Testing std::string speed");
\r
1450 const u32 jj = 10000;
\r
1451 for(u32 j=0; j<jj; j++)
\r
1455 const u32 ii = 10;
\r
1456 for(u32 i=0; i<ii; i++){
\r
1457 tempstring2 += "asd";
\r
1459 for(u32 i=0; i<ii+1; i++){
\r
1460 tempstring += "asd";
\r
1461 if(tempstring == tempstring2)
\r
1467 dstream<<"All of the following tests should take around 100ms each."
\r
1471 TimeTaker timer("Testing floating-point conversion speed");
\r
1473 for(u32 i=0; i<4000000; i++){
\r
1480 TimeTaker timer("Testing floating-point vector speed");
\r
1482 tempv3f1 = v3f(1,2,3);
\r
1483 tempv3f2 = v3f(4,5,6);
\r
1484 for(u32 i=0; i<10000000; i++){
\r
1485 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1486 tempv3f2 += v3f(7,8,9);
\r
1491 TimeTaker timer("Testing core::map speed");
\r
1493 core::map<v2s16, f32> map1;
\r
1496 for(s16 y=0; y<ii; y++){
\r
1497 for(s16 x=0; x<ii; x++){
\r
1498 map1.insert(v2s16(x,y), tempf);
\r
1502 for(s16 y=ii-1; y>=0; y--){
\r
1503 for(s16 x=0; x<ii; x++){
\r
1504 tempf = map1[v2s16(x,y)];
\r
1510 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1511 TimeTaker timer("Testing mutex speed");
\r
1524 // Do at least 10ms
\r
1525 while(timer.getTime() < 10);
\r
1527 u32 dtime = timer.stop();
\r
1528 u32 per_ms = n / dtime;
\r
1529 std::cout<<"Done. "<<dtime<<"ms, "
\r
1530 <<per_ms<<"/ms"<<std::endl;
\r
1534 int main(int argc, char *argv[])
\r
1537 Parse command line
\r
1540 // List all allowed options
\r
1541 core::map<std::string, ValueSpec> allowed_options;
\r
1542 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1543 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1544 "Run server directly"));
\r
1545 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1546 "Load configuration from specified file"));
\r
1547 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1548 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1549 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1550 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1551 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1552 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1554 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1556 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1558 Settings cmd_args;
\r
1560 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1562 if(ret == false || cmd_args.getFlag("help"))
\r
1564 dstream<<"Allowed options:"<<std::endl;
\r
1565 for(core::map<std::string, ValueSpec>::Iterator
\r
1566 i = allowed_options.getIterator();
\r
1567 i.atEnd() == false; i++)
\r
1569 dstream<<" --"<<i.getNode()->getKey();
\r
1570 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1575 dstream<<" <value>";
\r
1577 dstream<<std::endl;
\r
1579 if(i.getNode()->getValue().help != NULL)
\r
1581 dstream<<" "<<i.getNode()->getValue().help
\r
1586 return cmd_args.getFlag("help") ? 0 : 1;
\r
1590 Low-level initialization
\r
1593 bool disable_stderr = false;
\r
1595 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1596 disable_stderr = true;
\r
1599 // Initialize debug streams
\r
1600 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1601 // Initialize debug stacks
\r
1602 debug_stacks_init();
\r
1604 DSTACK(__FUNCTION_NAME);
\r
1606 porting::signal_handler_init();
\r
1607 bool &kill = *porting::signal_handler_killstatus();
\r
1609 porting::initializePaths();
\r
1610 // Create user data directory
\r
1611 fs::CreateDir(porting::path_userdata);
\r
1613 // C-style stuff initialization
\r
1614 initializeMaterialProperties();
\r
1617 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1619 // Print startup message
\r
1620 dstream<<DTIME<<"minetest-c55"
\r
1621 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1622 <<", "<<BUILD_INFO
\r
1626 Basic initialization
\r
1629 // Initialize default settings
\r
1630 set_default_settings();
\r
1632 // Set locale. This is for forcing '.' as the decimal point.
\r
1633 std::locale::global(std::locale("C"));
\r
1634 // This enables printing all characters in bitmap font
\r
1635 setlocale(LC_CTYPE, "en_US");
\r
1637 // Initialize sockets
\r
1639 atexit(sockets_cleanup);
\r
1649 // Path of configuration file in use
\r
1650 std::string configpath = "";
\r
1652 if(cmd_args.exists("config"))
\r
1654 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1657 dstream<<"Could not read configuration from \""
\r
1658 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1661 configpath = cmd_args.get("config");
\r
1665 core::array<std::string> filenames;
\r
1666 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1667 #ifdef RUN_IN_PLACE
\r
1668 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1671 for(u32 i=0; i<filenames.size(); i++)
\r
1673 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1676 configpath = filenames[i];
\r
1681 // If no path found, use the first one (menu creates the file)
\r
1682 if(configpath == "")
\r
1683 configpath = filenames[0];
\r
1686 // Initialize random seed
\r
1691 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1693 These are needed for unit tests at least.
\r
1696 // Initial call with g_texturesource not set.
\r
1703 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1704 || cmd_args.getFlag("enable-unittests") == true)
\r
1709 /*for(s16 y=-100; y<100; y++)
\r
1710 for(s16 x=-100; x<100; x++)
\r
1712 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1722 if(cmd_args.exists("port"))
\r
1723 port = cmd_args.getU16("port");
\r
1724 else if(cmd_args.exists("port"))
\r
1725 port = g_settings.getU16("port");
\r
1728 std::string map_dir = porting::path_userdata+"/map";
\r
1729 if(cmd_args.exists("map-dir"))
\r
1730 map_dir = cmd_args.get("map-dir");
\r
1731 else if(g_settings.exists("map-dir"))
\r
1732 map_dir = g_settings.get("map-dir");
\r
1734 // Run dedicated server if asked to
\r
1735 if(cmd_args.getFlag("server"))
\r
1737 DSTACK("Dedicated server branch");
\r
1740 Server server(map_dir.c_str());
\r
1741 server.start(port);
\r
1744 dedicated_server_loop(server, kill);
\r
1753 // Address to connect to
\r
1754 std::string address = "";
\r
1756 if(cmd_args.exists("address"))
\r
1758 address = cmd_args.get("address");
\r
1762 address = g_settings.get("address");
\r
1765 std::string playername = g_settings.get("name");
\r
1767 // Resolution selection
\r
1769 bool fullscreen = false;
\r
1770 u16 screenW = g_settings.getU16("screenW");
\r
1771 u16 screenH = g_settings.getU16("screenH");
\r
1773 // Determine driver
\r
1775 video::E_DRIVER_TYPE driverType;
\r
1777 std::string driverstring = g_settings.get("video_driver");
\r
1779 if(driverstring == "null")
\r
1780 driverType = video::EDT_NULL;
\r
1781 else if(driverstring == "software")
\r
1782 driverType = video::EDT_SOFTWARE;
\r
1783 else if(driverstring == "burningsvideo")
\r
1784 driverType = video::EDT_BURNINGSVIDEO;
\r
1785 else if(driverstring == "direct3d8")
\r
1786 driverType = video::EDT_DIRECT3D8;
\r
1787 else if(driverstring == "direct3d9")
\r
1788 driverType = video::EDT_DIRECT3D9;
\r
1789 else if(driverstring == "opengl")
\r
1790 driverType = video::EDT_OPENGL;
\r
1793 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1794 "to opengl"<<std::endl;
\r
1795 driverType = video::EDT_OPENGL;
\r
1798 // create device and exit if creation failed
\r
1800 MyEventReceiver receiver;
\r
1802 IrrlichtDevice *device;
\r
1803 device = createDevice(driverType,
\r
1804 core::dimension2d<u32>(screenW, screenH),
\r
1805 16, fullscreen, false, false, &receiver);
\r
1808 return 1; // could not create selected driver.
\r
1810 g_device = device;
\r
1811 g_irrlicht = new IrrlichtWrapper(device);
\r
1812 TextureSource *texturesource = new TextureSource(device);
\r
1813 g_texturesource = texturesource;
\r
1816 Speed tests (done after irrlicht is loaded to get timer)
\r
1818 if(cmd_args.getFlag("speedtests"))
\r
1820 dstream<<"Running speed tests"<<std::endl;
\r
1825 device->setResizable(true);
\r
1827 bool random_input = g_settings.getBool("random_input")
\r
1828 || cmd_args.getFlag("random-input");
\r
1830 g_input = new RandomInputHandler();
\r
1832 g_input = new RealInputHandler(device, &receiver);
\r
1835 Continue initialization
\r
1838 video::IVideoDriver* driver = device->getVideoDriver();
\r
1841 This changes the minimum allowed number of vertices in a VBO.
\r
1844 //driver->setMinHardwareBufferVertexCount(50);
\r
1846 scene::ISceneManager* smgr = device->getSceneManager();
\r
1848 guienv = device->getGUIEnvironment();
\r
1849 gui::IGUISkin* skin = guienv->getSkin();
\r
1850 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1852 skin->setFont(font);
\r
1854 dstream<<"WARNING: Font file was not found."
\r
1855 " Using default font."<<std::endl;
\r
1856 // If font was not found, this will get us one
\r
1857 font = skin->getFont();
\r
1860 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1861 dstream<<"text_height="<<text_height<<std::endl;
\r
1863 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1864 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1865 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1866 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1867 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1868 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1871 Preload some textures and stuff
\r
1874 init_content_inventory_texture_paths();
\r
1875 init_mapnode(); // Second call with g_texturesource set
\r
1883 We need some kind of a root node to be able to add
\r
1884 custom gui elements directly on the screen.
\r
1885 Otherwise they won't be automatically drawn.
\r
1887 guiroot = guienv->addStaticText(L"",
\r
1888 core::rect<s32>(0, 0, 10000, 10000));
\r
1890 // First line of debug text
\r
1891 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1893 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1895 // Second line of debug text
\r
1896 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1898 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1901 // At the middle of the screen
\r
1902 // Object infos are shown in this
\r
1903 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1905 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1909 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1911 core::rect<s32>(0,0,0,0),
\r
1912 false, false); // Disable word wrap as of now
\r
1914 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1915 core::list<ChatLine> chat_lines;
\r
1918 If an error occurs, this is set to something and the
\r
1919 menu-game loop is restarted. It is then displayed before
\r
1922 std::wstring error_message = L"";
\r
1927 while(g_device->run() && kill == false)
\r
1930 // This is used for catching disconnects
\r
1935 Out-of-game menu loop.
\r
1937 Loop quits when menu returns proper parameters.
\r
1939 while(kill == false)
\r
1941 // Cursor can be non-visible when coming from the game
\r
1942 device->getCursorControl()->setVisible(true);
\r
1943 // Some stuff are left to scene manager when coming from the game
\r
1944 // (map at least?)
\r
1946 // Reset or hide the debug gui texts
\r
1947 guitext->setText(L"Minetest-c55");
\r
1948 guitext2->setVisible(false);
\r
1949 guitext_info->setVisible(false);
\r
1950 guitext_chat->setVisible(false);
\r
1952 // Initialize menu data
\r
1953 MainMenuData menudata;
\r
1954 menudata.address = narrow_to_wide(address);
\r
1955 menudata.name = narrow_to_wide(playername);
\r
1956 menudata.port = narrow_to_wide(itos(port));
\r
1957 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1959 GUIMainMenu *menu =
\r
1960 new GUIMainMenu(guienv, guiroot, -1,
\r
1961 &g_menumgr, &menudata, &g_gamecallback);
\r
1962 menu->allowFocusRemoval(true);
\r
1964 if(error_message != L"")
\r
1966 GUIMessageMenu *menu2 =
\r
1967 new GUIMessageMenu(guienv, guiroot, -1,
\r
1968 &g_menumgr, error_message.c_str());
\r
1970 error_message = L"";
\r
1973 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1975 dstream<<"Created main menu"<<std::endl;
\r
1977 while(g_device->run() && kill == false)
\r
1979 // Run global IrrlichtWrapper's main thread processing stuff
\r
1980 g_irrlicht->Run();
\r
1982 if(menu->getStatus() == true)
\r
1985 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1986 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1987 guienv->drawAll();
\r
1988 driver->endScene();
\r
1991 // Break out of menu-game loop to shut down cleanly
\r
1992 if(g_device->run() == false || kill == true)
\r
1995 dstream<<"Dropping main menu"<<std::endl;
\r
1999 // Delete map if requested
\r
2000 if(menudata.delete_map)
\r
2002 bool r = fs::RecursiveDeleteContent(map_dir);
\r
2004 error_message = L"Delete failed";
\r
2008 playername = wide_to_narrow(menudata.name);
\r
2009 address = wide_to_narrow(menudata.address);
\r
2010 port = stoi(wide_to_narrow(menudata.port));
\r
2011 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
2013 // Check for valid parameters, restart menu if invalid.
\r
2014 if(playername == "")
\r
2016 error_message = L"Name required.";
\r
2021 g_settings.set("name", playername);
\r
2022 g_settings.set("address", address);
\r
2023 g_settings.set("port", itos(port));
\r
2024 // Update configuration file
\r
2025 if(configpath != "")
\r
2026 g_settings.updateConfigFile(configpath.c_str());
\r
2028 // Continue to game
\r
2032 // Break out of menu-game loop to shut down cleanly
\r
2033 if(g_device->run() == false)
\r
2037 Make a scope here so that the client and the server and other
\r
2038 stuff gets removed when disconnected or the irrlicht device
\r
2043 // This is set to true at the end of the scope
\r
2044 g_irrlicht->Shutdown(false);
\r
2047 Draw "Loading" screen
\r
2049 const wchar_t *text = L"Loading and connecting...";
\r
2050 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2051 core::vector2d<s32> textsize(300, text_height);
\r
2052 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2054 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2055 text, textrect, false, false);
\r
2056 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2058 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2059 guienv->drawAll();
\r
2060 driver->endScene();
\r
2062 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2066 SharedPtr will delete it when it goes out of scope.
\r
2068 SharedPtr<Server> server;
\r
2069 if(address == ""){
\r
2070 server = new Server(map_dir);
\r
2071 server->start(port);
\r
2078 Client client(device, playername.c_str(), draw_control);
\r
2080 g_client = &client;
\r
2082 Address connect_address(0,0,0,0, port);
\r
2085 //connect_address.Resolve("localhost");
\r
2086 connect_address.setAddress(127,0,0,1);
\r
2088 connect_address.Resolve(address.c_str());
\r
2090 catch(ResolveError &e)
\r
2092 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2094 error_message = L"Couldn't resolve address";
\r
2095 gui_loadingtext->remove();
\r
2099 dstream<<DTIME<<"Connecting to server at ";
\r
2100 connect_address.print(&dstream);
\r
2101 dstream<<std::endl;
\r
2102 client.connect(connect_address);
\r
2105 while(client.connectedAndInitialized() == false)
\r
2108 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2109 guienv->drawAll();
\r
2110 driver->endScene();
\r
2112 // Update client and server
\r
2116 if(server != NULL)
\r
2117 server->step(0.1);
\r
2123 catch(con::PeerNotFoundException &e)
\r
2125 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2127 error_message = L"Connection timed out.";
\r
2128 gui_loadingtext->remove();
\r
2135 /*scene::ISceneNode* skybox;
\r
2136 skybox = smgr->addSkyBoxSceneNode(
\r
2137 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2138 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2139 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2140 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2141 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2142 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2145 Create the camera node
\r
2148 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2149 0, // Camera parent
\r
2150 v3f(BS*100, BS*2, BS*100), // Look from
\r
2151 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2155 if(camera == NULL)
\r
2158 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2159 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2160 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2162 camera->setFOV(FOV_ANGLE);
\r
2164 // Just so big a value that everything rendered is visible
\r
2165 camera->setFarValue(100000*BS);
\r
2168 Lighting test code. Doesn't quite work this way.
\r
2169 The CPU-computed lighting is good.
\r
2173 smgr->addLightSceneNode(NULL,
\r
2174 v3f(0, BS*1000000, 0),
\r
2175 video::SColorf(0.3,0.3,0.3),
\r
2178 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2180 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2181 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2184 f32 camera_yaw = 0; // "right/left"
\r
2185 f32 camera_pitch = 0; // "up/down"
\r
2191 gui_loadingtext->remove();
\r
2194 Add some gui stuff
\r
2197 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2198 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2199 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2200 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2202 // Test the text input system
\r
2203 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2205 /*GUIMessageMenu *menu =
\r
2206 new GUIMessageMenu(guienv, guiroot, -1,
\r
2211 // Launch pause menu
\r
2212 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2213 &g_menumgr))->drop();
\r
2216 guitext2->setVisible(true);
\r
2217 guitext_info->setVisible(true);
\r
2218 guitext_chat->setVisible(true);
\r
2220 //s32 guitext_chat_pad_bottom = 70;
\r
2222 v2u32 screensize(0,0);
\r
2223 v2u32 last_screensize(0,0);
\r
2226 Some statistics are collected in these
\r
2229 u32 beginscenetime = 0;
\r
2230 u32 scenetime = 0;
\r
2231 u32 endscenetime = 0;
\r
2234 //throw con::PeerNotFoundException("lol");
\r
2236 core::list<float> frametime_log;
\r
2242 bool first_loop_after_window_activation = true;
\r
2244 // Time is in milliseconds
\r
2245 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2246 // NOTE: So we have to use getTime() and call run()s between them
\r
2247 u32 lasttime = device->getTimer()->getTime();
\r
2249 while(device->run() && kill == false)
\r
2251 if(g_disconnect_requested)
\r
2253 g_disconnect_requested = false;
\r
2258 Run global IrrlichtWrapper's main thread processing stuff
\r
2260 g_irrlicht->Run();
\r
2263 Process TextureSource's queue
\r
2265 texturesource->processQueue();
\r
2268 Random calculations
\r
2270 last_screensize = screensize;
\r
2271 screensize = driver->getScreenSize();
\r
2272 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2273 //bool screensize_changed = screensize != last_screensize;
\r
2275 // Hilight boxes collected during the loop and displayed
\r
2276 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2279 std::wstring infotext;
\r
2281 // When screen size changes, update positions and sizes of stuff
\r
2282 /*if(screensize_changed)
\r
2284 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2285 quick_inventory->updatePosition(pos);
\r
2288 //TimeTaker //timer1("//timer1");
\r
2290 // Time of frame without fps limit
\r
2294 // not using getRealTime is necessary for wine
\r
2295 u32 time = device->getTimer()->getTime();
\r
2296 if(time > lasttime)
\r
2297 busytime_u32 = time - lasttime;
\r
2300 busytime = busytime_u32 / 1000.0;
\r
2303 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2305 // Absolutelu necessary for wine!
\r
2312 updateViewingRange(busytime, &client);
\r
2319 float fps_max = g_settings.getFloat("fps_max");
\r
2320 u32 frametime_min = 1000./fps_max;
\r
2322 if(busytime_u32 < frametime_min)
\r
2324 u32 sleeptime = frametime_min - busytime_u32;
\r
2325 device->sleep(sleeptime);
\r
2329 // Absolutelu necessary for wine!
\r
2333 Time difference calculation
\r
2335 f32 dtime; // in seconds
\r
2337 u32 time = device->getTimer()->getTime();
\r
2338 if(time > lasttime)
\r
2339 dtime = (time - lasttime) / 1000.0;
\r
2345 Log frametime for visualization
\r
2347 frametime_log.push_back(dtime);
\r
2348 if(frametime_log.size() > 100)
\r
2350 core::list<float>::Iterator i = frametime_log.begin();
\r
2351 frametime_log.erase(i);
\r
2355 Visualize frametime in terminal
\r
2357 /*for(u32 i=0; i<dtime*400; i++)
\r
2359 std::cout<<std::endl;*/
\r
2362 Time average and jitter calculation
\r
2365 static f32 dtime_avg1 = 0.0;
\r
2366 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2367 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2369 static f32 dtime_jitter1_max_sample = 0.0;
\r
2370 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2372 static f32 jitter1_max = 0.0;
\r
2373 static f32 counter = 0.0;
\r
2374 if(dtime_jitter1 > jitter1_max)
\r
2375 jitter1_max = dtime_jitter1;
\r
2380 dtime_jitter1_max_sample = jitter1_max;
\r
2381 dtime_jitter1_max_fraction
\r
2382 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2383 jitter1_max = 0.0;
\r
2388 Busytime average and jitter calculation
\r
2391 static f32 busytime_avg1 = 0.0;
\r
2392 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2393 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2395 static f32 busytime_jitter1_max_sample = 0.0;
\r
2396 static f32 busytime_jitter1_min_sample = 0.0;
\r
2398 static f32 jitter1_max = 0.0;
\r
2399 static f32 jitter1_min = 0.0;
\r
2400 static f32 counter = 0.0;
\r
2401 if(busytime_jitter1 > jitter1_max)
\r
2402 jitter1_max = busytime_jitter1;
\r
2403 if(busytime_jitter1 < jitter1_min)
\r
2404 jitter1_min = busytime_jitter1;
\r
2406 if(counter > 0.0){
\r
2408 busytime_jitter1_max_sample = jitter1_max;
\r
2409 busytime_jitter1_min_sample = jitter1_min;
\r
2410 jitter1_max = 0.0;
\r
2411 jitter1_min = 0.0;
\r
2416 Debug info for client
\r
2419 static float counter = 0.0;
\r
2424 client.printDebugInfo(std::cout);
\r
2429 Input handler step()
\r
2431 g_input->step(dtime);
\r
2438 Player speed control
\r
2447 bool a_superspeed,
\r
2451 PlayerControl control(
\r
2452 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2453 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2454 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2455 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2456 g_input->isKeyDown(irr::KEY_SPACE),
\r
2457 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2458 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2459 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2463 client.setPlayerControl(control);
\r
2467 Process environment
\r
2471 //TimeTaker timer("client.step(dtime)");
\r
2472 client.step(dtime);
\r
2473 //client.step(dtime_avg1);
\r
2476 if(server != NULL)
\r
2478 //TimeTaker timer("server->step(dtime)");
\r
2479 server->step(dtime);
\r
2482 v3f player_position = client.getPlayerPosition();
\r
2484 //TimeTaker //timer2("//timer2");
\r
2487 Mouse and camera control
\r
2490 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2493 device->getCursorControl()->setVisible(false);
\r
2495 if(first_loop_after_window_activation){
\r
2496 //std::cout<<"window active, first loop"<<std::endl;
\r
2497 first_loop_after_window_activation = false;
\r
2500 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2501 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2502 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2503 camera_yaw -= dx*0.2;
\r
2504 camera_pitch += dy*0.2;
\r
2505 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2506 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2508 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2511 device->getCursorControl()->setVisible(true);
\r
2513 //std::cout<<"window inactive"<<std::endl;
\r
2514 first_loop_after_window_activation = true;
\r
2517 camera_yaw = wrapDegrees(camera_yaw);
\r
2518 camera_pitch = wrapDegrees(camera_pitch);
\r
2520 v3f camera_direction = v3f(0,0,1);
\r
2521 camera_direction.rotateYZBy(camera_pitch);
\r
2522 camera_direction.rotateXZBy(camera_yaw);
\r
2524 // This is at the height of the eyes of the current figure
\r
2525 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2526 // This is more like in minecraft
\r
2527 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2529 camera->setPosition(camera_position);
\r
2530 // *100.0 helps in large map coordinates
\r
2531 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2533 if(FIELD_OF_VIEW_TEST){
\r
2534 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2537 //TimeTaker timer("client.updateCamera");
\r
2538 client.updateCamera(camera_position, camera_direction);
\r
2542 //TimeTaker //timer3("//timer3");
\r
2545 Calculate what block is the crosshair pointing to
\r
2548 //u32 t1 = device->getTimer()->getRealTime();
\r
2550 //f32 d = 4; // max. distance
\r
2551 f32 d = 4; // max. distance
\r
2552 core::line3d<f32> shootline(camera_position,
\r
2553 camera_position + camera_direction * BS * (d+1));
\r
2555 MapBlockObject *selected_object = client.getSelectedObject
\r
2556 (d*BS, camera_position, shootline);
\r
2559 If it's pointing to a MapBlockObject
\r
2562 if(selected_object != NULL)
\r
2564 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2566 core::aabbox3d<f32> box_on_map
\r
2567 = selected_object->getSelectionBoxOnMap();
\r
2569 hilightboxes.push_back(box_on_map);
\r
2571 infotext = narrow_to_wide(selected_object->infoText());
\r
2573 if(g_input->getLeftClicked())
\r
2575 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2576 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2577 selected_object->getId(), g_selected_item);
\r
2579 else if(g_input->getRightClicked())
\r
2581 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2583 Check if we want to modify the object ourselves
\r
2585 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2587 dstream<<"Sign object right-clicked"<<std::endl;
\r
2589 if(random_input == false)
\r
2591 // Get a new text for it
\r
2593 TextDest *dest = new TextDestSign(
\r
2594 selected_object->getBlock()->getPos(),
\r
2595 selected_object->getId(),
\r
2598 SignObject *sign_object = (SignObject*)selected_object;
\r
2600 std::wstring wtext =
\r
2601 narrow_to_wide(sign_object->getText());
\r
2603 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2609 Otherwise pass the event to the server as-is
\r
2613 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2614 selected_object->getId(), g_selected_item);
\r
2618 else // selected_object == NULL
\r
2622 Find out which node we are pointing at
\r
2625 bool nodefound = false;
\r
2627 v3s16 neighbourpos;
\r
2628 core::aabbox3d<f32> nodehilightbox;
\r
2629 f32 mindistance = BS * 1001;
\r
2631 v3s16 pos_i = floatToInt(player_position, BS);
\r
2633 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2637 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2638 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2639 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2640 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2641 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2642 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2644 for(s16 y = ystart; y <= yend; y++)
\r
2645 for(s16 z = zstart; z <= zend; z++)
\r
2646 for(s16 x = xstart; x <= xend; x++)
\r
2651 n = client.getNode(v3s16(x,y,z));
\r
2652 if(content_pointable(n.d) == false)
\r
2655 catch(InvalidPositionException &e)
\r
2661 v3f npf = intToFloat(np, BS);
\r
2666 v3s16(0,0,1), // back
\r
2667 v3s16(0,1,0), // top
\r
2668 v3s16(1,0,0), // right
\r
2669 v3s16(0,0,-1), // front
\r
2670 v3s16(0,-1,0), // bottom
\r
2671 v3s16(-1,0,0), // left
\r
2677 if(n.d == CONTENT_TORCH)
\r
2679 v3s16 dir = unpackDir(n.dir);
\r
2680 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2681 dir_f *= BS/2 - BS/6 - BS/20;
\r
2682 v3f cpf = npf + dir_f;
\r
2683 f32 distance = (cpf - camera_position).getLength();
\r
2685 core::aabbox3d<f32> box;
\r
2688 if(dir == v3s16(0,-1,0))
\r
2690 box = core::aabbox3d<f32>(
\r
2691 npf - v3f(BS/6, BS/2, BS/6),
\r
2692 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2696 else if(dir == v3s16(0,1,0))
\r
2698 box = core::aabbox3d<f32>(
\r
2699 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2700 npf + v3f(BS/6, BS/2, BS/6)
\r
2706 box = core::aabbox3d<f32>(
\r
2707 cpf - v3f(BS/6, BS/3, BS/6),
\r
2708 cpf + v3f(BS/6, BS/3, BS/6)
\r
2712 if(distance < mindistance)
\r
2714 if(box.intersectsWithLine(shootline))
\r
2718 neighbourpos = np;
\r
2719 mindistance = distance;
\r
2720 nodehilightbox = box;
\r
2729 for(u16 i=0; i<6; i++)
\r
2731 v3f dir_f = v3f(dirs[i].X,
\r
2732 dirs[i].Y, dirs[i].Z);
\r
2733 v3f centerpoint = npf + dir_f * BS/2;
\r
2735 (centerpoint - camera_position).getLength();
\r
2737 if(distance < mindistance)
\r
2739 core::CMatrix4<f32> m;
\r
2740 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2742 // This is the back face
\r
2743 v3f corners[2] = {
\r
2744 v3f(BS/2, BS/2, BS/2),
\r
2745 v3f(-BS/2, -BS/2, BS/2+d)
\r
2748 for(u16 j=0; j<2; j++)
\r
2750 m.rotateVect(corners[j]);
\r
2751 corners[j] += npf;
\r
2754 core::aabbox3d<f32> facebox(corners[0]);
\r
2755 facebox.addInternalPoint(corners[1]);
\r
2757 if(facebox.intersectsWithLine(shootline))
\r
2761 neighbourpos = np + dirs[i];
\r
2762 mindistance = distance;
\r
2764 //nodehilightbox = facebox;
\r
2766 const float d = 0.502;
\r
2767 core::aabbox3d<f32> nodebox
\r
2768 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2769 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2770 nodebox.MinEdge += nodepos_f;
\r
2771 nodebox.MaxEdge += nodepos_f;
\r
2772 nodehilightbox = nodebox;
\r
2774 } // if distance < mindistance
\r
2776 } // regular block
\r
2779 static float nodig_delay_counter = 0.0;
\r
2783 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2785 static float dig_time = 0.0;
\r
2786 static u16 dig_index = 0;
\r
2788 // Visualize selection
\r
2790 hilightboxes.push_back(nodehilightbox);
\r
2794 if(g_input->getLeftReleased())
\r
2796 client.clearTempMod(nodepos);
\r
2800 if(nodig_delay_counter > 0.0)
\r
2802 nodig_delay_counter -= dtime;
\r
2806 if(nodepos != nodepos_old)
\r
2808 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2809 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2811 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2813 client.clearTempMod(nodepos_old);
\r
2818 if(g_input->getLeftClicked() ||
\r
2819 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2821 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2822 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2824 if(g_input->getLeftClicked())
\r
2826 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2828 if(g_input->getLeftState())
\r
2830 MapNode n = client.getNode(nodepos);
\r
2832 // Get tool name. Default is "" = bare hands
\r
2833 std::string toolname = "";
\r
2834 InventoryList *mlist = local_inventory.getList("main");
\r
2837 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2838 if(item && (std::string)item->getName() == "ToolItem")
\r
2840 ToolItem *titem = (ToolItem*)item;
\r
2841 toolname = titem->getToolName();
\r
2845 // Get digging properties for material and tool
\r
2846 u8 material = n.d;
\r
2847 DiggingProperties prop =
\r
2848 getDiggingProperties(material, toolname);
\r
2850 float dig_time_complete = 0.0;
\r
2852 if(prop.diggable == false)
\r
2854 /*dstream<<"Material "<<(int)material
\r
2855 <<" not diggable with \""
\r
2856 <<toolname<<"\""<<std::endl;*/
\r
2857 // I guess nobody will wait for this long
\r
2858 dig_time_complete = 10000000.0;
\r
2862 dig_time_complete = prop.time;
\r
2865 if(dig_time_complete >= 0.001)
\r
2867 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2868 * dig_time/dig_time_complete);
\r
2870 // This is for torches
\r
2873 dig_index = CRACK_ANIMATION_LENGTH;
\r
2876 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2878 //TimeTaker timer("client.setTempMod");
\r
2879 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2880 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2884 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2885 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2886 client.clearTempMod(nodepos);
\r
2887 client.removeNode(nodepos);
\r
2891 nodig_delay_counter = dig_time_complete
\r
2892 / (float)CRACK_ANIMATION_LENGTH;
\r
2894 // We don't want a corresponding delay to
\r
2895 // very time consuming nodes
\r
2896 if(nodig_delay_counter > 0.5)
\r
2898 nodig_delay_counter = 0.5;
\r
2900 // We want a slight delay to very little
\r
2901 // time consuming nodes
\r
2902 float mindelay = 0.15;
\r
2903 if(nodig_delay_counter < mindelay)
\r
2905 nodig_delay_counter = mindelay;
\r
2909 dig_time += dtime;
\r
2913 if(g_input->getRightClicked())
\r
2915 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2916 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2919 nodepos_old = nodepos;
\r
2924 } // selected_object == NULL
\r
2926 g_input->resetLeftClicked();
\r
2927 g_input->resetRightClicked();
\r
2929 if(g_input->getLeftReleased())
\r
2931 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2933 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2935 if(g_input->getRightReleased())
\r
2937 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2941 g_input->resetLeftReleased();
\r
2942 g_input->resetRightReleased();
\r
2945 Calculate stuff for drawing
\r
2948 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2950 u32 daynight_ratio = client.getDayNightRatio();
\r
2951 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2952 video::SColor bgcolor = video::SColor(
\r
2954 skycolor.getRed() * l / 255,
\r
2955 skycolor.getGreen() * l / 255,
\r
2956 skycolor.getBlue() * l / 255);
\r
2962 if(g_settings.getBool("enable_fog") == true)
\r
2964 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2965 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2966 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2967 if(draw_control.range_all)
\r
2968 range = 100000*BS;
\r
2972 video::EFT_FOG_LINEAR,
\r
2976 false, // pixel fog
\r
2977 false // range fog
\r
2984 video::EFT_FOG_LINEAR,
\r
2988 false, // pixel fog
\r
2989 false // range fog
\r
2995 Update gui stuff (0ms)
\r
2998 //TimeTaker guiupdatetimer("Gui updating");
\r
3001 static float drawtime_avg = 0;
\r
3002 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
3003 static float beginscenetime_avg = 0;
\r
3004 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
3005 static float scenetime_avg = 0;
\r
3006 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
3007 static float endscenetime_avg = 0;
\r
3008 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3010 char temptext[300];
\r
3011 snprintf(temptext, 300, "Minetest-c55 ("
\r
3013 ", R: range_all=%i"
\r
3015 " drawtime=%.0f, beginscenetime=%.0f"
\r
3016 ", scenetime=%.0f, endscenetime=%.0f",
\r
3018 draw_control.range_all,
\r
3020 beginscenetime_avg,
\r
3025 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3029 char temptext[300];
\r
3030 snprintf(temptext, 300,
\r
3031 "(% .1f, % .1f, % .1f)"
\r
3032 " (% .3f < btime_jitter < % .3f"
\r
3033 ", dtime_jitter = % .1f %%"
\r
3034 ", v_range = %.1f)",
\r
3035 player_position.X/BS,
\r
3036 player_position.Y/BS,
\r
3037 player_position.Z/BS,
\r
3038 busytime_jitter1_min_sample,
\r
3039 busytime_jitter1_max_sample,
\r
3040 dtime_jitter1_max_fraction * 100.0,
\r
3041 draw_control.wanted_range
\r
3044 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3048 guitext_info->setText(infotext.c_str());
\r
3052 Get chat messages from client
\r
3055 // Get new messages
\r
3056 std::wstring message;
\r
3057 while(client.getChatMessage(message))
\r
3059 chat_lines.push_back(ChatLine(message));
\r
3060 /*if(chat_lines.size() > 6)
\r
3062 core::list<ChatLine>::Iterator
\r
3063 i = chat_lines.begin();
\r
3064 chat_lines.erase(i);
\r
3067 // Append them to form the whole static text and throw
\r
3068 // it to the gui element
\r
3069 std::wstring whole;
\r
3070 // This will correspond to the line number counted from
\r
3071 // top to bottom, from size-1 to 0
\r
3072 s16 line_number = chat_lines.size();
\r
3073 // Count of messages to be removed from the top
\r
3074 u16 to_be_removed_count = 0;
\r
3075 for(core::list<ChatLine>::Iterator
\r
3076 i = chat_lines.begin();
\r
3077 i != chat_lines.end(); i++)
\r
3079 // After this, line number is valid for this loop
\r
3082 (*i).age += dtime;
\r
3084 This results in a maximum age of 60*6 to the
\r
3085 lowermost line and a maximum of 6 lines
\r
3087 float allowed_age = (6-line_number) * 60.0;
\r
3089 if((*i).age > allowed_age)
\r
3091 to_be_removed_count++;
\r
3094 whole += (*i).text + L'\n';
\r
3096 for(u16 i=0; i<to_be_removed_count; i++)
\r
3098 core::list<ChatLine>::Iterator
\r
3099 it = chat_lines.begin();
\r
3100 chat_lines.erase(it);
\r
3102 guitext_chat->setText(whole.c_str());
\r
3104 // Update gui element size and position
\r
3106 /*core::rect<s32> rect(
\r
3108 screensize.Y - guitext_chat_pad_bottom
\r
3109 - text_height*chat_lines.size(),
\r
3110 screensize.X - 10,
\r
3111 screensize.Y - guitext_chat_pad_bottom
\r
3113 core::rect<s32> rect(
\r
3116 screensize.X - 10,
\r
3117 50 + text_height*chat_lines.size()
\r
3120 guitext_chat->setRelativePosition(rect);
\r
3122 if(chat_lines.size() == 0)
\r
3123 guitext_chat->setVisible(false);
\r
3125 guitext_chat->setVisible(true);
\r
3132 static u16 old_selected_item = 65535;
\r
3133 if(client.getLocalInventoryUpdated()
\r
3134 || g_selected_item != old_selected_item)
\r
3136 old_selected_item = g_selected_item;
\r
3137 //std::cout<<"Updating local inventory"<<std::endl;
\r
3138 client.getLocalInventory(local_inventory);
\r
3139 /*quick_inventory->setSelection(g_selected_item);
\r
3140 quick_inventory->update();*/
\r
3144 Send actions returned by the inventory menu
\r
3146 while(inventory_action_queue.size() != 0)
\r
3148 InventoryAction *a = inventory_action_queue.pop_front();
\r
3150 client.sendInventoryAction(a);
\r
3159 TimeTaker drawtimer("Drawing");
\r
3163 TimeTaker timer("beginScene");
\r
3164 driver->beginScene(true, true, bgcolor);
\r
3165 //driver->beginScene(false, true, bgcolor);
\r
3166 beginscenetime = timer.stop(true);
\r
3171 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3174 TimeTaker timer("smgr");
\r
3176 scenetime = timer.stop(true);
\r
3180 //TimeTaker timer9("auxiliary drawings");
\r
3184 //TimeTaker //timer10("//timer10");
\r
3186 video::SMaterial m;
\r
3187 //m.Thickness = 10;
\r
3189 m.Lighting = false;
\r
3190 driver->setMaterial(m);
\r
3192 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3194 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3195 i != hilightboxes.end(); i++)
\r
3197 /*std::cout<<"hilightbox min="
\r
3198 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3200 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3202 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3208 if(g_settings.getBool("frametime_graph") == true)
\r
3211 for(core::list<float>::Iterator
\r
3212 i = frametime_log.begin();
\r
3213 i != frametime_log.end();
\r
3216 driver->draw2DLine(v2s32(x,50),
\r
3217 v2s32(x,50+(*i)*1000),
\r
3218 video::SColor(255,255,255,255));
\r
3226 if(g_show_map_plot && g_map_plot_texture)
\r
3228 core::dimension2d<u32> drawdim(640,480);
\r
3229 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3231 (screensize.X-drawdim.Width)/2,
\r
3232 (screensize.Y-drawdim.Height)/2
\r
3234 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3235 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3241 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3242 displaycenter + core::vector2d<s32>(10,0),
\r
3243 video::SColor(255,255,255,255));
\r
3244 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3245 displaycenter + core::vector2d<s32>(0,10),
\r
3246 video::SColor(255,255,255,255));
\r
3251 //TimeTaker //timer11("//timer11");
\r
3257 guienv->drawAll();
\r
3263 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3264 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3269 TimeTaker timer("endScene");
\r
3270 driver->endScene();
\r
3271 endscenetime = timer.stop(true);
\r
3274 drawtime = drawtimer.stop(true);
\r
3281 Refresh map plot if player has moved considerably
\r
3283 if(g_refresh_map_plot)
\r
3285 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3286 v3f p = client.getPlayerPosition() / BS;
\r
3287 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3289 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3290 old_player_pos = p;
\r
3292 g_refresh_map_plot = false;
\r
3295 static s16 lastFPS = 0;
\r
3296 //u16 fps = driver->getFPS();
\r
3297 u16 fps = (1.0/dtime_avg1);
\r
3299 if (lastFPS != fps)
\r
3301 core::stringw str = L"Minetest [";
\r
3302 str += driver->getName();
\r
3306 device->setWindowCaption(str.c_str());
\r
3312 device->yield();*/
\r
3315 //delete quick_inventory;
\r
3318 Disable texture fetches and other stuff that is queued
\r
3319 to be processed by the main loop.
\r
3321 This has to be done before client goes out of scope.
\r
3323 g_irrlicht->Shutdown(true);
\r
3325 } // client and server are deleted at this point
\r
3328 catch(con::PeerNotFoundException &e)
\r
3330 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3331 error_message = L"Connection timed out.";
\r
3334 } // Menu-game loop
\r
3339 In the end, delete the Irrlicht device.
\r
3344 Update configuration file
\r
3346 /*if(configpath != "")
\r
3348 g_settings.updateConfigFile(configpath.c_str());
\r
3351 END_DEBUG_EXCEPTION_HANDLER
\r
3353 debugstreams_deinit();
\r