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
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 * Possibly add some kind of erosion and other stuff
\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
278 * Make an "environment metafile" to store at least time of day
\r
279 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
280 - Or maybe move content_features to material.{h,cpp}?
\r
282 Make a system for pregenerating quick information for mapblocks, so
\r
283 that the client can show them as cubes before they are actually sent
\r
285 * Optimize VoxelManipulator lighting implementation by using indices
\r
286 in place of coordinates?
\r
288 Making it more portable:
\r
289 ------------------------
\r
290 * Some MSVC: std::sto* are defined without a namespace and collide
\r
291 with the ones in utility.h
\r
293 ======================================================================
\r
298 Setting this to 1 enables a special camera mode that forces
\r
299 the renderers to think that the camera statically points from
\r
300 the starting place to a static direction.
\r
302 This allows one to move around with the player and see what
\r
303 is actually drawn behind solid things and behind the player.
\r
305 #define FIELD_OF_VIEW_TEST 0
\r
309 #pragma message ("Disabling unit tests")
\r
311 #warning "Disabling unit tests"
\r
313 // Disable unit tests
\r
314 #define ENABLE_TESTS 0
\r
316 // Enable unit tests
\r
317 #define ENABLE_TESTS 1
\r
321 #pragma comment(lib, "Irrlicht.lib")
\r
322 //#pragma comment(lib, "jthread.lib")
\r
323 #pragma comment(lib, "zlibwapi.lib")
\r
324 #pragma comment(lib, "Shell32.lib")
\r
325 // This would get rid of the console window
\r
326 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
329 #include <iostream>
\r
331 #include <jmutexautolock.h>
\r
332 #include <locale.h>
\r
334 #include "common_irrlicht.h"
\r
337 #include "player.h"
\r
339 //#include "environment.h"
\r
340 #include "server.h"
\r
341 #include "client.h"
\r
342 //#include "serialization.h"
\r
343 #include "constants.h"
\r
344 //#include "strfnd.h"
\r
345 #include "porting.h"
\r
346 #include "irrlichtwrapper.h"
\r
347 #include "gettime.h"
\r
348 #include "porting.h"
\r
349 #include "guiPauseMenu.h"
\r
350 #include "guiInventoryMenu.h"
\r
351 #include "guiTextInputMenu.h"
\r
352 #include "materials.h"
\r
353 #include "guiMessageMenu.h"
\r
354 #include "filesys.h"
\r
355 #include "config.h"
\r
356 #include "guiMainMenu.h"
\r
357 #include "mineral.h"
\r
361 // TODO: Remove this
\r
362 IrrlichtWrapper *g_irrlicht = NULL;
\r
364 // This makes textures
\r
365 ITextureSource *g_texturesource = NULL;
\r
367 MapDrawControl draw_control;
\r
371 These are loaded from the config file.
\r
374 Settings g_settings;
\r
376 extern void set_default_settings();
\r
382 IrrlichtDevice *g_device = NULL;
\r
383 Client *g_client = NULL;
\r
385 /*const s16 quickinv_size = 40;
\r
386 const s16 quickinv_padding = 8;
\r
387 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
388 const s16 quickinv_outer_padding = 4;
\r
389 const s16 quickinv_itemcount = 8;*/
\r
391 const s32 hotbar_itemcount = 8;
\r
392 const s32 hotbar_imagesize = 36;
\r
398 gui::IGUIEnvironment* guienv = NULL;
\r
399 gui::IGUIStaticText *guiroot = NULL;
\r
401 class MainMenuManager : public IMenuManager
\r
404 virtual void createdMenu(GUIModalMenu *menu)
\r
406 for(core::list<GUIModalMenu*>::Iterator
\r
407 i = m_stack.begin();
\r
408 i != m_stack.end(); i++)
\r
410 assert(*i != menu);
\r
413 if(m_stack.size() != 0)
\r
414 (*m_stack.getLast())->setVisible(false);
\r
415 m_stack.push_back(menu);
\r
418 virtual void deletingMenu(GUIModalMenu *menu)
\r
420 // Remove all entries if there are duplicates
\r
421 bool removed_entry;
\r
423 removed_entry = false;
\r
424 for(core::list<GUIModalMenu*>::Iterator
\r
425 i = m_stack.begin();
\r
426 i != m_stack.end(); i++)
\r
431 removed_entry = true;
\r
435 }while(removed_entry);
\r
437 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
438 assert(*i == menu);
\r
439 m_stack.erase(i);*/
\r
441 if(m_stack.size() != 0)
\r
442 (*m_stack.getLast())->setVisible(true);
\r
447 return m_stack.size();
\r
450 core::list<GUIModalMenu*> m_stack;
\r
453 MainMenuManager g_menumgr;
\r
455 bool noMenuActive()
\r
457 return (g_menumgr.menuCount() == 0);
\r
460 bool g_disconnect_requested = false;
\r
462 class MainGameCallback : public IGameCallback
\r
465 virtual void exitToOS()
\r
467 g_device->closeDevice();
\r
470 virtual void disconnect()
\r
472 g_disconnect_requested = true;
\r
476 MainGameCallback g_gamecallback;
\r
478 // Inventory actions from the menu are buffered here before sending
\r
479 Queue<InventoryAction*> inventory_action_queue;
\r
480 // This is a copy of the inventory that the client's environment has
\r
481 Inventory local_inventory;
\r
483 u16 g_selected_item = 0;
\r
485 bool g_show_map_plot = false;
\r
486 bool g_refresh_map_plot = false;
\r
493 std::ostream *dout_con_ptr = &dummyout;
\r
494 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
495 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
496 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
497 //std::ostream *dout_con_ptr = &dstream;
\r
498 //std::ostream *derr_con_ptr = &dstream;
\r
501 std::ostream *dout_server_ptr = &dstream;
\r
502 std::ostream *derr_server_ptr = &dstream;
\r
505 std::ostream *dout_client_ptr = &dstream;
\r
506 std::ostream *derr_client_ptr = &dstream;
\r
509 gettime.h implementation
\r
515 Use irrlicht because it is more precise than porting.h's
\r
518 if(g_irrlicht == NULL)
\r
520 return g_irrlicht->getTime();
\r
527 struct TextDestSign : public TextDest
\r
529 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
531 m_blockpos = blockpos;
\r
535 void gotText(std::wstring text)
\r
537 std::string ntext = wide_to_narrow(text);
\r
538 dstream<<"Changing text of a sign object: "
\r
539 <<ntext<<std::endl;
\r
540 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
548 struct TextDestChat : public TextDest
\r
550 TextDestChat(Client *client)
\r
554 void gotText(std::wstring text)
\r
556 // Discard empty line
\r
560 // Parse command (server command starts with "/#")
\r
561 if(text[0] == L'/' && text[1] != L'#')
\r
563 std::wstring reply = L"Local: ";
\r
565 reply += L"Local commands not yet supported. "
\r
566 L"Server prefix is \"/#\".";
\r
568 m_client->addChatMessage(reply);
\r
573 m_client->sendChatMessage(text);
\r
575 m_client->addChatMessage(text);
\r
581 class MyEventReceiver : public IEventReceiver
\r
584 // This is the one method that we have to implement
\r
585 virtual bool OnEvent(const SEvent& event)
\r
588 React to nothing here if a menu is active
\r
590 if(noMenuActive() == false)
\r
596 // Remember whether each key is down or up
\r
597 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
599 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
601 if(event.KeyInput.PressedDown)
\r
603 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
604 if(g_show_map_plot)
\r
606 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
607 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
609 g_show_map_plot = false;
\r
618 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
620 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
622 dstream<<DTIME<<"MyEventReceiver: "
\r
623 <<"Launching pause menu"<<std::endl;
\r
624 // It will delete itself by itself
\r
625 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
626 &g_menumgr))->drop();
\r
629 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
631 dstream<<DTIME<<"MyEventReceiver: "
\r
632 <<"Launching inventory"<<std::endl;
\r
633 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
634 &local_inventory, &inventory_action_queue,
\r
635 &g_menumgr))->drop();
\r
638 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
640 TextDest *dest = new TextDestChat(g_client);
\r
642 (new GUITextInputMenu(guienv, guiroot, -1,
\r
649 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
650 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
652 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
653 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
655 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
656 g_selected_item = s1-1;
\r
657 dstream<<DTIME<<"Selected item: "
\r
658 <<g_selected_item<<std::endl;
\r
661 // Viewing range selection
\r
662 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
664 if(draw_control.range_all)
\r
666 draw_control.range_all = false;
\r
667 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
671 draw_control.range_all = true;
\r
672 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
676 // Print debug stacks
\r
677 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
679 dstream<<"-----------------------------------------"
\r
681 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
682 dstream<<"-----------------------------------------"
\r
684 debug_stacks_print();
\r
688 if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
690 dstream<<"Map plot requested"<<std::endl;
\r
691 g_show_map_plot = !g_show_map_plot;
\r
692 if(g_show_map_plot)
\r
693 g_refresh_map_plot = true;
\r
699 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
701 if(noMenuActive() == false)
\r
703 left_active = false;
\r
704 middle_active = false;
\r
705 right_active = false;
\r
709 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
710 left_active = event.MouseInput.isLeftPressed();
\r
711 middle_active = event.MouseInput.isMiddlePressed();
\r
712 right_active = event.MouseInput.isRightPressed();
\r
714 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
716 leftclicked = true;
\r
718 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
720 rightclicked = true;
\r
722 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
724 leftreleased = true;
\r
726 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
728 rightreleased = true;
\r
730 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
732 /*dstream<<"event.MouseInput.Wheel="
\r
733 <<event.MouseInput.Wheel<<std::endl;*/
\r
735 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
736 hotbar_itemcount-1);
\r
737 if(event.MouseInput.Wheel < 0)
\r
739 if(g_selected_item < max_item)
\r
742 g_selected_item = 0;
\r
744 else if(event.MouseInput.Wheel > 0)
\r
746 if(g_selected_item > 0)
\r
749 g_selected_item = max_item;
\r
758 // This is used to check whether a key is being held down
\r
759 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
761 return keyIsDown[keyCode];
\r
766 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
767 keyIsDown[i] = false;
\r
769 leftclicked = false;
\r
770 rightclicked = false;
\r
771 leftreleased = false;
\r
772 rightreleased = false;
\r
774 left_active = false;
\r
775 middle_active = false;
\r
776 right_active = false;
\r
787 bool rightreleased;
\r
790 bool middle_active;
\r
794 // We use this array to store the current state of each key
\r
795 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
798 IrrlichtDevice *m_device;
\r
807 virtual ~InputHandler()
\r
811 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
813 virtual v2s32 getMousePos() = 0;
\r
814 virtual void setMousePos(s32 x, s32 y) = 0;
\r
816 virtual bool getLeftState() = 0;
\r
817 virtual bool getRightState() = 0;
\r
819 virtual bool getLeftClicked() = 0;
\r
820 virtual bool getRightClicked() = 0;
\r
821 virtual void resetLeftClicked() = 0;
\r
822 virtual void resetRightClicked() = 0;
\r
824 virtual bool getLeftReleased() = 0;
\r
825 virtual bool getRightReleased() = 0;
\r
826 virtual void resetLeftReleased() = 0;
\r
827 virtual void resetRightReleased() = 0;
\r
829 virtual void step(float dtime) {};
\r
831 virtual void clear() {};
\r
834 InputHandler *g_input = NULL;
\r
836 class RealInputHandler : public InputHandler
\r
839 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
841 m_receiver(receiver)
\r
844 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
846 return m_receiver->IsKeyDown(keyCode);
\r
848 virtual v2s32 getMousePos()
\r
850 return m_device->getCursorControl()->getPosition();
\r
852 virtual void setMousePos(s32 x, s32 y)
\r
854 m_device->getCursorControl()->setPosition(x, y);
\r
857 virtual bool getLeftState()
\r
859 return m_receiver->left_active;
\r
861 virtual bool getRightState()
\r
863 return m_receiver->right_active;
\r
866 virtual bool getLeftClicked()
\r
868 return m_receiver->leftclicked;
\r
870 virtual bool getRightClicked()
\r
872 return m_receiver->rightclicked;
\r
874 virtual void resetLeftClicked()
\r
876 m_receiver->leftclicked = false;
\r
878 virtual void resetRightClicked()
\r
880 m_receiver->rightclicked = false;
\r
883 virtual bool getLeftReleased()
\r
885 return m_receiver->leftreleased;
\r
887 virtual bool getRightReleased()
\r
889 return m_receiver->rightreleased;
\r
891 virtual void resetLeftReleased()
\r
893 m_receiver->leftreleased = false;
\r
895 virtual void resetRightReleased()
\r
897 m_receiver->rightreleased = false;
\r
902 resetRightClicked();
\r
903 resetLeftClicked();
\r
906 IrrlichtDevice *m_device;
\r
907 MyEventReceiver *m_receiver;
\r
910 class RandomInputHandler : public InputHandler
\r
913 RandomInputHandler()
\r
917 leftclicked = false;
\r
918 rightclicked = false;
\r
919 leftreleased = false;
\r
920 rightreleased = false;
\r
921 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
922 keydown[i] = false;
\r
924 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
926 return keydown[keyCode];
\r
928 virtual v2s32 getMousePos()
\r
932 virtual void setMousePos(s32 x, s32 y)
\r
934 mousepos = v2s32(x,y);
\r
937 virtual bool getLeftState()
\r
941 virtual bool getRightState()
\r
946 virtual bool getLeftClicked()
\r
948 return leftclicked;
\r
950 virtual bool getRightClicked()
\r
952 return rightclicked;
\r
954 virtual void resetLeftClicked()
\r
956 leftclicked = false;
\r
958 virtual void resetRightClicked()
\r
960 rightclicked = false;
\r
963 virtual bool getLeftReleased()
\r
965 return leftreleased;
\r
967 virtual bool getRightReleased()
\r
969 return rightreleased;
\r
971 virtual void resetLeftReleased()
\r
973 leftreleased = false;
\r
975 virtual void resetRightReleased()
\r
977 rightreleased = false;
\r
980 virtual void step(float dtime)
\r
983 static float counter1 = 0;
\r
987 counter1 = 0.1*Rand(1, 40);
\r
988 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
992 static float counter1 = 0;
\r
996 counter1 = 0.1*Rand(1, 40);
\r
997 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1001 static float counter1 = 0;
\r
1002 counter1 -= dtime;
\r
1003 if(counter1 < 0.0)
\r
1005 counter1 = 0.1*Rand(1, 40);
\r
1006 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1010 static float counter1 = 0;
\r
1011 counter1 -= dtime;
\r
1012 if(counter1 < 0.0)
\r
1014 counter1 = 0.1*Rand(1, 40);
\r
1015 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1019 static float counter1 = 0;
\r
1020 counter1 -= dtime;
\r
1021 if(counter1 < 0.0)
\r
1023 counter1 = 0.1*Rand(1, 20);
\r
1024 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1028 static float counter1 = 0;
\r
1029 counter1 -= dtime;
\r
1030 if(counter1 < 0.0)
\r
1032 counter1 = 0.1*Rand(1, 30);
\r
1033 leftdown = !leftdown;
\r
1035 leftclicked = true;
\r
1037 leftreleased = true;
\r
1041 static float counter1 = 0;
\r
1042 counter1 -= dtime;
\r
1043 if(counter1 < 0.0)
\r
1045 counter1 = 0.1*Rand(1, 15);
\r
1046 rightdown = !rightdown;
\r
1048 rightclicked = true;
\r
1050 rightreleased = true;
\r
1053 mousepos += mousespeed;
\r
1056 s32 Rand(s32 min, s32 max)
\r
1058 return (myrand()%(max-min+1))+min;
\r
1061 bool keydown[KEY_KEY_CODES_COUNT];
\r
1067 bool rightclicked;
\r
1068 bool leftreleased;
\r
1069 bool rightreleased;
\r
1072 void updateViewingRange(f32 frametime_in, Client *client)
\r
1074 if(draw_control.range_all == true)
\r
1077 static f32 added_frametime = 0;
\r
1078 static s16 added_frames = 0;
\r
1080 added_frametime += frametime_in;
\r
1081 added_frames += 1;
\r
1083 // Actually this counter kind of sucks because frametime is busytime
\r
1084 static f32 counter = 0;
\r
1085 counter -= frametime_in;
\r
1091 /*dstream<<__FUNCTION_NAME
\r
1092 <<": Collected "<<added_frames<<" frames, total of "
\r
1093 <<added_frametime<<"s."<<std::endl;*/
\r
1095 /*dstream<<"draw_control.blocks_drawn="
\r
1096 <<draw_control.blocks_drawn
\r
1097 <<", draw_control.blocks_would_have_drawn="
\r
1098 <<draw_control.blocks_would_have_drawn
\r
1101 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1102 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1104 draw_control.wanted_min_range = range_min;
\r
1105 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1107 float block_draw_ratio = 1.0;
\r
1108 if(draw_control.blocks_would_have_drawn != 0)
\r
1110 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1111 / (float)draw_control.blocks_would_have_drawn;
\r
1114 // Calculate the average frametime in the case that all wanted
\r
1115 // blocks had been drawn
\r
1116 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1118 added_frametime = 0.0;
\r
1121 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1122 float wanted_frametime = 1.0 / wanted_fps;
\r
1124 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1125 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1127 // If needed frametime change is small, just return
\r
1128 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1130 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1134 float range = draw_control.wanted_range;
\r
1135 float new_range = range;
\r
1137 static s16 range_old = 0;
\r
1138 static f32 frametime_old = 0;
\r
1140 float d_range = range - range_old;
\r
1141 f32 d_frametime = frametime - frametime_old;
\r
1142 // A sane default of 30ms per 50 nodes of range
\r
1143 static f32 time_per_range = 30. / 50;
\r
1146 time_per_range = d_frametime / d_range;
\r
1149 // The minimum allowed calculated frametime-range derivative:
\r
1150 // Practically this sets the maximum speed of changing the range.
\r
1151 // The lower this value, the higher the maximum changing speed.
\r
1152 // A low value here results in wobbly range (0.001)
\r
1153 // A high value here results in slow changing range (0.0025)
\r
1154 // SUGG: This could be dynamically adjusted so that when
\r
1155 // the camera is turning, this is lower
\r
1156 //float min_time_per_range = 0.0015;
\r
1157 float min_time_per_range = 0.0010;
\r
1158 //float min_time_per_range = 0.05 / range;
\r
1159 if(time_per_range < min_time_per_range)
\r
1161 time_per_range = min_time_per_range;
\r
1162 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1166 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1169 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1170 // Dampen the change a bit to kill oscillations
\r
1171 //wanted_range_change *= 0.9;
\r
1172 //wanted_range_change *= 0.75;
\r
1173 wanted_range_change *= 0.5;
\r
1174 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1176 // If needed range change is very small, just return
\r
1177 if(fabs(wanted_range_change) < 0.001)
\r
1179 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1183 new_range += wanted_range_change;
\r
1184 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1186 //float new_range_unclamped = new_range;
\r
1187 if(new_range < range_min)
\r
1188 new_range = range_min;
\r
1189 if(new_range > range_max)
\r
1190 new_range = range_max;
\r
1192 /*if(new_range != new_range_unclamped)
\r
1193 dstream<<", clamped to "<<new_range<<std::endl;
\r
1195 dstream<<std::endl;*/
\r
1197 draw_control.wanted_range = new_range;
\r
1199 range_old = new_range;
\r
1200 frametime_old = frametime;
\r
1203 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1204 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1205 Inventory *inventory)
\r
1207 InventoryList *mainlist = inventory->getList("main");
\r
1208 if(mainlist == NULL)
\r
1210 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1214 s32 padding = imgsize/12;
\r
1215 //s32 height = imgsize + padding*2;
\r
1216 s32 width = itemcount*(imgsize+padding*2);
\r
1218 // Position of upper left corner of bar
\r
1219 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1221 // Draw background color
\r
1222 /*core::rect<s32> barrect(0,0,width,height);
\r
1224 video::SColor bgcolor(255,128,128,128);
\r
1225 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1227 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1229 for(s32 i=0; i<itemcount; i++)
\r
1231 InventoryItem *item = mainlist->getItem(i);
\r
1233 core::rect<s32> rect = imgrect + pos
\r
1234 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1236 if(g_selected_item == i)
\r
1238 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1239 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1240 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1245 video::SColor bgcolor2(128,0,0,0);
\r
1246 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1251 drawInventoryItem(driver, font, item, rect, NULL);
\r
1257 video::ITexture *g_map_plot_texture = NULL;
\r
1258 float g_map_plot_texture_scale = 4;
\r
1260 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1266 core::dimension2d<u32> dim(640,480);
\r
1267 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1269 for(u32 y=0; y<dim.Height; y++)
\r
1270 for(u32 x=0; x<dim.Width; x++)
\r
1272 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1273 pf *= g_map_plot_texture_scale;
\r
1275 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1278 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1279 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1281 // Screen coordinates that are based on multiples of
\r
1282 // 1000/g_map_plot_texture_scale and never negative
\r
1283 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1284 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1285 // Offset to center of image
\r
1286 ux -= dim.Width/2;
\r
1287 uy -= dim.Height/2;
\r
1289 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1290 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1291 c.set(255, 255, 255, 255);
\r
1292 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1293 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1294 c.set(255, 160, 160, 160);
\r
1295 else if(h < WATER_LEVEL - 0.5) // Water
\r
1296 c.set(255, 50, 50, 255);
\r
1298 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1299 || (h < WATER_LEVEL + 2
\r
1300 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1304 h = 1.0 - exp(-h);
\r
1306 video::SColor c1(255,237,201,175);
\r
1307 //video::SColor c2(255,20,20,20);
\r
1308 video::SColor c2(255,150,0,0);
\r
1309 c = c2.getInterpolated(c1, h);
\r
1315 h = 1.0 - exp(-h);
\r
1317 video::SColor c1(255,110,185,90);
\r
1318 //video::SColor c2(255,20,20,20);
\r
1319 video::SColor c2(255,150,0,0);
\r
1320 c = c2.getInterpolated(c1, h);
\r
1325 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1329 h = 1.0 - exp(-h);
\r
1331 video::SColor c1(255,237,201,175);
\r
1332 //video::SColor c2(255,20,20,20);
\r
1333 video::SColor c2(255,150,0,0);
\r
1334 c = c2.getInterpolated(c1, h);
\r
1338 else if(h < WATER_LEVEL + 2
\r
1339 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1340 c.set(255, 237, 201, 175);
\r
1342 else if(h < WATER_LEVEL + 10)
\r
1343 c.set(255, 50, 150, 50); // Green
\r
1344 else if(h < WATER_LEVEL + 20)
\r
1345 c.set(255, 110, 185, 50); // Yellowish green
\r
1346 else if(h < WATER_LEVEL + 40)
\r
1347 c.set(255, 180, 210, 50); // Greenish yellow
\r
1348 else if(h < WATER_LEVEL + 60)
\r
1349 c.set(255, 220, 220, 50); // Yellow
\r
1350 else if(h < WATER_LEVEL + 80)
\r
1351 c.set(255, 200, 200, 110); // Yellowish white
\r
1352 else if(h < WATER_LEVEL + 100)
\r
1353 c.set(255, 190, 190, 190); // Grey
\r
1355 c.set(255, 255, 255, 255); // White
\r
1363 h = 1.0 - exp(-h);
\r
1365 video::SColor c1(255,200,200,50);
\r
1366 video::SColor c2(255,0,150,0);
\r
1367 c = c1.getInterpolated(c2, h);
\r
1369 /*u32 a = (u32)(h*255);
\r
1373 c.set(255, a, a, a);*/
\r
1377 if(h >= WATER_LEVEL - 0.5
\r
1378 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1380 video::SColor c1(255,237,201,175);
\r
1381 c = c.getInterpolated(c1, 0.5);
\r
1385 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1388 video::SColor c1(255,255,0,0);
\r
1389 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1392 img->setPixel(x, y, c);
\r
1394 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1396 assert(g_map_plot_texture);
\r
1407 ChatLine(const std::wstring &a_text):
\r
1413 std::wstring text;
\r
1416 // These are defined global so that they're not optimized too much.
\r
1417 // Can't change them to volatile.
\r
1422 std::string tempstring;
\r
1423 std::string tempstring2;
\r
1428 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1429 TimeTaker timer("Testing std::string speed");
\r
1430 const u32 jj = 10000;
\r
1431 for(u32 j=0; j<jj; j++)
\r
1435 const u32 ii = 10;
\r
1436 for(u32 i=0; i<ii; i++){
\r
1437 tempstring2 += "asd";
\r
1439 for(u32 i=0; i<ii+1; i++){
\r
1440 tempstring += "asd";
\r
1441 if(tempstring == tempstring2)
\r
1447 dstream<<"All of the following tests should take around 100ms each."
\r
1451 TimeTaker timer("Testing floating-point conversion speed");
\r
1453 for(u32 i=0; i<4000000; i++){
\r
1460 TimeTaker timer("Testing floating-point vector speed");
\r
1462 tempv3f1 = v3f(1,2,3);
\r
1463 tempv3f2 = v3f(4,5,6);
\r
1464 for(u32 i=0; i<10000000; i++){
\r
1465 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1466 tempv3f2 += v3f(7,8,9);
\r
1471 TimeTaker timer("Testing core::map speed");
\r
1473 core::map<v2s16, f32> map1;
\r
1476 for(s16 y=0; y<ii; y++){
\r
1477 for(s16 x=0; x<ii; x++){
\r
1478 map1.insert(v2s16(x,y), tempf);
\r
1482 for(s16 y=ii-1; y>=0; y--){
\r
1483 for(s16 x=0; x<ii; x++){
\r
1484 tempf = map1[v2s16(x,y)];
\r
1490 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1491 TimeTaker timer("Testing mutex speed");
\r
1504 // Do at least 10ms
\r
1505 while(timer.getTime() < 10);
\r
1507 u32 dtime = timer.stop();
\r
1508 u32 per_ms = n / dtime;
\r
1509 std::cout<<"Done. "<<dtime<<"ms, "
\r
1510 <<per_ms<<"/ms"<<std::endl;
\r
1514 int main(int argc, char *argv[])
\r
1517 Parse command line
\r
1520 // List all allowed options
\r
1521 core::map<std::string, ValueSpec> allowed_options;
\r
1522 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1523 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1524 "Run server directly"));
\r
1525 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1526 "Load configuration from specified file"));
\r
1527 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1528 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1529 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1530 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1531 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1532 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1534 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1536 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1538 Settings cmd_args;
\r
1540 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1542 if(ret == false || cmd_args.getFlag("help"))
\r
1544 dstream<<"Allowed options:"<<std::endl;
\r
1545 for(core::map<std::string, ValueSpec>::Iterator
\r
1546 i = allowed_options.getIterator();
\r
1547 i.atEnd() == false; i++)
\r
1549 dstream<<" --"<<i.getNode()->getKey();
\r
1550 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1555 dstream<<" <value>";
\r
1557 dstream<<std::endl;
\r
1559 if(i.getNode()->getValue().help != NULL)
\r
1561 dstream<<" "<<i.getNode()->getValue().help
\r
1566 return cmd_args.getFlag("help") ? 0 : 1;
\r
1570 Low-level initialization
\r
1573 bool disable_stderr = false;
\r
1575 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1576 disable_stderr = true;
\r
1579 // Initialize debug streams
\r
1580 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1581 // Initialize debug stacks
\r
1582 debug_stacks_init();
\r
1584 DSTACK(__FUNCTION_NAME);
\r
1586 porting::signal_handler_init();
\r
1587 bool &kill = *porting::signal_handler_killstatus();
\r
1589 porting::initializePaths();
\r
1590 // Create user data directory
\r
1591 fs::CreateDir(porting::path_userdata);
\r
1593 // C-style stuff initialization
\r
1594 initializeMaterialProperties();
\r
1597 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1599 // Print startup message
\r
1600 dstream<<DTIME<<"minetest-c55"
\r
1601 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1602 <<", "<<BUILD_INFO
\r
1606 Basic initialization
\r
1609 // Initialize default settings
\r
1610 set_default_settings();
\r
1612 // Set locale. This is for forcing '.' as the decimal point.
\r
1613 std::locale::global(std::locale("C"));
\r
1614 // This enables printing all characters in bitmap font
\r
1615 setlocale(LC_CTYPE, "en_US");
\r
1617 // Initialize sockets
\r
1619 atexit(sockets_cleanup);
\r
1629 // Path of configuration file in use
\r
1630 std::string configpath = "";
\r
1632 if(cmd_args.exists("config"))
\r
1634 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1637 dstream<<"Could not read configuration from \""
\r
1638 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1641 configpath = cmd_args.get("config");
\r
1645 core::array<std::string> filenames;
\r
1646 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1647 #ifdef RUN_IN_PLACE
\r
1648 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1651 for(u32 i=0; i<filenames.size(); i++)
\r
1653 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1656 configpath = filenames[i];
\r
1661 // If no path found, use the first one (menu creates the file)
\r
1662 if(configpath == "")
\r
1663 configpath = filenames[0];
\r
1666 // Initialize random seed
\r
1671 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1673 These are needed for unit tests at least.
\r
1676 // Initial call with g_texturesource not set.
\r
1683 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1684 || cmd_args.getFlag("enable-unittests") == true)
\r
1689 /*for(s16 y=-100; y<100; y++)
\r
1690 for(s16 x=-100; x<100; x++)
\r
1692 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1702 if(cmd_args.exists("port"))
\r
1703 port = cmd_args.getU16("port");
\r
1704 else if(cmd_args.exists("port"))
\r
1705 port = g_settings.getU16("port");
\r
1708 std::string map_dir = porting::path_userdata+"/map";
\r
1709 if(cmd_args.exists("map-dir"))
\r
1710 map_dir = cmd_args.get("map-dir");
\r
1711 else if(g_settings.exists("map-dir"))
\r
1712 map_dir = g_settings.get("map-dir");
\r
1714 // Run dedicated server if asked to
\r
1715 if(cmd_args.getFlag("server"))
\r
1717 DSTACK("Dedicated server branch");
\r
1720 Server server(map_dir.c_str());
\r
1721 server.start(port);
\r
1724 dedicated_server_loop(server, kill);
\r
1733 // Address to connect to
\r
1734 std::string address = "";
\r
1736 if(cmd_args.exists("address"))
\r
1738 address = cmd_args.get("address");
\r
1742 address = g_settings.get("address");
\r
1745 std::string playername = g_settings.get("name");
\r
1747 // Resolution selection
\r
1749 bool fullscreen = false;
\r
1750 u16 screenW = g_settings.getU16("screenW");
\r
1751 u16 screenH = g_settings.getU16("screenH");
\r
1753 // Determine driver
\r
1755 video::E_DRIVER_TYPE driverType;
\r
1757 std::string driverstring = g_settings.get("video_driver");
\r
1759 if(driverstring == "null")
\r
1760 driverType = video::EDT_NULL;
\r
1761 else if(driverstring == "software")
\r
1762 driverType = video::EDT_SOFTWARE;
\r
1763 else if(driverstring == "burningsvideo")
\r
1764 driverType = video::EDT_BURNINGSVIDEO;
\r
1765 else if(driverstring == "direct3d8")
\r
1766 driverType = video::EDT_DIRECT3D8;
\r
1767 else if(driverstring == "direct3d9")
\r
1768 driverType = video::EDT_DIRECT3D9;
\r
1769 else if(driverstring == "opengl")
\r
1770 driverType = video::EDT_OPENGL;
\r
1773 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1774 "to opengl"<<std::endl;
\r
1775 driverType = video::EDT_OPENGL;
\r
1778 // create device and exit if creation failed
\r
1780 MyEventReceiver receiver;
\r
1782 IrrlichtDevice *device;
\r
1783 device = createDevice(driverType,
\r
1784 core::dimension2d<u32>(screenW, screenH),
\r
1785 16, fullscreen, false, false, &receiver);
\r
1788 return 1; // could not create selected driver.
\r
1790 g_device = device;
\r
1791 g_irrlicht = new IrrlichtWrapper(device);
\r
1792 TextureSource *texturesource = new TextureSource(device);
\r
1793 g_texturesource = texturesource;
\r
1796 Speed tests (done after irrlicht is loaded to get timer)
\r
1798 if(cmd_args.getFlag("speedtests"))
\r
1800 dstream<<"Running speed tests"<<std::endl;
\r
1805 device->setResizable(true);
\r
1807 bool random_input = g_settings.getBool("random_input")
\r
1808 || cmd_args.getFlag("random-input");
\r
1810 g_input = new RandomInputHandler();
\r
1812 g_input = new RealInputHandler(device, &receiver);
\r
1815 Continue initialization
\r
1818 video::IVideoDriver* driver = device->getVideoDriver();
\r
1821 This changes the minimum allowed number of vertices in a VBO.
\r
1824 //driver->setMinHardwareBufferVertexCount(50);
\r
1826 scene::ISceneManager* smgr = device->getSceneManager();
\r
1828 guienv = device->getGUIEnvironment();
\r
1829 gui::IGUISkin* skin = guienv->getSkin();
\r
1830 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1832 skin->setFont(font);
\r
1834 dstream<<"WARNING: Font file was not found."
\r
1835 " Using default font."<<std::endl;
\r
1836 // If font was not found, this will get us one
\r
1837 font = skin->getFont();
\r
1840 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1841 dstream<<"text_height="<<text_height<<std::endl;
\r
1843 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1844 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1845 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1846 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1847 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1848 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1851 Preload some textures and stuff
\r
1854 init_content_inventory_texture_paths();
\r
1855 init_mapnode(); // Second call with g_texturesource set
\r
1863 We need some kind of a root node to be able to add
\r
1864 custom gui elements directly on the screen.
\r
1865 Otherwise they won't be automatically drawn.
\r
1867 guiroot = guienv->addStaticText(L"",
\r
1868 core::rect<s32>(0, 0, 10000, 10000));
\r
1870 // First line of debug text
\r
1871 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1873 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1875 // Second line of debug text
\r
1876 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1878 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1881 // At the middle of the screen
\r
1882 // Object infos are shown in this
\r
1883 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1885 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1889 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1891 core::rect<s32>(0,0,0,0),
\r
1892 false, false); // Disable word wrap as of now
\r
1894 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1895 core::list<ChatLine> chat_lines;
\r
1898 If an error occurs, this is set to something and the
\r
1899 menu-game loop is restarted. It is then displayed before
\r
1902 std::wstring error_message = L"";
\r
1907 while(g_device->run() && kill == false)
\r
1910 // This is used for catching disconnects
\r
1915 Out-of-game menu loop.
\r
1917 Loop quits when menu returns proper parameters.
\r
1919 while(kill == false)
\r
1921 // Cursor can be non-visible when coming from the game
\r
1922 device->getCursorControl()->setVisible(true);
\r
1923 // Some stuff are left to scene manager when coming from the game
\r
1924 // (map at least?)
\r
1926 // Reset or hide the debug gui texts
\r
1927 guitext->setText(L"Minetest-c55");
\r
1928 guitext2->setVisible(false);
\r
1929 guitext_info->setVisible(false);
\r
1930 guitext_chat->setVisible(false);
\r
1932 // Initialize menu data
\r
1933 MainMenuData menudata;
\r
1934 menudata.address = narrow_to_wide(address);
\r
1935 menudata.name = narrow_to_wide(playername);
\r
1936 menudata.port = narrow_to_wide(itos(port));
\r
1937 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1939 GUIMainMenu *menu =
\r
1940 new GUIMainMenu(guienv, guiroot, -1,
\r
1941 &g_menumgr, &menudata, &g_gamecallback);
\r
1942 menu->allowFocusRemoval(true);
\r
1944 if(error_message != L"")
\r
1946 GUIMessageMenu *menu2 =
\r
1947 new GUIMessageMenu(guienv, guiroot, -1,
\r
1948 &g_menumgr, error_message.c_str());
\r
1950 error_message = L"";
\r
1953 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1955 dstream<<"Created main menu"<<std::endl;
\r
1957 while(g_device->run() && kill == false)
\r
1959 // Run global IrrlichtWrapper's main thread processing stuff
\r
1960 g_irrlicht->Run();
\r
1962 if(menu->getStatus() == true)
\r
1965 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1966 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1967 guienv->drawAll();
\r
1968 driver->endScene();
\r
1971 // Break out of menu-game loop to shut down cleanly
\r
1972 if(g_device->run() == false || kill == true)
\r
1975 dstream<<"Dropping main menu"<<std::endl;
\r
1979 // Delete map if requested
\r
1980 if(menudata.delete_map)
\r
1982 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1984 error_message = L"Delete failed";
\r
1988 playername = wide_to_narrow(menudata.name);
\r
1989 address = wide_to_narrow(menudata.address);
\r
1990 port = stoi(wide_to_narrow(menudata.port));
\r
1991 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1993 // Check for valid parameters, restart menu if invalid.
\r
1994 if(playername == "")
\r
1996 error_message = L"Name required.";
\r
2001 g_settings.set("name", playername);
\r
2002 g_settings.set("address", address);
\r
2003 g_settings.set("port", itos(port));
\r
2004 // Update configuration file
\r
2005 if(configpath != "")
\r
2006 g_settings.updateConfigFile(configpath.c_str());
\r
2008 // Continue to game
\r
2012 // Break out of menu-game loop to shut down cleanly
\r
2013 if(g_device->run() == false)
\r
2017 Make a scope here so that the client and the server and other
\r
2018 stuff gets removed when disconnected or the irrlicht device
\r
2023 // This is set to true at the end of the scope
\r
2024 g_irrlicht->Shutdown(false);
\r
2027 Draw "Loading" screen
\r
2029 const wchar_t *text = L"Loading and connecting...";
\r
2030 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2031 core::vector2d<s32> textsize(300, text_height);
\r
2032 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2034 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2035 text, textrect, false, false);
\r
2036 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2038 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2039 guienv->drawAll();
\r
2040 driver->endScene();
\r
2042 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2046 SharedPtr will delete it when it goes out of scope.
\r
2048 SharedPtr<Server> server;
\r
2049 if(address == ""){
\r
2050 server = new Server(map_dir);
\r
2051 server->start(port);
\r
2058 Client client(device, playername.c_str(), draw_control);
\r
2060 g_client = &client;
\r
2062 Address connect_address(0,0,0,0, port);
\r
2065 //connect_address.Resolve("localhost");
\r
2066 connect_address.setAddress(127,0,0,1);
\r
2068 connect_address.Resolve(address.c_str());
\r
2070 catch(ResolveError &e)
\r
2072 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2074 error_message = L"Couldn't resolve address";
\r
2075 gui_loadingtext->remove();
\r
2079 dstream<<DTIME<<"Connecting to server at ";
\r
2080 connect_address.print(&dstream);
\r
2081 dstream<<std::endl;
\r
2082 client.connect(connect_address);
\r
2085 while(client.connectedAndInitialized() == false)
\r
2088 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2089 guienv->drawAll();
\r
2090 driver->endScene();
\r
2092 // Update client and server
\r
2096 if(server != NULL)
\r
2097 server->step(0.1);
\r
2103 catch(con::PeerNotFoundException &e)
\r
2105 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2107 error_message = L"Connection timed out.";
\r
2108 gui_loadingtext->remove();
\r
2115 /*scene::ISceneNode* skybox;
\r
2116 skybox = smgr->addSkyBoxSceneNode(
\r
2117 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2118 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2119 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2120 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2121 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2122 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2125 Create the camera node
\r
2128 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2129 0, // Camera parent
\r
2130 v3f(BS*100, BS*2, BS*100), // Look from
\r
2131 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2135 if(camera == NULL)
\r
2138 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2139 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2140 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2142 camera->setFOV(FOV_ANGLE);
\r
2144 // Just so big a value that everything rendered is visible
\r
2145 camera->setFarValue(100000*BS);
\r
2148 Lighting test code. Doesn't quite work this way.
\r
2149 The CPU-computed lighting is good.
\r
2153 smgr->addLightSceneNode(NULL,
\r
2154 v3f(0, BS*1000000, 0),
\r
2155 video::SColorf(0.3,0.3,0.3),
\r
2158 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2160 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2161 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2164 f32 camera_yaw = 0; // "right/left"
\r
2165 f32 camera_pitch = 0; // "up/down"
\r
2171 gui_loadingtext->remove();
\r
2174 Add some gui stuff
\r
2177 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2178 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2179 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2180 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2182 // Test the text input system
\r
2183 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2185 /*GUIMessageMenu *menu =
\r
2186 new GUIMessageMenu(guienv, guiroot, -1,
\r
2191 // Launch pause menu
\r
2192 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2193 &g_menumgr))->drop();
\r
2196 guitext2->setVisible(true);
\r
2197 guitext_info->setVisible(true);
\r
2198 guitext_chat->setVisible(true);
\r
2200 //s32 guitext_chat_pad_bottom = 70;
\r
2202 v2u32 screensize(0,0);
\r
2203 v2u32 last_screensize(0,0);
\r
2206 Some statistics are collected in these
\r
2209 u32 beginscenetime = 0;
\r
2210 u32 scenetime = 0;
\r
2211 u32 endscenetime = 0;
\r
2214 //throw con::PeerNotFoundException("lol");
\r
2216 core::list<float> frametime_log;
\r
2222 bool first_loop_after_window_activation = true;
\r
2224 // Time is in milliseconds
\r
2225 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2226 // NOTE: So we have to use getTime() and call run()s between them
\r
2227 u32 lasttime = device->getTimer()->getTime();
\r
2229 while(device->run() && kill == false)
\r
2231 if(g_disconnect_requested)
\r
2233 g_disconnect_requested = false;
\r
2238 Run global IrrlichtWrapper's main thread processing stuff
\r
2240 g_irrlicht->Run();
\r
2243 Process TextureSource's queue
\r
2245 texturesource->processQueue();
\r
2248 Random calculations
\r
2250 last_screensize = screensize;
\r
2251 screensize = driver->getScreenSize();
\r
2252 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2253 //bool screensize_changed = screensize != last_screensize;
\r
2255 // Hilight boxes collected during the loop and displayed
\r
2256 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2259 std::wstring infotext;
\r
2261 // When screen size changes, update positions and sizes of stuff
\r
2262 /*if(screensize_changed)
\r
2264 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2265 quick_inventory->updatePosition(pos);
\r
2268 //TimeTaker //timer1("//timer1");
\r
2270 // Time of frame without fps limit
\r
2274 // not using getRealTime is necessary for wine
\r
2275 u32 time = device->getTimer()->getTime();
\r
2276 if(time > lasttime)
\r
2277 busytime_u32 = time - lasttime;
\r
2280 busytime = busytime_u32 / 1000.0;
\r
2283 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2285 // Absolutelu necessary for wine!
\r
2292 updateViewingRange(busytime, &client);
\r
2299 float fps_max = g_settings.getFloat("fps_max");
\r
2300 u32 frametime_min = 1000./fps_max;
\r
2302 if(busytime_u32 < frametime_min)
\r
2304 u32 sleeptime = frametime_min - busytime_u32;
\r
2305 device->sleep(sleeptime);
\r
2309 // Absolutelu necessary for wine!
\r
2313 Time difference calculation
\r
2315 f32 dtime; // in seconds
\r
2317 u32 time = device->getTimer()->getTime();
\r
2318 if(time > lasttime)
\r
2319 dtime = (time - lasttime) / 1000.0;
\r
2325 Log frametime for visualization
\r
2327 frametime_log.push_back(dtime);
\r
2328 if(frametime_log.size() > 100)
\r
2330 core::list<float>::Iterator i = frametime_log.begin();
\r
2331 frametime_log.erase(i);
\r
2335 Visualize frametime in terminal
\r
2337 /*for(u32 i=0; i<dtime*400; i++)
\r
2339 std::cout<<std::endl;*/
\r
2342 Time average and jitter calculation
\r
2345 static f32 dtime_avg1 = 0.0;
\r
2346 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2347 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2349 static f32 dtime_jitter1_max_sample = 0.0;
\r
2350 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2352 static f32 jitter1_max = 0.0;
\r
2353 static f32 counter = 0.0;
\r
2354 if(dtime_jitter1 > jitter1_max)
\r
2355 jitter1_max = dtime_jitter1;
\r
2360 dtime_jitter1_max_sample = jitter1_max;
\r
2361 dtime_jitter1_max_fraction
\r
2362 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2363 jitter1_max = 0.0;
\r
2368 Busytime average and jitter calculation
\r
2371 static f32 busytime_avg1 = 0.0;
\r
2372 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2373 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2375 static f32 busytime_jitter1_max_sample = 0.0;
\r
2376 static f32 busytime_jitter1_min_sample = 0.0;
\r
2378 static f32 jitter1_max = 0.0;
\r
2379 static f32 jitter1_min = 0.0;
\r
2380 static f32 counter = 0.0;
\r
2381 if(busytime_jitter1 > jitter1_max)
\r
2382 jitter1_max = busytime_jitter1;
\r
2383 if(busytime_jitter1 < jitter1_min)
\r
2384 jitter1_min = busytime_jitter1;
\r
2386 if(counter > 0.0){
\r
2388 busytime_jitter1_max_sample = jitter1_max;
\r
2389 busytime_jitter1_min_sample = jitter1_min;
\r
2390 jitter1_max = 0.0;
\r
2391 jitter1_min = 0.0;
\r
2396 Debug info for client
\r
2399 static float counter = 0.0;
\r
2404 client.printDebugInfo(std::cout);
\r
2409 Input handler step()
\r
2411 g_input->step(dtime);
\r
2418 Player speed control
\r
2427 bool a_superspeed,
\r
2431 PlayerControl control(
\r
2432 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2433 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2434 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2435 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2436 g_input->isKeyDown(irr::KEY_SPACE),
\r
2437 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2438 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2439 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2443 client.setPlayerControl(control);
\r
2447 Process environment
\r
2451 //TimeTaker timer("client.step(dtime)");
\r
2452 client.step(dtime);
\r
2453 //client.step(dtime_avg1);
\r
2456 if(server != NULL)
\r
2458 //TimeTaker timer("server->step(dtime)");
\r
2459 server->step(dtime);
\r
2462 v3f player_position = client.getPlayerPosition();
\r
2464 //TimeTaker //timer2("//timer2");
\r
2467 Mouse and camera control
\r
2470 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2473 device->getCursorControl()->setVisible(false);
\r
2475 if(first_loop_after_window_activation){
\r
2476 //std::cout<<"window active, first loop"<<std::endl;
\r
2477 first_loop_after_window_activation = false;
\r
2480 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2481 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2482 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2483 camera_yaw -= dx*0.2;
\r
2484 camera_pitch += dy*0.2;
\r
2485 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2486 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2488 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2491 device->getCursorControl()->setVisible(true);
\r
2493 //std::cout<<"window inactive"<<std::endl;
\r
2494 first_loop_after_window_activation = true;
\r
2497 camera_yaw = wrapDegrees(camera_yaw);
\r
2498 camera_pitch = wrapDegrees(camera_pitch);
\r
2500 v3f camera_direction = v3f(0,0,1);
\r
2501 camera_direction.rotateYZBy(camera_pitch);
\r
2502 camera_direction.rotateXZBy(camera_yaw);
\r
2504 // This is at the height of the eyes of the current figure
\r
2505 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2506 // This is more like in minecraft
\r
2507 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2509 camera->setPosition(camera_position);
\r
2510 // *100.0 helps in large map coordinates
\r
2511 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2513 if(FIELD_OF_VIEW_TEST){
\r
2514 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2517 //TimeTaker timer("client.updateCamera");
\r
2518 client.updateCamera(camera_position, camera_direction);
\r
2522 //TimeTaker //timer3("//timer3");
\r
2525 Calculate what block is the crosshair pointing to
\r
2528 //u32 t1 = device->getTimer()->getRealTime();
\r
2530 //f32 d = 4; // max. distance
\r
2531 f32 d = 4; // max. distance
\r
2532 core::line3d<f32> shootline(camera_position,
\r
2533 camera_position + camera_direction * BS * (d+1));
\r
2535 MapBlockObject *selected_object = client.getSelectedObject
\r
2536 (d*BS, camera_position, shootline);
\r
2539 If it's pointing to a MapBlockObject
\r
2542 if(selected_object != NULL)
\r
2544 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2546 core::aabbox3d<f32> box_on_map
\r
2547 = selected_object->getSelectionBoxOnMap();
\r
2549 hilightboxes.push_back(box_on_map);
\r
2551 infotext = narrow_to_wide(selected_object->infoText());
\r
2553 if(g_input->getLeftClicked())
\r
2555 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2556 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2557 selected_object->getId(), g_selected_item);
\r
2559 else if(g_input->getRightClicked())
\r
2561 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2563 Check if we want to modify the object ourselves
\r
2565 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2567 dstream<<"Sign object right-clicked"<<std::endl;
\r
2569 if(random_input == false)
\r
2571 // Get a new text for it
\r
2573 TextDest *dest = new TextDestSign(
\r
2574 selected_object->getBlock()->getPos(),
\r
2575 selected_object->getId(),
\r
2578 SignObject *sign_object = (SignObject*)selected_object;
\r
2580 std::wstring wtext =
\r
2581 narrow_to_wide(sign_object->getText());
\r
2583 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2589 Otherwise pass the event to the server as-is
\r
2593 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2594 selected_object->getId(), g_selected_item);
\r
2598 else // selected_object == NULL
\r
2602 Find out which node we are pointing at
\r
2605 bool nodefound = false;
\r
2607 v3s16 neighbourpos;
\r
2608 core::aabbox3d<f32> nodehilightbox;
\r
2609 f32 mindistance = BS * 1001;
\r
2611 v3s16 pos_i = floatToInt(player_position, BS);
\r
2613 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2617 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2618 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2619 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2620 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2621 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2622 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2624 for(s16 y = ystart; y <= yend; y++)
\r
2625 for(s16 z = zstart; z <= zend; z++)
\r
2626 for(s16 x = xstart; x <= xend; x++)
\r
2631 n = client.getNode(v3s16(x,y,z));
\r
2632 if(content_pointable(n.d) == false)
\r
2635 catch(InvalidPositionException &e)
\r
2641 v3f npf = intToFloat(np, BS);
\r
2646 v3s16(0,0,1), // back
\r
2647 v3s16(0,1,0), // top
\r
2648 v3s16(1,0,0), // right
\r
2649 v3s16(0,0,-1), // front
\r
2650 v3s16(0,-1,0), // bottom
\r
2651 v3s16(-1,0,0), // left
\r
2657 if(n.d == CONTENT_TORCH)
\r
2659 v3s16 dir = unpackDir(n.dir);
\r
2660 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2661 dir_f *= BS/2 - BS/6 - BS/20;
\r
2662 v3f cpf = npf + dir_f;
\r
2663 f32 distance = (cpf - camera_position).getLength();
\r
2665 core::aabbox3d<f32> box;
\r
2668 if(dir == v3s16(0,-1,0))
\r
2670 box = core::aabbox3d<f32>(
\r
2671 npf - v3f(BS/6, BS/2, BS/6),
\r
2672 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2676 else if(dir == v3s16(0,1,0))
\r
2678 box = core::aabbox3d<f32>(
\r
2679 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2680 npf + v3f(BS/6, BS/2, BS/6)
\r
2686 box = core::aabbox3d<f32>(
\r
2687 cpf - v3f(BS/6, BS/3, BS/6),
\r
2688 cpf + v3f(BS/6, BS/3, BS/6)
\r
2692 if(distance < mindistance)
\r
2694 if(box.intersectsWithLine(shootline))
\r
2698 neighbourpos = np;
\r
2699 mindistance = distance;
\r
2700 nodehilightbox = box;
\r
2709 for(u16 i=0; i<6; i++)
\r
2711 v3f dir_f = v3f(dirs[i].X,
\r
2712 dirs[i].Y, dirs[i].Z);
\r
2713 v3f centerpoint = npf + dir_f * BS/2;
\r
2715 (centerpoint - camera_position).getLength();
\r
2717 if(distance < mindistance)
\r
2719 core::CMatrix4<f32> m;
\r
2720 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2722 // This is the back face
\r
2723 v3f corners[2] = {
\r
2724 v3f(BS/2, BS/2, BS/2),
\r
2725 v3f(-BS/2, -BS/2, BS/2+d)
\r
2728 for(u16 j=0; j<2; j++)
\r
2730 m.rotateVect(corners[j]);
\r
2731 corners[j] += npf;
\r
2734 core::aabbox3d<f32> facebox(corners[0]);
\r
2735 facebox.addInternalPoint(corners[1]);
\r
2737 if(facebox.intersectsWithLine(shootline))
\r
2741 neighbourpos = np + dirs[i];
\r
2742 mindistance = distance;
\r
2744 //nodehilightbox = facebox;
\r
2746 const float d = 0.502;
\r
2747 core::aabbox3d<f32> nodebox
\r
2748 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2749 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2750 nodebox.MinEdge += nodepos_f;
\r
2751 nodebox.MaxEdge += nodepos_f;
\r
2752 nodehilightbox = nodebox;
\r
2754 } // if distance < mindistance
\r
2756 } // regular block
\r
2759 static float nodig_delay_counter = 0.0;
\r
2763 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2765 static float dig_time = 0.0;
\r
2766 static u16 dig_index = 0;
\r
2768 // Visualize selection
\r
2770 hilightboxes.push_back(nodehilightbox);
\r
2774 if(g_input->getLeftReleased())
\r
2776 client.clearTempMod(nodepos);
\r
2780 if(nodig_delay_counter > 0.0)
\r
2782 nodig_delay_counter -= dtime;
\r
2786 if(nodepos != nodepos_old)
\r
2788 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2789 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2791 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2793 client.clearTempMod(nodepos_old);
\r
2798 if(g_input->getLeftClicked() ||
\r
2799 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2801 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2802 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2804 if(g_input->getLeftClicked())
\r
2806 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2808 if(g_input->getLeftState())
\r
2810 MapNode n = client.getNode(nodepos);
\r
2812 // Get tool name. Default is "" = bare hands
\r
2813 std::string toolname = "";
\r
2814 InventoryList *mlist = local_inventory.getList("main");
\r
2817 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2818 if(item && (std::string)item->getName() == "ToolItem")
\r
2820 ToolItem *titem = (ToolItem*)item;
\r
2821 toolname = titem->getToolName();
\r
2825 // Get digging properties for material and tool
\r
2826 u8 material = n.d;
\r
2827 DiggingProperties prop =
\r
2828 getDiggingProperties(material, toolname);
\r
2830 float dig_time_complete = 0.0;
\r
2832 if(prop.diggable == false)
\r
2834 /*dstream<<"Material "<<(int)material
\r
2835 <<" not diggable with \""
\r
2836 <<toolname<<"\""<<std::endl;*/
\r
2837 // I guess nobody will wait for this long
\r
2838 dig_time_complete = 10000000.0;
\r
2842 dig_time_complete = prop.time;
\r
2845 if(dig_time_complete >= 0.001)
\r
2847 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2848 * dig_time/dig_time_complete);
\r
2850 // This is for torches
\r
2853 dig_index = CRACK_ANIMATION_LENGTH;
\r
2856 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2858 //TimeTaker timer("client.setTempMod");
\r
2859 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2860 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2864 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2865 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2866 client.clearTempMod(nodepos);
\r
2867 client.removeNode(nodepos);
\r
2871 nodig_delay_counter = dig_time_complete
\r
2872 / (float)CRACK_ANIMATION_LENGTH;
\r
2874 // We don't want a corresponding delay to
\r
2875 // very time consuming nodes
\r
2876 if(nodig_delay_counter > 0.5)
\r
2878 nodig_delay_counter = 0.5;
\r
2880 // We want a slight delay to very little
\r
2881 // time consuming nodes
\r
2882 float mindelay = 0.15;
\r
2883 if(nodig_delay_counter < mindelay)
\r
2885 nodig_delay_counter = mindelay;
\r
2889 dig_time += dtime;
\r
2893 if(g_input->getRightClicked())
\r
2895 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2896 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2899 nodepos_old = nodepos;
\r
2904 } // selected_object == NULL
\r
2906 g_input->resetLeftClicked();
\r
2907 g_input->resetRightClicked();
\r
2909 if(g_input->getLeftReleased())
\r
2911 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2913 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2915 if(g_input->getRightReleased())
\r
2917 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2921 g_input->resetLeftReleased();
\r
2922 g_input->resetRightReleased();
\r
2925 Calculate stuff for drawing
\r
2928 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2930 u32 daynight_ratio = client.getDayNightRatio();
\r
2931 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2932 video::SColor bgcolor = video::SColor(
\r
2934 skycolor.getRed() * l / 255,
\r
2935 skycolor.getGreen() * l / 255,
\r
2936 skycolor.getBlue() * l / 255);
\r
2942 if(g_settings.getBool("enable_fog") == true)
\r
2944 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2945 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2946 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2947 if(draw_control.range_all)
\r
2948 range = 100000*BS;
\r
2952 video::EFT_FOG_LINEAR,
\r
2956 false, // pixel fog
\r
2957 false // range fog
\r
2964 video::EFT_FOG_LINEAR,
\r
2968 false, // pixel fog
\r
2969 false // range fog
\r
2975 Update gui stuff (0ms)
\r
2978 //TimeTaker guiupdatetimer("Gui updating");
\r
2981 static float drawtime_avg = 0;
\r
2982 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2983 static float beginscenetime_avg = 0;
\r
2984 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2985 static float scenetime_avg = 0;
\r
2986 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2987 static float endscenetime_avg = 0;
\r
2988 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2990 char temptext[300];
\r
2991 snprintf(temptext, 300, "Minetest-c55 ("
\r
2993 ", R: range_all=%i"
\r
2995 " drawtime=%.0f, beginscenetime=%.0f"
\r
2996 ", scenetime=%.0f, endscenetime=%.0f",
\r
2998 draw_control.range_all,
\r
3000 beginscenetime_avg,
\r
3005 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3009 char temptext[300];
\r
3010 snprintf(temptext, 300,
\r
3011 "(% .1f, % .1f, % .1f)"
\r
3012 " (% .3f < btime_jitter < % .3f"
\r
3013 ", dtime_jitter = % .1f %%"
\r
3014 ", v_range = %.1f)",
\r
3015 player_position.X/BS,
\r
3016 player_position.Y/BS,
\r
3017 player_position.Z/BS,
\r
3018 busytime_jitter1_min_sample,
\r
3019 busytime_jitter1_max_sample,
\r
3020 dtime_jitter1_max_fraction * 100.0,
\r
3021 draw_control.wanted_range
\r
3024 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3028 guitext_info->setText(infotext.c_str());
\r
3032 Get chat messages from client
\r
3035 // Get new messages
\r
3036 std::wstring message;
\r
3037 while(client.getChatMessage(message))
\r
3039 chat_lines.push_back(ChatLine(message));
\r
3040 /*if(chat_lines.size() > 6)
\r
3042 core::list<ChatLine>::Iterator
\r
3043 i = chat_lines.begin();
\r
3044 chat_lines.erase(i);
\r
3047 // Append them to form the whole static text and throw
\r
3048 // it to the gui element
\r
3049 std::wstring whole;
\r
3050 // This will correspond to the line number counted from
\r
3051 // top to bottom, from size-1 to 0
\r
3052 s16 line_number = chat_lines.size();
\r
3053 // Count of messages to be removed from the top
\r
3054 u16 to_be_removed_count = 0;
\r
3055 for(core::list<ChatLine>::Iterator
\r
3056 i = chat_lines.begin();
\r
3057 i != chat_lines.end(); i++)
\r
3059 // After this, line number is valid for this loop
\r
3062 (*i).age += dtime;
\r
3064 This results in a maximum age of 60*6 to the
\r
3065 lowermost line and a maximum of 6 lines
\r
3067 float allowed_age = (6-line_number) * 60.0;
\r
3069 if((*i).age > allowed_age)
\r
3071 to_be_removed_count++;
\r
3074 whole += (*i).text + L'\n';
\r
3076 for(u16 i=0; i<to_be_removed_count; i++)
\r
3078 core::list<ChatLine>::Iterator
\r
3079 it = chat_lines.begin();
\r
3080 chat_lines.erase(it);
\r
3082 guitext_chat->setText(whole.c_str());
\r
3084 // Update gui element size and position
\r
3086 /*core::rect<s32> rect(
\r
3088 screensize.Y - guitext_chat_pad_bottom
\r
3089 - text_height*chat_lines.size(),
\r
3090 screensize.X - 10,
\r
3091 screensize.Y - guitext_chat_pad_bottom
\r
3093 core::rect<s32> rect(
\r
3096 screensize.X - 10,
\r
3097 50 + text_height*chat_lines.size()
\r
3100 guitext_chat->setRelativePosition(rect);
\r
3102 if(chat_lines.size() == 0)
\r
3103 guitext_chat->setVisible(false);
\r
3105 guitext_chat->setVisible(true);
\r
3112 static u16 old_selected_item = 65535;
\r
3113 if(client.getLocalInventoryUpdated()
\r
3114 || g_selected_item != old_selected_item)
\r
3116 old_selected_item = g_selected_item;
\r
3117 //std::cout<<"Updating local inventory"<<std::endl;
\r
3118 client.getLocalInventory(local_inventory);
\r
3119 /*quick_inventory->setSelection(g_selected_item);
\r
3120 quick_inventory->update();*/
\r
3124 Send actions returned by the inventory menu
\r
3126 while(inventory_action_queue.size() != 0)
\r
3128 InventoryAction *a = inventory_action_queue.pop_front();
\r
3130 client.sendInventoryAction(a);
\r
3139 TimeTaker drawtimer("Drawing");
\r
3143 TimeTaker timer("beginScene");
\r
3144 driver->beginScene(true, true, bgcolor);
\r
3145 //driver->beginScene(false, true, bgcolor);
\r
3146 beginscenetime = timer.stop(true);
\r
3151 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3154 TimeTaker timer("smgr");
\r
3156 scenetime = timer.stop(true);
\r
3160 //TimeTaker timer9("auxiliary drawings");
\r
3164 //TimeTaker //timer10("//timer10");
\r
3166 video::SMaterial m;
\r
3167 //m.Thickness = 10;
\r
3169 m.Lighting = false;
\r
3170 driver->setMaterial(m);
\r
3172 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3174 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3175 i != hilightboxes.end(); i++)
\r
3177 /*std::cout<<"hilightbox min="
\r
3178 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3180 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3182 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3188 if(g_settings.getBool("frametime_graph") == true)
\r
3191 for(core::list<float>::Iterator
\r
3192 i = frametime_log.begin();
\r
3193 i != frametime_log.end();
\r
3196 driver->draw2DLine(v2s32(x,50),
\r
3197 v2s32(x,50+(*i)*1000),
\r
3198 video::SColor(255,255,255,255));
\r
3206 if(g_show_map_plot && g_map_plot_texture)
\r
3208 core::dimension2d<u32> drawdim(640,480);
\r
3209 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3211 (screensize.X-drawdim.Width)/2,
\r
3212 (screensize.Y-drawdim.Height)/2
\r
3214 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3215 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3221 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3222 displaycenter + core::vector2d<s32>(10,0),
\r
3223 video::SColor(255,255,255,255));
\r
3224 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3225 displaycenter + core::vector2d<s32>(0,10),
\r
3226 video::SColor(255,255,255,255));
\r
3231 //TimeTaker //timer11("//timer11");
\r
3237 guienv->drawAll();
\r
3243 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3244 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3249 TimeTaker timer("endScene");
\r
3250 driver->endScene();
\r
3251 endscenetime = timer.stop(true);
\r
3254 drawtime = drawtimer.stop(true);
\r
3262 Refresh map plot if player has moved considerably
\r
3264 if(g_refresh_map_plot)
\r
3266 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3267 v3f p = client.getPlayerPosition() / BS;
\r
3268 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3270 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3271 old_player_pos = p;
\r
3273 g_refresh_map_plot = false;
\r
3277 static s16 lastFPS = 0;
\r
3278 //u16 fps = driver->getFPS();
\r
3279 u16 fps = (1.0/dtime_avg1);
\r
3281 if (lastFPS != fps)
\r
3283 core::stringw str = L"Minetest [";
\r
3284 str += driver->getName();
\r
3288 device->setWindowCaption(str.c_str());
\r
3294 device->yield();*/
\r
3297 //delete quick_inventory;
\r
3300 Disable texture fetches and other stuff that is queued
\r
3301 to be processed by the main loop.
\r
3303 This has to be done before client goes out of scope.
\r
3305 g_irrlicht->Shutdown(true);
\r
3307 } // client and server are deleted at this point
\r
3310 catch(con::PeerNotFoundException &e)
\r
3312 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3313 error_message = L"Connection timed out.";
\r
3316 } // Menu-game loop
\r
3321 In the end, delete the Irrlicht device.
\r
3326 Update configuration file
\r
3328 /*if(configpath != "")
\r
3330 g_settings.updateConfigFile(configpath.c_str());
\r
3333 END_DEBUG_EXCEPTION_HANDLER
\r
3335 debugstreams_deinit();
\r