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
169 TODO: Untie client network operations from framerate
\r
170 - Needs some input queues or something
\r
172 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
174 SUGG: Don't update all meshes always on single node changes, but
\r
175 check which ones should be updated
\r
176 - implement Map::updateNodeMeshes()
\r
178 TODO: Remove IrrlichtWrapper
\r
183 TODO: When player dies, throw items on map
\r
185 SUGG: Make an option to the server to disable building and digging near
\r
186 the starting position
\r
188 TODO: Copy the text of the last picked sign to inventory in creative
\r
191 TODO: Check what goes wrong with caching map to disk (Kray)
\r
194 FIXME: Server went into some infinite PeerNotFoundException loop
\r
196 * Fix the problem with the server constantly saving one or a few
\r
197 blocks? List the first saved block, maybe it explains.
\r
198 - It is probably caused by oscillating water
\r
199 * Make a small history check to transformLiquids to detect and log
\r
200 continuous oscillations, in such detail that they can be fixed.
\r
205 TODO: There has to be some better way to handle static objects than to
\r
206 send them all the time. This affects signs and item objects.
\r
207 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
208 need an additional metadata field for the texts
\r
209 - This is also needed for item container chests
\r
211 Block object server side:
\r
212 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
213 - For all blocks in the buffer, objects are stepped(). This
\r
214 means they are active.
\r
215 - TODO: A global active buffer is needed for the server
\r
216 - TODO: A timestamp to blocks
\r
217 - TODO: All blocks going in and out of the buffer are recorded.
\r
218 - TODO: For outgoing blocks, timestamp is written.
\r
219 - TODO: For incoming blocks, time difference is calculated and
\r
220 objects are stepped according to it.
\r
222 - When an active object goes far from a player, either delete
\r
223 it or store it statically.
\r
224 - When a statically stored active object comes near a player,
\r
225 recreate the active object
\r
227 * Continue making the scripting system:
\r
228 * Make updateNodeMesh for a less verbose mesh update on add/removenode
\r
229 * Switch to using a safe way for the self and env pointers
\r
230 * Make some global environment hooks, like node placed and general
\r
232 * Add a global Lua spawn handler and such
\r
233 * Get rid of MapBlockObjects
\r
234 * Other players could be sent to clients as LuaCAOs
\r
239 TODO: Mineral and ground material properties
\r
240 - This way mineral ground toughness can be calculated with just
\r
241 some formula, as well as tool strengths
\r
243 TODO: Flowing water to actually contain flow direction information
\r
245 TODO: Remove duplicate lighting implementation from Map (leave
\r
246 VoxelManipulator, which is faster)
\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 * 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
277 * Generate trees better
\r
278 - Add a "trees_added" flag to sector, or something
\r
279 * How 'bout making turbulence controlled so that for a given 2d position
\r
280 it can be completely turned off, and is usually so. This way generation
\r
281 can be sped up a lot.
\r
284 * only_from_disk might not work anymore - check and fix it.
\r
285 * Make the generator to run in background and not blocking block
\r
286 placement and transfer
\r
287 * Make chunks to be tiled vertically too
\r
291 * Make an "environment metafile" to store at least time of day
\r
292 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
293 - Or maybe move content_features to material.{h,cpp}?
\r
295 Make a system for pregenerating quick information for mapblocks, so
\r
296 that the client can show them as cubes before they are actually sent
\r
298 * Optimize VoxelManipulator lighting implementation by using indices
\r
299 in place of coordinates?
\r
301 Making it more portable:
\r
302 ------------------------
\r
303 * Some MSVC: std::sto* are defined without a namespace and collide
\r
304 with the ones in utility.h
\r
306 ======================================================================
\r
311 Setting this to 1 enables a special camera mode that forces
\r
312 the renderers to think that the camera statically points from
\r
313 the starting place to a static direction.
\r
315 This allows one to move around with the player and see what
\r
316 is actually drawn behind solid things and behind the player.
\r
318 #define FIELD_OF_VIEW_TEST 0
\r
322 #pragma message ("Disabling unit tests")
\r
324 #warning "Disabling unit tests"
\r
326 // Disable unit tests
\r
327 #define ENABLE_TESTS 0
\r
329 // Enable unit tests
\r
330 #define ENABLE_TESTS 1
\r
334 #pragma comment(lib, "Irrlicht.lib")
\r
335 //#pragma comment(lib, "jthread.lib")
\r
336 #pragma comment(lib, "zlibwapi.lib")
\r
337 #pragma comment(lib, "Shell32.lib")
\r
338 // This would get rid of the console window
\r
339 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
342 #include <iostream>
\r
344 #include <jmutexautolock.h>
\r
345 #include <locale.h>
\r
347 #include "common_irrlicht.h"
\r
350 #include "player.h"
\r
352 //#include "environment.h"
\r
353 #include "server.h"
\r
354 #include "client.h"
\r
355 //#include "serialization.h"
\r
356 #include "constants.h"
\r
357 //#include "strfnd.h"
\r
358 #include "porting.h"
\r
359 #include "irrlichtwrapper.h"
\r
360 #include "gettime.h"
\r
361 #include "porting.h"
\r
362 #include "guiPauseMenu.h"
\r
363 #include "guiInventoryMenu.h"
\r
364 #include "guiTextInputMenu.h"
\r
365 #include "materials.h"
\r
366 #include "guiMessageMenu.h"
\r
367 #include "filesys.h"
\r
368 #include "config.h"
\r
369 #include "guiMainMenu.h"
\r
370 #include "mineral.h"
\r
374 // TODO: Remove this
\r
375 IrrlichtWrapper *g_irrlicht = NULL;
\r
377 // This makes textures
\r
378 ITextureSource *g_texturesource = NULL;
\r
380 MapDrawControl draw_control;
\r
384 These are loaded from the config file.
\r
387 Settings g_settings;
\r
389 extern void set_default_settings();
\r
395 IrrlichtDevice *g_device = NULL;
\r
396 Client *g_client = NULL;
\r
398 /*const s16 quickinv_size = 40;
\r
399 const s16 quickinv_padding = 8;
\r
400 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
401 const s16 quickinv_outer_padding = 4;
\r
402 const s16 quickinv_itemcount = 8;*/
\r
404 const s32 hotbar_itemcount = 8;
\r
405 const s32 hotbar_imagesize = 36;
\r
411 gui::IGUIEnvironment* guienv = NULL;
\r
412 gui::IGUIStaticText *guiroot = NULL;
\r
414 class MainMenuManager : public IMenuManager
\r
417 virtual void createdMenu(GUIModalMenu *menu)
\r
419 for(core::list<GUIModalMenu*>::Iterator
\r
420 i = m_stack.begin();
\r
421 i != m_stack.end(); i++)
\r
423 assert(*i != menu);
\r
426 if(m_stack.size() != 0)
\r
427 (*m_stack.getLast())->setVisible(false);
\r
428 m_stack.push_back(menu);
\r
431 virtual void deletingMenu(GUIModalMenu *menu)
\r
433 // Remove all entries if there are duplicates
\r
434 bool removed_entry;
\r
436 removed_entry = false;
\r
437 for(core::list<GUIModalMenu*>::Iterator
\r
438 i = m_stack.begin();
\r
439 i != m_stack.end(); i++)
\r
444 removed_entry = true;
\r
448 }while(removed_entry);
\r
450 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
451 assert(*i == menu);
\r
452 m_stack.erase(i);*/
\r
454 if(m_stack.size() != 0)
\r
455 (*m_stack.getLast())->setVisible(true);
\r
460 return m_stack.size();
\r
463 core::list<GUIModalMenu*> m_stack;
\r
466 MainMenuManager g_menumgr;
\r
468 bool noMenuActive()
\r
470 return (g_menumgr.menuCount() == 0);
\r
473 bool g_disconnect_requested = false;
\r
475 class MainGameCallback : public IGameCallback
\r
478 virtual void exitToOS()
\r
480 g_device->closeDevice();
\r
483 virtual void disconnect()
\r
485 g_disconnect_requested = true;
\r
489 MainGameCallback g_gamecallback;
\r
491 // Inventory actions from the menu are buffered here before sending
\r
492 Queue<InventoryAction*> inventory_action_queue;
\r
493 // This is a copy of the inventory that the client's environment has
\r
494 Inventory local_inventory;
\r
496 u16 g_selected_item = 0;
\r
498 bool g_show_map_plot = false;
\r
499 bool g_refresh_map_plot = false;
\r
506 std::ostream *dout_con_ptr = &dummyout;
\r
507 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
508 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
509 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
510 //std::ostream *dout_con_ptr = &dstream;
\r
511 //std::ostream *derr_con_ptr = &dstream;
\r
514 std::ostream *dout_server_ptr = &dstream;
\r
515 std::ostream *derr_server_ptr = &dstream;
\r
518 std::ostream *dout_client_ptr = &dstream;
\r
519 std::ostream *derr_client_ptr = &dstream;
\r
522 gettime.h implementation
\r
528 Use irrlicht because it is more precise than porting.h's
\r
531 if(g_irrlicht == NULL)
\r
533 return g_irrlicht->getTime();
\r
540 struct TextDestSign : public TextDest
\r
542 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
544 m_blockpos = blockpos;
\r
548 void gotText(std::wstring text)
\r
550 std::string ntext = wide_to_narrow(text);
\r
551 dstream<<"Changing text of a sign object: "
\r
552 <<ntext<<std::endl;
\r
553 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
561 struct TextDestChat : public TextDest
\r
563 TextDestChat(Client *client)
\r
567 void gotText(std::wstring text)
\r
569 // Discard empty line
\r
573 // Parse command (server command starts with "/#")
\r
574 if(text[0] == L'/' && text[1] != L'#')
\r
576 std::wstring reply = L"Local: ";
\r
578 reply += L"Local commands not yet supported. "
\r
579 L"Server prefix is \"/#\".";
\r
581 m_client->addChatMessage(reply);
\r
586 m_client->sendChatMessage(text);
\r
588 m_client->addChatMessage(text);
\r
594 class MyEventReceiver : public IEventReceiver
\r
597 // This is the one method that we have to implement
\r
598 virtual bool OnEvent(const SEvent& event)
\r
601 React to nothing here if a menu is active
\r
603 if(noMenuActive() == false)
\r
609 // Remember whether each key is down or up
\r
610 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
612 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
614 if(event.KeyInput.PressedDown)
\r
616 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
617 if(g_show_map_plot)
\r
619 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
620 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
622 g_show_map_plot = false;
\r
631 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
633 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
635 dstream<<DTIME<<"MyEventReceiver: "
\r
636 <<"Launching pause menu"<<std::endl;
\r
637 // It will delete itself by itself
\r
638 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
639 &g_menumgr))->drop();
\r
642 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
644 dstream<<DTIME<<"MyEventReceiver: "
\r
645 <<"Launching inventory"<<std::endl;
\r
646 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
647 &local_inventory, &inventory_action_queue,
\r
648 &g_menumgr))->drop();
\r
651 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
653 TextDest *dest = new TextDestChat(g_client);
\r
655 (new GUITextInputMenu(guienv, guiroot, -1,
\r
662 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
663 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
665 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
666 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
668 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
669 g_selected_item = s1-1;
\r
670 dstream<<DTIME<<"Selected item: "
\r
671 <<g_selected_item<<std::endl;
\r
674 // Viewing range selection
\r
675 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
677 if(draw_control.range_all)
\r
679 draw_control.range_all = false;
\r
680 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
684 draw_control.range_all = true;
\r
685 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
689 // Print debug stacks
\r
690 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
692 dstream<<"-----------------------------------------"
\r
694 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
695 dstream<<"-----------------------------------------"
\r
697 debug_stacks_print();
\r
701 if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
703 dstream<<"Map plot requested"<<std::endl;
\r
704 g_show_map_plot = !g_show_map_plot;
\r
705 if(g_show_map_plot)
\r
706 g_refresh_map_plot = true;
\r
712 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
714 if(noMenuActive() == false)
\r
716 left_active = false;
\r
717 middle_active = false;
\r
718 right_active = false;
\r
722 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
723 left_active = event.MouseInput.isLeftPressed();
\r
724 middle_active = event.MouseInput.isMiddlePressed();
\r
725 right_active = event.MouseInput.isRightPressed();
\r
727 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
729 leftclicked = true;
\r
731 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
733 rightclicked = true;
\r
735 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
737 leftreleased = true;
\r
739 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
741 rightreleased = true;
\r
743 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
745 /*dstream<<"event.MouseInput.Wheel="
\r
746 <<event.MouseInput.Wheel<<std::endl;*/
\r
748 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
749 hotbar_itemcount-1);
\r
750 if(event.MouseInput.Wheel < 0)
\r
752 if(g_selected_item < max_item)
\r
755 g_selected_item = 0;
\r
757 else if(event.MouseInput.Wheel > 0)
\r
759 if(g_selected_item > 0)
\r
762 g_selected_item = max_item;
\r
771 // This is used to check whether a key is being held down
\r
772 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
774 return keyIsDown[keyCode];
\r
779 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
780 keyIsDown[i] = false;
\r
782 leftclicked = false;
\r
783 rightclicked = false;
\r
784 leftreleased = false;
\r
785 rightreleased = false;
\r
787 left_active = false;
\r
788 middle_active = false;
\r
789 right_active = false;
\r
800 bool rightreleased;
\r
803 bool middle_active;
\r
807 // We use this array to store the current state of each key
\r
808 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
811 IrrlichtDevice *m_device;
\r
820 virtual ~InputHandler()
\r
824 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
826 virtual v2s32 getMousePos() = 0;
\r
827 virtual void setMousePos(s32 x, s32 y) = 0;
\r
829 virtual bool getLeftState() = 0;
\r
830 virtual bool getRightState() = 0;
\r
832 virtual bool getLeftClicked() = 0;
\r
833 virtual bool getRightClicked() = 0;
\r
834 virtual void resetLeftClicked() = 0;
\r
835 virtual void resetRightClicked() = 0;
\r
837 virtual bool getLeftReleased() = 0;
\r
838 virtual bool getRightReleased() = 0;
\r
839 virtual void resetLeftReleased() = 0;
\r
840 virtual void resetRightReleased() = 0;
\r
842 virtual void step(float dtime) {};
\r
844 virtual void clear() {};
\r
847 InputHandler *g_input = NULL;
\r
849 class RealInputHandler : public InputHandler
\r
852 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
854 m_receiver(receiver)
\r
857 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
859 return m_receiver->IsKeyDown(keyCode);
\r
861 virtual v2s32 getMousePos()
\r
863 return m_device->getCursorControl()->getPosition();
\r
865 virtual void setMousePos(s32 x, s32 y)
\r
867 m_device->getCursorControl()->setPosition(x, y);
\r
870 virtual bool getLeftState()
\r
872 return m_receiver->left_active;
\r
874 virtual bool getRightState()
\r
876 return m_receiver->right_active;
\r
879 virtual bool getLeftClicked()
\r
881 return m_receiver->leftclicked;
\r
883 virtual bool getRightClicked()
\r
885 return m_receiver->rightclicked;
\r
887 virtual void resetLeftClicked()
\r
889 m_receiver->leftclicked = false;
\r
891 virtual void resetRightClicked()
\r
893 m_receiver->rightclicked = false;
\r
896 virtual bool getLeftReleased()
\r
898 return m_receiver->leftreleased;
\r
900 virtual bool getRightReleased()
\r
902 return m_receiver->rightreleased;
\r
904 virtual void resetLeftReleased()
\r
906 m_receiver->leftreleased = false;
\r
908 virtual void resetRightReleased()
\r
910 m_receiver->rightreleased = false;
\r
915 resetRightClicked();
\r
916 resetLeftClicked();
\r
919 IrrlichtDevice *m_device;
\r
920 MyEventReceiver *m_receiver;
\r
923 class RandomInputHandler : public InputHandler
\r
926 RandomInputHandler()
\r
930 leftclicked = false;
\r
931 rightclicked = false;
\r
932 leftreleased = false;
\r
933 rightreleased = false;
\r
934 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
935 keydown[i] = false;
\r
937 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
939 return keydown[keyCode];
\r
941 virtual v2s32 getMousePos()
\r
945 virtual void setMousePos(s32 x, s32 y)
\r
947 mousepos = v2s32(x,y);
\r
950 virtual bool getLeftState()
\r
954 virtual bool getRightState()
\r
959 virtual bool getLeftClicked()
\r
961 return leftclicked;
\r
963 virtual bool getRightClicked()
\r
965 return rightclicked;
\r
967 virtual void resetLeftClicked()
\r
969 leftclicked = false;
\r
971 virtual void resetRightClicked()
\r
973 rightclicked = false;
\r
976 virtual bool getLeftReleased()
\r
978 return leftreleased;
\r
980 virtual bool getRightReleased()
\r
982 return rightreleased;
\r
984 virtual void resetLeftReleased()
\r
986 leftreleased = false;
\r
988 virtual void resetRightReleased()
\r
990 rightreleased = false;
\r
993 virtual void step(float dtime)
\r
996 static float counter1 = 0;
\r
1000 counter1 = 0.1*Rand(1, 40);
\r
1001 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\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_KEY_E] = !keydown[irr::KEY_KEY_E];
\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_W] = !keydown[irr::KEY_KEY_W];
\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_A] = !keydown[irr::KEY_KEY_A];
\r
1032 static float counter1 = 0;
\r
1033 counter1 -= dtime;
\r
1034 if(counter1 < 0.0)
\r
1036 counter1 = 0.1*Rand(1, 20);
\r
1037 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1041 static float counter1 = 0;
\r
1042 counter1 -= dtime;
\r
1043 if(counter1 < 0.0)
\r
1045 counter1 = 0.1*Rand(1, 30);
\r
1046 leftdown = !leftdown;
\r
1048 leftclicked = true;
\r
1050 leftreleased = true;
\r
1054 static float counter1 = 0;
\r
1055 counter1 -= dtime;
\r
1056 if(counter1 < 0.0)
\r
1058 counter1 = 0.1*Rand(1, 15);
\r
1059 rightdown = !rightdown;
\r
1061 rightclicked = true;
\r
1063 rightreleased = true;
\r
1066 mousepos += mousespeed;
\r
1069 s32 Rand(s32 min, s32 max)
\r
1071 return (myrand()%(max-min+1))+min;
\r
1074 bool keydown[KEY_KEY_CODES_COUNT];
\r
1080 bool rightclicked;
\r
1081 bool leftreleased;
\r
1082 bool rightreleased;
\r
1085 void updateViewingRange(f32 frametime_in, Client *client)
\r
1087 if(draw_control.range_all == true)
\r
1090 static f32 added_frametime = 0;
\r
1091 static s16 added_frames = 0;
\r
1093 added_frametime += frametime_in;
\r
1094 added_frames += 1;
\r
1096 // Actually this counter kind of sucks because frametime is busytime
\r
1097 static f32 counter = 0;
\r
1098 counter -= frametime_in;
\r
1104 /*dstream<<__FUNCTION_NAME
\r
1105 <<": Collected "<<added_frames<<" frames, total of "
\r
1106 <<added_frametime<<"s."<<std::endl;*/
\r
1108 /*dstream<<"draw_control.blocks_drawn="
\r
1109 <<draw_control.blocks_drawn
\r
1110 <<", draw_control.blocks_would_have_drawn="
\r
1111 <<draw_control.blocks_would_have_drawn
\r
1114 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1115 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1117 draw_control.wanted_min_range = range_min;
\r
1118 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1120 float block_draw_ratio = 1.0;
\r
1121 if(draw_control.blocks_would_have_drawn != 0)
\r
1123 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1124 / (float)draw_control.blocks_would_have_drawn;
\r
1127 // Calculate the average frametime in the case that all wanted
\r
1128 // blocks had been drawn
\r
1129 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1131 added_frametime = 0.0;
\r
1134 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1135 float wanted_frametime = 1.0 / wanted_fps;
\r
1137 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1138 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1140 // If needed frametime change is small, just return
\r
1141 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1143 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1147 float range = draw_control.wanted_range;
\r
1148 float new_range = range;
\r
1150 static s16 range_old = 0;
\r
1151 static f32 frametime_old = 0;
\r
1153 float d_range = range - range_old;
\r
1154 f32 d_frametime = frametime - frametime_old;
\r
1155 // A sane default of 30ms per 50 nodes of range
\r
1156 static f32 time_per_range = 30. / 50;
\r
1159 time_per_range = d_frametime / d_range;
\r
1162 // The minimum allowed calculated frametime-range derivative:
\r
1163 // Practically this sets the maximum speed of changing the range.
\r
1164 // The lower this value, the higher the maximum changing speed.
\r
1165 // A low value here results in wobbly range (0.001)
\r
1166 // A high value here results in slow changing range (0.0025)
\r
1167 // SUGG: This could be dynamically adjusted so that when
\r
1168 // the camera is turning, this is lower
\r
1169 //float min_time_per_range = 0.0015;
\r
1170 float min_time_per_range = 0.0010;
\r
1171 //float min_time_per_range = 0.05 / range;
\r
1172 if(time_per_range < min_time_per_range)
\r
1174 time_per_range = min_time_per_range;
\r
1175 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1179 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1182 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1183 // Dampen the change a bit to kill oscillations
\r
1184 //wanted_range_change *= 0.9;
\r
1185 //wanted_range_change *= 0.75;
\r
1186 wanted_range_change *= 0.5;
\r
1187 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1189 // If needed range change is very small, just return
\r
1190 if(fabs(wanted_range_change) < 0.001)
\r
1192 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1196 new_range += wanted_range_change;
\r
1197 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1199 //float new_range_unclamped = new_range;
\r
1200 if(new_range < range_min)
\r
1201 new_range = range_min;
\r
1202 if(new_range > range_max)
\r
1203 new_range = range_max;
\r
1205 /*if(new_range != new_range_unclamped)
\r
1206 dstream<<", clamped to "<<new_range<<std::endl;
\r
1208 dstream<<std::endl;*/
\r
1210 draw_control.wanted_range = new_range;
\r
1212 range_old = new_range;
\r
1213 frametime_old = frametime;
\r
1216 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1217 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1218 Inventory *inventory)
\r
1220 InventoryList *mainlist = inventory->getList("main");
\r
1221 if(mainlist == NULL)
\r
1223 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1227 s32 padding = imgsize/12;
\r
1228 //s32 height = imgsize + padding*2;
\r
1229 s32 width = itemcount*(imgsize+padding*2);
\r
1231 // Position of upper left corner of bar
\r
1232 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1234 // Draw background color
\r
1235 /*core::rect<s32> barrect(0,0,width,height);
\r
1237 video::SColor bgcolor(255,128,128,128);
\r
1238 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1240 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1242 for(s32 i=0; i<itemcount; i++)
\r
1244 InventoryItem *item = mainlist->getItem(i);
\r
1246 core::rect<s32> rect = imgrect + pos
\r
1247 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1249 if(g_selected_item == i)
\r
1251 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1252 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1253 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1258 video::SColor bgcolor2(128,0,0,0);
\r
1259 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1264 drawInventoryItem(driver, font, item, rect, NULL);
\r
1269 video::ITexture *g_map_plot_texture = NULL;
\r
1270 float g_map_plot_texture_scale = 4;
\r
1272 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1278 core::dimension2d<u32> dim(640,480);
\r
1279 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1281 for(u32 y=0; y<dim.Height; y++)
\r
1282 for(u32 x=0; x<dim.Width; x++)
\r
1284 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1285 pf *= g_map_plot_texture_scale;
\r
1287 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1290 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1291 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1293 // Screen coordinates that are based on multiples of
\r
1294 // 1000/g_map_plot_texture_scale and never negative
\r
1295 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1296 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1297 // Offset to center of image
\r
1298 ux -= dim.Width/2;
\r
1299 uy -= dim.Height/2;
\r
1301 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1302 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1303 c.set(255, 255, 255, 255);
\r
1304 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1305 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1306 c.set(255, 160, 160, 160);
\r
1307 else if(h < WATER_LEVEL - 0.5) // Water
\r
1308 c.set(255, 50, 50, 255);
\r
1310 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1311 || (h < WATER_LEVEL + 2
\r
1312 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1316 h = 1.0 - exp(-h);
\r
1318 video::SColor c1(255,237,201,175);
\r
1319 //video::SColor c2(255,20,20,20);
\r
1320 video::SColor c2(255,150,0,0);
\r
1321 c = c2.getInterpolated(c1, h);
\r
1327 h = 1.0 - exp(-h);
\r
1329 video::SColor c1(255,110,185,90);
\r
1330 //video::SColor c2(255,20,20,20);
\r
1331 video::SColor c2(255,150,0,0);
\r
1332 c = c2.getInterpolated(c1, h);
\r
1337 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1341 h = 1.0 - exp(-h);
\r
1343 video::SColor c1(255,237,201,175);
\r
1344 //video::SColor c2(255,20,20,20);
\r
1345 video::SColor c2(255,150,0,0);
\r
1346 c = c2.getInterpolated(c1, h);
\r
1350 else if(h < WATER_LEVEL + 2
\r
1351 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1352 c.set(255, 237, 201, 175);
\r
1354 else if(h < WATER_LEVEL + 10)
\r
1355 c.set(255, 50, 150, 50); // Green
\r
1356 else if(h < WATER_LEVEL + 20)
\r
1357 c.set(255, 110, 185, 50); // Yellowish green
\r
1358 else if(h < WATER_LEVEL + 40)
\r
1359 c.set(255, 180, 210, 50); // Greenish yellow
\r
1360 else if(h < WATER_LEVEL + 60)
\r
1361 c.set(255, 220, 220, 50); // Yellow
\r
1362 else if(h < WATER_LEVEL + 80)
\r
1363 c.set(255, 200, 200, 110); // Yellowish white
\r
1364 else if(h < WATER_LEVEL + 100)
\r
1365 c.set(255, 190, 190, 190); // Grey
\r
1367 c.set(255, 255, 255, 255); // White
\r
1375 h = 1.0 - exp(-h);
\r
1377 video::SColor c1(255,200,200,50);
\r
1378 video::SColor c2(255,0,150,0);
\r
1379 c = c1.getInterpolated(c2, h);
\r
1381 /*u32 a = (u32)(h*255);
\r
1385 c.set(255, a, a, a);*/
\r
1389 if(h >= WATER_LEVEL - 0.5
\r
1390 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1392 video::SColor c1(255,237,201,175);
\r
1393 c = c.getInterpolated(c1, 0.5);
\r
1397 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1400 video::SColor c1(255,255,0,0);
\r
1401 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1404 img->setPixel(x, y, c);
\r
1406 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1408 assert(g_map_plot_texture);
\r
1418 ChatLine(const std::wstring &a_text):
\r
1424 std::wstring text;
\r
1427 // These are defined global so that they're not optimized too much.
\r
1428 // Can't change them to volatile.
\r
1433 std::string tempstring;
\r
1434 std::string tempstring2;
\r
1439 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1440 TimeTaker timer("Testing std::string speed");
\r
1441 const u32 jj = 10000;
\r
1442 for(u32 j=0; j<jj; j++)
\r
1446 const u32 ii = 10;
\r
1447 for(u32 i=0; i<ii; i++){
\r
1448 tempstring2 += "asd";
\r
1450 for(u32 i=0; i<ii+1; i++){
\r
1451 tempstring += "asd";
\r
1452 if(tempstring == tempstring2)
\r
1458 dstream<<"All of the following tests should take around 100ms each."
\r
1462 TimeTaker timer("Testing floating-point conversion speed");
\r
1464 for(u32 i=0; i<4000000; i++){
\r
1471 TimeTaker timer("Testing floating-point vector speed");
\r
1473 tempv3f1 = v3f(1,2,3);
\r
1474 tempv3f2 = v3f(4,5,6);
\r
1475 for(u32 i=0; i<10000000; i++){
\r
1476 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1477 tempv3f2 += v3f(7,8,9);
\r
1482 TimeTaker timer("Testing core::map speed");
\r
1484 core::map<v2s16, f32> map1;
\r
1487 for(s16 y=0; y<ii; y++){
\r
1488 for(s16 x=0; x<ii; x++){
\r
1489 map1.insert(v2s16(x,y), tempf);
\r
1493 for(s16 y=ii-1; y>=0; y--){
\r
1494 for(s16 x=0; x<ii; x++){
\r
1495 tempf = map1[v2s16(x,y)];
\r
1501 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1502 TimeTaker timer("Testing mutex speed");
\r
1515 // Do at least 10ms
\r
1516 while(timer.getTime() < 10);
\r
1518 u32 dtime = timer.stop();
\r
1519 u32 per_ms = n / dtime;
\r
1520 std::cout<<"Done. "<<dtime<<"ms, "
\r
1521 <<per_ms<<"/ms"<<std::endl;
\r
1525 int main(int argc, char *argv[])
\r
1528 Parse command line
\r
1531 // List all allowed options
\r
1532 core::map<std::string, ValueSpec> allowed_options;
\r
1533 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1534 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1535 "Run server directly"));
\r
1536 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1537 "Load configuration from specified file"));
\r
1538 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1539 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1540 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1541 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1542 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1543 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1545 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1547 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1549 Settings cmd_args;
\r
1551 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1553 if(ret == false || cmd_args.getFlag("help"))
\r
1555 dstream<<"Allowed options:"<<std::endl;
\r
1556 for(core::map<std::string, ValueSpec>::Iterator
\r
1557 i = allowed_options.getIterator();
\r
1558 i.atEnd() == false; i++)
\r
1560 dstream<<" --"<<i.getNode()->getKey();
\r
1561 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1566 dstream<<" <value>";
\r
1568 dstream<<std::endl;
\r
1570 if(i.getNode()->getValue().help != NULL)
\r
1572 dstream<<" "<<i.getNode()->getValue().help
\r
1577 return cmd_args.getFlag("help") ? 0 : 1;
\r
1581 Low-level initialization
\r
1584 bool disable_stderr = false;
\r
1586 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1587 disable_stderr = true;
\r
1590 // Initialize debug streams
\r
1591 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1592 // Initialize debug stacks
\r
1593 debug_stacks_init();
\r
1595 DSTACK(__FUNCTION_NAME);
\r
1597 porting::signal_handler_init();
\r
1598 bool &kill = *porting::signal_handler_killstatus();
\r
1600 porting::initializePaths();
\r
1601 // Create user data directory
\r
1602 fs::CreateDir(porting::path_userdata);
\r
1604 // C-style stuff initialization
\r
1605 initializeMaterialProperties();
\r
1608 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1610 // Print startup message
\r
1611 dstream<<DTIME<<"minetest-c55"
\r
1612 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1613 <<", "<<BUILD_INFO
\r
1617 Basic initialization
\r
1620 // Initialize default settings
\r
1621 set_default_settings();
\r
1623 // Set locale. This is for forcing '.' as the decimal point.
\r
1624 std::locale::global(std::locale("C"));
\r
1625 // This enables printing all characters in bitmap font
\r
1626 setlocale(LC_CTYPE, "en_US");
\r
1628 // Initialize sockets
\r
1630 atexit(sockets_cleanup);
\r
1640 // Path of configuration file in use
\r
1641 std::string configpath = "";
\r
1643 if(cmd_args.exists("config"))
\r
1645 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1648 dstream<<"Could not read configuration from \""
\r
1649 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1652 configpath = cmd_args.get("config");
\r
1656 core::array<std::string> filenames;
\r
1657 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1658 #ifdef RUN_IN_PLACE
\r
1659 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1662 for(u32 i=0; i<filenames.size(); i++)
\r
1664 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1667 configpath = filenames[i];
\r
1672 // If no path found, use the first one (menu creates the file)
\r
1673 if(configpath == "")
\r
1674 configpath = filenames[0];
\r
1677 // Initialize random seed
\r
1682 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1684 These are needed for unit tests at least.
\r
1687 // Initial call with g_texturesource not set.
\r
1694 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1695 || cmd_args.getFlag("enable-unittests") == true)
\r
1700 /*for(s16 y=-100; y<100; y++)
\r
1701 for(s16 x=-100; x<100; x++)
\r
1703 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1713 if(cmd_args.exists("port"))
\r
1714 port = cmd_args.getU16("port");
\r
1715 else if(cmd_args.exists("port"))
\r
1716 port = g_settings.getU16("port");
\r
1719 std::string map_dir = porting::path_userdata+"/map";
\r
1720 if(cmd_args.exists("map-dir"))
\r
1721 map_dir = cmd_args.get("map-dir");
\r
1722 else if(g_settings.exists("map-dir"))
\r
1723 map_dir = g_settings.get("map-dir");
\r
1725 // Run dedicated server if asked to
\r
1726 if(cmd_args.getFlag("server"))
\r
1728 DSTACK("Dedicated server branch");
\r
1731 Server server(map_dir.c_str());
\r
1732 server.start(port);
\r
1735 dedicated_server_loop(server, kill);
\r
1744 // Address to connect to
\r
1745 std::string address = "";
\r
1747 if(cmd_args.exists("address"))
\r
1749 address = cmd_args.get("address");
\r
1753 address = g_settings.get("address");
\r
1756 std::string playername = g_settings.get("name");
\r
1758 // Resolution selection
\r
1760 bool fullscreen = false;
\r
1761 u16 screenW = g_settings.getU16("screenW");
\r
1762 u16 screenH = g_settings.getU16("screenH");
\r
1764 // Determine driver
\r
1766 video::E_DRIVER_TYPE driverType;
\r
1768 std::string driverstring = g_settings.get("video_driver");
\r
1770 if(driverstring == "null")
\r
1771 driverType = video::EDT_NULL;
\r
1772 else if(driverstring == "software")
\r
1773 driverType = video::EDT_SOFTWARE;
\r
1774 else if(driverstring == "burningsvideo")
\r
1775 driverType = video::EDT_BURNINGSVIDEO;
\r
1776 else if(driverstring == "direct3d8")
\r
1777 driverType = video::EDT_DIRECT3D8;
\r
1778 else if(driverstring == "direct3d9")
\r
1779 driverType = video::EDT_DIRECT3D9;
\r
1780 else if(driverstring == "opengl")
\r
1781 driverType = video::EDT_OPENGL;
\r
1784 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1785 "to opengl"<<std::endl;
\r
1786 driverType = video::EDT_OPENGL;
\r
1789 // create device and exit if creation failed
\r
1791 MyEventReceiver receiver;
\r
1793 IrrlichtDevice *device;
\r
1794 device = createDevice(driverType,
\r
1795 core::dimension2d<u32>(screenW, screenH),
\r
1796 16, fullscreen, false, false, &receiver);
\r
1799 return 1; // could not create selected driver.
\r
1801 g_device = device;
\r
1802 g_irrlicht = new IrrlichtWrapper(device);
\r
1803 TextureSource *texturesource = new TextureSource(device);
\r
1804 g_texturesource = texturesource;
\r
1807 Speed tests (done after irrlicht is loaded to get timer)
\r
1809 if(cmd_args.getFlag("speedtests"))
\r
1811 dstream<<"Running speed tests"<<std::endl;
\r
1816 device->setResizable(true);
\r
1818 bool random_input = g_settings.getBool("random_input")
\r
1819 || cmd_args.getFlag("random-input");
\r
1821 g_input = new RandomInputHandler();
\r
1823 g_input = new RealInputHandler(device, &receiver);
\r
1826 Continue initialization
\r
1829 video::IVideoDriver* driver = device->getVideoDriver();
\r
1832 This changes the minimum allowed number of vertices in a VBO.
\r
1835 //driver->setMinHardwareBufferVertexCount(50);
\r
1837 scene::ISceneManager* smgr = device->getSceneManager();
\r
1839 guienv = device->getGUIEnvironment();
\r
1840 gui::IGUISkin* skin = guienv->getSkin();
\r
1841 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1843 skin->setFont(font);
\r
1845 dstream<<"WARNING: Font file was not found."
\r
1846 " Using default font."<<std::endl;
\r
1847 // If font was not found, this will get us one
\r
1848 font = skin->getFont();
\r
1851 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1852 dstream<<"text_height="<<text_height<<std::endl;
\r
1854 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1855 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1856 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1857 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1858 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1859 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1862 Preload some textures and stuff
\r
1865 init_content_inventory_texture_paths();
\r
1866 init_mapnode(); // Second call with g_texturesource set
\r
1874 We need some kind of a root node to be able to add
\r
1875 custom gui elements directly on the screen.
\r
1876 Otherwise they won't be automatically drawn.
\r
1878 guiroot = guienv->addStaticText(L"",
\r
1879 core::rect<s32>(0, 0, 10000, 10000));
\r
1881 // First line of debug text
\r
1882 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1884 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1886 // Second line of debug text
\r
1887 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1889 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1892 // At the middle of the screen
\r
1893 // Object infos are shown in this
\r
1894 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1896 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1900 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1902 core::rect<s32>(0,0,0,0),
\r
1903 false, false); // Disable word wrap as of now
\r
1905 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1906 core::list<ChatLine> chat_lines;
\r
1909 If an error occurs, this is set to something and the
\r
1910 menu-game loop is restarted. It is then displayed before
\r
1913 std::wstring error_message = L"";
\r
1918 while(g_device->run() && kill == false)
\r
1921 // This is used for catching disconnects
\r
1926 Out-of-game menu loop.
\r
1928 Loop quits when menu returns proper parameters.
\r
1930 while(kill == false)
\r
1932 // Cursor can be non-visible when coming from the game
\r
1933 device->getCursorControl()->setVisible(true);
\r
1934 // Some stuff are left to scene manager when coming from the game
\r
1935 // (map at least?)
\r
1937 // Reset or hide the debug gui texts
\r
1938 guitext->setText(L"Minetest-c55");
\r
1939 guitext2->setVisible(false);
\r
1940 guitext_info->setVisible(false);
\r
1941 guitext_chat->setVisible(false);
\r
1943 // Initialize menu data
\r
1944 MainMenuData menudata;
\r
1945 menudata.address = narrow_to_wide(address);
\r
1946 menudata.name = narrow_to_wide(playername);
\r
1947 menudata.port = narrow_to_wide(itos(port));
\r
1948 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1950 GUIMainMenu *menu =
\r
1951 new GUIMainMenu(guienv, guiroot, -1,
\r
1952 &g_menumgr, &menudata, &g_gamecallback);
\r
1953 menu->allowFocusRemoval(true);
\r
1955 if(error_message != L"")
\r
1957 GUIMessageMenu *menu2 =
\r
1958 new GUIMessageMenu(guienv, guiroot, -1,
\r
1959 &g_menumgr, error_message.c_str());
\r
1961 error_message = L"";
\r
1964 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1966 dstream<<"Created main menu"<<std::endl;
\r
1968 while(g_device->run() && kill == false)
\r
1970 // Run global IrrlichtWrapper's main thread processing stuff
\r
1971 g_irrlicht->Run();
\r
1973 if(menu->getStatus() == true)
\r
1976 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1977 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1978 guienv->drawAll();
\r
1979 driver->endScene();
\r
1982 // Break out of menu-game loop to shut down cleanly
\r
1983 if(g_device->run() == false || kill == true)
\r
1986 dstream<<"Dropping main menu"<<std::endl;
\r
1990 // Delete map if requested
\r
1991 if(menudata.delete_map)
\r
1993 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1995 error_message = L"Delete failed";
\r
1999 playername = wide_to_narrow(menudata.name);
\r
2000 address = wide_to_narrow(menudata.address);
\r
2001 port = stoi(wide_to_narrow(menudata.port));
\r
2002 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
2004 // Check for valid parameters, restart menu if invalid.
\r
2005 if(playername == "")
\r
2007 error_message = L"Name required.";
\r
2012 g_settings.set("name", playername);
\r
2013 g_settings.set("address", address);
\r
2014 g_settings.set("port", itos(port));
\r
2015 // Update configuration file
\r
2016 if(configpath != "")
\r
2017 g_settings.updateConfigFile(configpath.c_str());
\r
2019 // Continue to game
\r
2023 // Break out of menu-game loop to shut down cleanly
\r
2024 if(g_device->run() == false)
\r
2028 Make a scope here so that the client and the server and other
\r
2029 stuff gets removed when disconnected or the irrlicht device
\r
2034 // This is set to true at the end of the scope
\r
2035 g_irrlicht->Shutdown(false);
\r
2038 Draw "Loading" screen
\r
2040 const wchar_t *text = L"Loading and connecting...";
\r
2041 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2042 core::vector2d<s32> textsize(300, text_height);
\r
2043 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2045 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2046 text, textrect, false, false);
\r
2047 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2049 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2050 guienv->drawAll();
\r
2051 driver->endScene();
\r
2053 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2057 SharedPtr will delete it when it goes out of scope.
\r
2059 SharedPtr<Server> server;
\r
2060 if(address == ""){
\r
2061 server = new Server(map_dir);
\r
2062 server->start(port);
\r
2069 Client client(device, playername.c_str(), draw_control);
\r
2071 g_client = &client;
\r
2073 Address connect_address(0,0,0,0, port);
\r
2076 //connect_address.Resolve("localhost");
\r
2077 connect_address.setAddress(127,0,0,1);
\r
2079 connect_address.Resolve(address.c_str());
\r
2081 catch(ResolveError &e)
\r
2083 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2085 error_message = L"Couldn't resolve address";
\r
2086 gui_loadingtext->remove();
\r
2090 dstream<<DTIME<<"Connecting to server at ";
\r
2091 connect_address.print(&dstream);
\r
2092 dstream<<std::endl;
\r
2093 client.connect(connect_address);
\r
2096 while(client.connectedAndInitialized() == false)
\r
2099 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2100 guienv->drawAll();
\r
2101 driver->endScene();
\r
2103 // Update client and server
\r
2107 if(server != NULL)
\r
2108 server->step(0.1);
\r
2114 catch(con::PeerNotFoundException &e)
\r
2116 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2118 error_message = L"Connection timed out.";
\r
2119 gui_loadingtext->remove();
\r
2126 /*scene::ISceneNode* skybox;
\r
2127 skybox = smgr->addSkyBoxSceneNode(
\r
2128 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2129 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2130 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2131 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2132 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2133 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2136 Create the camera node
\r
2139 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2140 0, // Camera parent
\r
2141 v3f(BS*100, BS*2, BS*100), // Look from
\r
2142 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2146 if(camera == NULL)
\r
2149 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2150 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2151 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2153 camera->setFOV(FOV_ANGLE);
\r
2155 // Just so big a value that everything rendered is visible
\r
2156 camera->setFarValue(100000*BS);
\r
2159 Lighting test code. Doesn't quite work this way.
\r
2160 The CPU-computed lighting is good.
\r
2164 smgr->addLightSceneNode(NULL,
\r
2165 v3f(0, BS*1000000, 0),
\r
2166 video::SColorf(0.3,0.3,0.3),
\r
2169 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2171 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2172 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2175 f32 camera_yaw = 0; // "right/left"
\r
2176 f32 camera_pitch = 0; // "up/down"
\r
2182 gui_loadingtext->remove();
\r
2185 Add some gui stuff
\r
2188 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2189 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2190 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2191 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2193 // Test the text input system
\r
2194 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2196 /*GUIMessageMenu *menu =
\r
2197 new GUIMessageMenu(guienv, guiroot, -1,
\r
2202 // Launch pause menu
\r
2203 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2204 &g_menumgr))->drop();
\r
2207 guitext2->setVisible(true);
\r
2208 guitext_info->setVisible(true);
\r
2209 guitext_chat->setVisible(true);
\r
2211 //s32 guitext_chat_pad_bottom = 70;
\r
2213 v2u32 screensize(0,0);
\r
2214 v2u32 last_screensize(0,0);
\r
2217 Some statistics are collected in these
\r
2220 u32 beginscenetime = 0;
\r
2221 u32 scenetime = 0;
\r
2222 u32 endscenetime = 0;
\r
2225 //throw con::PeerNotFoundException("lol");
\r
2227 core::list<float> frametime_log;
\r
2233 bool first_loop_after_window_activation = true;
\r
2235 // Time is in milliseconds
\r
2236 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2237 // NOTE: So we have to use getTime() and call run()s between them
\r
2238 u32 lasttime = device->getTimer()->getTime();
\r
2240 while(device->run() && kill == false)
\r
2242 if(g_disconnect_requested)
\r
2244 g_disconnect_requested = false;
\r
2249 Run global IrrlichtWrapper's main thread processing stuff
\r
2251 g_irrlicht->Run();
\r
2254 Process TextureSource's queue
\r
2256 texturesource->processQueue();
\r
2259 Random calculations
\r
2261 last_screensize = screensize;
\r
2262 screensize = driver->getScreenSize();
\r
2263 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2264 //bool screensize_changed = screensize != last_screensize;
\r
2266 // Hilight boxes collected during the loop and displayed
\r
2267 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2270 std::wstring infotext;
\r
2272 // When screen size changes, update positions and sizes of stuff
\r
2273 /*if(screensize_changed)
\r
2275 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2276 quick_inventory->updatePosition(pos);
\r
2279 //TimeTaker //timer1("//timer1");
\r
2281 // Time of frame without fps limit
\r
2285 // not using getRealTime is necessary for wine
\r
2286 u32 time = device->getTimer()->getTime();
\r
2287 if(time > lasttime)
\r
2288 busytime_u32 = time - lasttime;
\r
2291 busytime = busytime_u32 / 1000.0;
\r
2294 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2296 // Absolutelu necessary for wine!
\r
2303 updateViewingRange(busytime, &client);
\r
2310 float fps_max = g_settings.getFloat("fps_max");
\r
2311 u32 frametime_min = 1000./fps_max;
\r
2313 if(busytime_u32 < frametime_min)
\r
2315 u32 sleeptime = frametime_min - busytime_u32;
\r
2316 device->sleep(sleeptime);
\r
2320 // Absolutelu necessary for wine!
\r
2324 Time difference calculation
\r
2326 f32 dtime; // in seconds
\r
2328 u32 time = device->getTimer()->getTime();
\r
2329 if(time > lasttime)
\r
2330 dtime = (time - lasttime) / 1000.0;
\r
2336 Log frametime for visualization
\r
2338 frametime_log.push_back(dtime);
\r
2339 if(frametime_log.size() > 100)
\r
2341 core::list<float>::Iterator i = frametime_log.begin();
\r
2342 frametime_log.erase(i);
\r
2346 Visualize frametime in terminal
\r
2348 /*for(u32 i=0; i<dtime*400; i++)
\r
2350 std::cout<<std::endl;*/
\r
2353 Time average and jitter calculation
\r
2356 static f32 dtime_avg1 = 0.0;
\r
2357 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2358 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2360 static f32 dtime_jitter1_max_sample = 0.0;
\r
2361 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2363 static f32 jitter1_max = 0.0;
\r
2364 static f32 counter = 0.0;
\r
2365 if(dtime_jitter1 > jitter1_max)
\r
2366 jitter1_max = dtime_jitter1;
\r
2371 dtime_jitter1_max_sample = jitter1_max;
\r
2372 dtime_jitter1_max_fraction
\r
2373 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2374 jitter1_max = 0.0;
\r
2379 Busytime average and jitter calculation
\r
2382 static f32 busytime_avg1 = 0.0;
\r
2383 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2384 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2386 static f32 busytime_jitter1_max_sample = 0.0;
\r
2387 static f32 busytime_jitter1_min_sample = 0.0;
\r
2389 static f32 jitter1_max = 0.0;
\r
2390 static f32 jitter1_min = 0.0;
\r
2391 static f32 counter = 0.0;
\r
2392 if(busytime_jitter1 > jitter1_max)
\r
2393 jitter1_max = busytime_jitter1;
\r
2394 if(busytime_jitter1 < jitter1_min)
\r
2395 jitter1_min = busytime_jitter1;
\r
2397 if(counter > 0.0){
\r
2399 busytime_jitter1_max_sample = jitter1_max;
\r
2400 busytime_jitter1_min_sample = jitter1_min;
\r
2401 jitter1_max = 0.0;
\r
2402 jitter1_min = 0.0;
\r
2407 Debug info for client
\r
2410 static float counter = 0.0;
\r
2415 client.printDebugInfo(std::cout);
\r
2420 Input handler step()
\r
2422 g_input->step(dtime);
\r
2429 Player speed control
\r
2438 bool a_superspeed,
\r
2442 PlayerControl control(
\r
2443 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2444 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2445 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2446 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2447 g_input->isKeyDown(irr::KEY_SPACE),
\r
2448 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2449 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2450 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2454 client.setPlayerControl(control);
\r
2458 Process environment
\r
2462 //TimeTaker timer("client.step(dtime)");
\r
2463 client.step(dtime);
\r
2464 //client.step(dtime_avg1);
\r
2467 if(server != NULL)
\r
2469 //TimeTaker timer("server->step(dtime)");
\r
2470 server->step(dtime);
\r
2473 v3f player_position = client.getPlayerPosition();
\r
2475 //TimeTaker //timer2("//timer2");
\r
2478 Mouse and camera control
\r
2481 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2484 device->getCursorControl()->setVisible(false);
\r
2486 if(first_loop_after_window_activation){
\r
2487 //std::cout<<"window active, first loop"<<std::endl;
\r
2488 first_loop_after_window_activation = false;
\r
2491 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2492 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2493 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2494 camera_yaw -= dx*0.2;
\r
2495 camera_pitch += dy*0.2;
\r
2496 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2497 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2499 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2502 device->getCursorControl()->setVisible(true);
\r
2504 //std::cout<<"window inactive"<<std::endl;
\r
2505 first_loop_after_window_activation = true;
\r
2508 camera_yaw = wrapDegrees(camera_yaw);
\r
2509 camera_pitch = wrapDegrees(camera_pitch);
\r
2511 v3f camera_direction = v3f(0,0,1);
\r
2512 camera_direction.rotateYZBy(camera_pitch);
\r
2513 camera_direction.rotateXZBy(camera_yaw);
\r
2515 // This is at the height of the eyes of the current figure
\r
2516 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2517 // This is more like in minecraft
\r
2518 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2520 camera->setPosition(camera_position);
\r
2521 // *100.0 helps in large map coordinates
\r
2522 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2524 if(FIELD_OF_VIEW_TEST){
\r
2525 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2528 //TimeTaker timer("client.updateCamera");
\r
2529 client.updateCamera(camera_position, camera_direction);
\r
2533 //TimeTaker //timer3("//timer3");
\r
2536 Calculate what block is the crosshair pointing to
\r
2539 //u32 t1 = device->getTimer()->getRealTime();
\r
2541 //f32 d = 4; // max. distance
\r
2542 f32 d = 4; // max. distance
\r
2543 core::line3d<f32> shootline(camera_position,
\r
2544 camera_position + camera_direction * BS * (d+1));
\r
2546 MapBlockObject *selected_object = client.getSelectedObject
\r
2547 (d*BS, camera_position, shootline);
\r
2550 If it's pointing to a MapBlockObject
\r
2553 if(selected_object != NULL)
\r
2555 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2557 core::aabbox3d<f32> box_on_map
\r
2558 = selected_object->getSelectionBoxOnMap();
\r
2560 hilightboxes.push_back(box_on_map);
\r
2562 infotext = narrow_to_wide(selected_object->infoText());
\r
2564 if(g_input->getLeftClicked())
\r
2566 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2567 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2568 selected_object->getId(), g_selected_item);
\r
2570 else if(g_input->getRightClicked())
\r
2572 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2574 Check if we want to modify the object ourselves
\r
2576 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2578 dstream<<"Sign object right-clicked"<<std::endl;
\r
2580 if(random_input == false)
\r
2582 // Get a new text for it
\r
2584 TextDest *dest = new TextDestSign(
\r
2585 selected_object->getBlock()->getPos(),
\r
2586 selected_object->getId(),
\r
2589 SignObject *sign_object = (SignObject*)selected_object;
\r
2591 std::wstring wtext =
\r
2592 narrow_to_wide(sign_object->getText());
\r
2594 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2600 Otherwise pass the event to the server as-is
\r
2604 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2605 selected_object->getId(), g_selected_item);
\r
2609 else // selected_object == NULL
\r
2613 Find out which node we are pointing at
\r
2616 bool nodefound = false;
\r
2618 v3s16 neighbourpos;
\r
2619 core::aabbox3d<f32> nodehilightbox;
\r
2620 f32 mindistance = BS * 1001;
\r
2622 v3s16 pos_i = floatToInt(player_position, BS);
\r
2624 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2628 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2629 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2630 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2631 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2632 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2633 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2635 for(s16 y = ystart; y <= yend; y++)
\r
2636 for(s16 z = zstart; z <= zend; z++)
\r
2637 for(s16 x = xstart; x <= xend; x++)
\r
2642 n = client.getNode(v3s16(x,y,z));
\r
2643 if(content_pointable(n.d) == false)
\r
2646 catch(InvalidPositionException &e)
\r
2652 v3f npf = intToFloat(np, BS);
\r
2657 v3s16(0,0,1), // back
\r
2658 v3s16(0,1,0), // top
\r
2659 v3s16(1,0,0), // right
\r
2660 v3s16(0,0,-1), // front
\r
2661 v3s16(0,-1,0), // bottom
\r
2662 v3s16(-1,0,0), // left
\r
2668 if(n.d == CONTENT_TORCH)
\r
2670 v3s16 dir = unpackDir(n.dir);
\r
2671 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2672 dir_f *= BS/2 - BS/6 - BS/20;
\r
2673 v3f cpf = npf + dir_f;
\r
2674 f32 distance = (cpf - camera_position).getLength();
\r
2676 core::aabbox3d<f32> box;
\r
2679 if(dir == v3s16(0,-1,0))
\r
2681 box = core::aabbox3d<f32>(
\r
2682 npf - v3f(BS/6, BS/2, BS/6),
\r
2683 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2687 else if(dir == v3s16(0,1,0))
\r
2689 box = core::aabbox3d<f32>(
\r
2690 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2691 npf + v3f(BS/6, BS/2, BS/6)
\r
2697 box = core::aabbox3d<f32>(
\r
2698 cpf - v3f(BS/6, BS/3, BS/6),
\r
2699 cpf + v3f(BS/6, BS/3, BS/6)
\r
2703 if(distance < mindistance)
\r
2705 if(box.intersectsWithLine(shootline))
\r
2709 neighbourpos = np;
\r
2710 mindistance = distance;
\r
2711 nodehilightbox = box;
\r
2720 for(u16 i=0; i<6; i++)
\r
2722 v3f dir_f = v3f(dirs[i].X,
\r
2723 dirs[i].Y, dirs[i].Z);
\r
2724 v3f centerpoint = npf + dir_f * BS/2;
\r
2726 (centerpoint - camera_position).getLength();
\r
2728 if(distance < mindistance)
\r
2730 core::CMatrix4<f32> m;
\r
2731 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2733 // This is the back face
\r
2734 v3f corners[2] = {
\r
2735 v3f(BS/2, BS/2, BS/2),
\r
2736 v3f(-BS/2, -BS/2, BS/2+d)
\r
2739 for(u16 j=0; j<2; j++)
\r
2741 m.rotateVect(corners[j]);
\r
2742 corners[j] += npf;
\r
2745 core::aabbox3d<f32> facebox(corners[0]);
\r
2746 facebox.addInternalPoint(corners[1]);
\r
2748 if(facebox.intersectsWithLine(shootline))
\r
2752 neighbourpos = np + dirs[i];
\r
2753 mindistance = distance;
\r
2755 //nodehilightbox = facebox;
\r
2757 const float d = 0.502;
\r
2758 core::aabbox3d<f32> nodebox
\r
2759 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2760 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2761 nodebox.MinEdge += nodepos_f;
\r
2762 nodebox.MaxEdge += nodepos_f;
\r
2763 nodehilightbox = nodebox;
\r
2765 } // if distance < mindistance
\r
2767 } // regular block
\r
2770 static float nodig_delay_counter = 0.0;
\r
2774 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2776 static float dig_time = 0.0;
\r
2777 static u16 dig_index = 0;
\r
2779 // Visualize selection
\r
2781 hilightboxes.push_back(nodehilightbox);
\r
2785 if(g_input->getLeftReleased())
\r
2787 client.clearTempMod(nodepos);
\r
2791 if(nodig_delay_counter > 0.0)
\r
2793 nodig_delay_counter -= dtime;
\r
2797 if(nodepos != nodepos_old)
\r
2799 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2800 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2802 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2804 client.clearTempMod(nodepos_old);
\r
2809 if(g_input->getLeftClicked() ||
\r
2810 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2812 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2813 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2815 if(g_input->getLeftClicked())
\r
2817 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2819 if(g_input->getLeftState())
\r
2821 MapNode n = client.getNode(nodepos);
\r
2823 // Get tool name. Default is "" = bare hands
\r
2824 std::string toolname = "";
\r
2825 InventoryList *mlist = local_inventory.getList("main");
\r
2828 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2829 if(item && (std::string)item->getName() == "ToolItem")
\r
2831 ToolItem *titem = (ToolItem*)item;
\r
2832 toolname = titem->getToolName();
\r
2836 // Get digging properties for material and tool
\r
2837 u8 material = n.d;
\r
2838 DiggingProperties prop =
\r
2839 getDiggingProperties(material, toolname);
\r
2841 float dig_time_complete = 0.0;
\r
2843 if(prop.diggable == false)
\r
2845 /*dstream<<"Material "<<(int)material
\r
2846 <<" not diggable with \""
\r
2847 <<toolname<<"\""<<std::endl;*/
\r
2848 // I guess nobody will wait for this long
\r
2849 dig_time_complete = 10000000.0;
\r
2853 dig_time_complete = prop.time;
\r
2856 if(dig_time_complete >= 0.001)
\r
2858 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2859 * dig_time/dig_time_complete);
\r
2861 // This is for torches
\r
2864 dig_index = CRACK_ANIMATION_LENGTH;
\r
2867 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2869 //TimeTaker timer("client.setTempMod");
\r
2870 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2871 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2875 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2876 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2877 client.clearTempMod(nodepos);
\r
2878 client.removeNode(nodepos);
\r
2882 nodig_delay_counter = dig_time_complete
\r
2883 / (float)CRACK_ANIMATION_LENGTH;
\r
2885 // We don't want a corresponding delay to
\r
2886 // very time consuming nodes
\r
2887 if(nodig_delay_counter > 0.5)
\r
2889 nodig_delay_counter = 0.5;
\r
2891 // We want a slight delay to very little
\r
2892 // time consuming nodes
\r
2893 float mindelay = 0.15;
\r
2894 if(nodig_delay_counter < mindelay)
\r
2896 nodig_delay_counter = mindelay;
\r
2900 dig_time += dtime;
\r
2904 if(g_input->getRightClicked())
\r
2906 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2907 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2910 nodepos_old = nodepos;
\r
2915 } // selected_object == NULL
\r
2917 g_input->resetLeftClicked();
\r
2918 g_input->resetRightClicked();
\r
2920 if(g_input->getLeftReleased())
\r
2922 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2924 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2926 if(g_input->getRightReleased())
\r
2928 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2932 g_input->resetLeftReleased();
\r
2933 g_input->resetRightReleased();
\r
2936 Calculate stuff for drawing
\r
2939 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2941 u32 daynight_ratio = client.getDayNightRatio();
\r
2942 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2943 video::SColor bgcolor = video::SColor(
\r
2945 skycolor.getRed() * l / 255,
\r
2946 skycolor.getGreen() * l / 255,
\r
2947 skycolor.getBlue() * l / 255);
\r
2953 if(g_settings.getBool("enable_fog") == true)
\r
2955 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2956 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2957 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2958 if(draw_control.range_all)
\r
2959 range = 100000*BS;
\r
2963 video::EFT_FOG_LINEAR,
\r
2967 false, // pixel fog
\r
2968 false // range fog
\r
2975 video::EFT_FOG_LINEAR,
\r
2979 false, // pixel fog
\r
2980 false // range fog
\r
2986 Update gui stuff (0ms)
\r
2989 //TimeTaker guiupdatetimer("Gui updating");
\r
2992 static float drawtime_avg = 0;
\r
2993 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2994 static float beginscenetime_avg = 0;
\r
2995 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2996 static float scenetime_avg = 0;
\r
2997 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2998 static float endscenetime_avg = 0;
\r
2999 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3001 char temptext[300];
\r
3002 snprintf(temptext, 300, "Minetest-c55 ("
\r
3004 ", R: range_all=%i"
\r
3006 " drawtime=%.0f, beginscenetime=%.0f"
\r
3007 ", scenetime=%.0f, endscenetime=%.0f",
\r
3009 draw_control.range_all,
\r
3011 beginscenetime_avg,
\r
3016 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3020 char temptext[300];
\r
3021 snprintf(temptext, 300,
\r
3022 "(% .1f, % .1f, % .1f)"
\r
3023 " (% .3f < btime_jitter < % .3f"
\r
3024 ", dtime_jitter = % .1f %%"
\r
3025 ", v_range = %.1f)",
\r
3026 player_position.X/BS,
\r
3027 player_position.Y/BS,
\r
3028 player_position.Z/BS,
\r
3029 busytime_jitter1_min_sample,
\r
3030 busytime_jitter1_max_sample,
\r
3031 dtime_jitter1_max_fraction * 100.0,
\r
3032 draw_control.wanted_range
\r
3035 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3039 guitext_info->setText(infotext.c_str());
\r
3043 Get chat messages from client
\r
3046 // Get new messages
\r
3047 std::wstring message;
\r
3048 while(client.getChatMessage(message))
\r
3050 chat_lines.push_back(ChatLine(message));
\r
3051 /*if(chat_lines.size() > 6)
\r
3053 core::list<ChatLine>::Iterator
\r
3054 i = chat_lines.begin();
\r
3055 chat_lines.erase(i);
\r
3058 // Append them to form the whole static text and throw
\r
3059 // it to the gui element
\r
3060 std::wstring whole;
\r
3061 // This will correspond to the line number counted from
\r
3062 // top to bottom, from size-1 to 0
\r
3063 s16 line_number = chat_lines.size();
\r
3064 // Count of messages to be removed from the top
\r
3065 u16 to_be_removed_count = 0;
\r
3066 for(core::list<ChatLine>::Iterator
\r
3067 i = chat_lines.begin();
\r
3068 i != chat_lines.end(); i++)
\r
3070 // After this, line number is valid for this loop
\r
3073 (*i).age += dtime;
\r
3075 This results in a maximum age of 60*6 to the
\r
3076 lowermost line and a maximum of 6 lines
\r
3078 float allowed_age = (6-line_number) * 60.0;
\r
3080 if((*i).age > allowed_age)
\r
3082 to_be_removed_count++;
\r
3085 whole += (*i).text + L'\n';
\r
3087 for(u16 i=0; i<to_be_removed_count; i++)
\r
3089 core::list<ChatLine>::Iterator
\r
3090 it = chat_lines.begin();
\r
3091 chat_lines.erase(it);
\r
3093 guitext_chat->setText(whole.c_str());
\r
3095 // Update gui element size and position
\r
3097 /*core::rect<s32> rect(
\r
3099 screensize.Y - guitext_chat_pad_bottom
\r
3100 - text_height*chat_lines.size(),
\r
3101 screensize.X - 10,
\r
3102 screensize.Y - guitext_chat_pad_bottom
\r
3104 core::rect<s32> rect(
\r
3107 screensize.X - 10,
\r
3108 50 + text_height*chat_lines.size()
\r
3111 guitext_chat->setRelativePosition(rect);
\r
3113 if(chat_lines.size() == 0)
\r
3114 guitext_chat->setVisible(false);
\r
3116 guitext_chat->setVisible(true);
\r
3123 static u16 old_selected_item = 65535;
\r
3124 if(client.getLocalInventoryUpdated()
\r
3125 || g_selected_item != old_selected_item)
\r
3127 old_selected_item = g_selected_item;
\r
3128 //std::cout<<"Updating local inventory"<<std::endl;
\r
3129 client.getLocalInventory(local_inventory);
\r
3130 /*quick_inventory->setSelection(g_selected_item);
\r
3131 quick_inventory->update();*/
\r
3135 Send actions returned by the inventory menu
\r
3137 while(inventory_action_queue.size() != 0)
\r
3139 InventoryAction *a = inventory_action_queue.pop_front();
\r
3141 client.sendInventoryAction(a);
\r
3150 TimeTaker drawtimer("Drawing");
\r
3154 TimeTaker timer("beginScene");
\r
3155 driver->beginScene(true, true, bgcolor);
\r
3156 //driver->beginScene(false, true, bgcolor);
\r
3157 beginscenetime = timer.stop(true);
\r
3162 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3165 TimeTaker timer("smgr");
\r
3167 scenetime = timer.stop(true);
\r
3171 //TimeTaker timer9("auxiliary drawings");
\r
3175 //TimeTaker //timer10("//timer10");
\r
3177 video::SMaterial m;
\r
3178 //m.Thickness = 10;
\r
3180 m.Lighting = false;
\r
3181 driver->setMaterial(m);
\r
3183 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3185 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3186 i != hilightboxes.end(); i++)
\r
3188 /*std::cout<<"hilightbox min="
\r
3189 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3191 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3193 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3199 if(g_settings.getBool("frametime_graph") == true)
\r
3202 for(core::list<float>::Iterator
\r
3203 i = frametime_log.begin();
\r
3204 i != frametime_log.end();
\r
3207 driver->draw2DLine(v2s32(x,50),
\r
3208 v2s32(x,50+(*i)*1000),
\r
3209 video::SColor(255,255,255,255));
\r
3217 if(g_show_map_plot && g_map_plot_texture)
\r
3219 core::dimension2d<u32> drawdim(640,480);
\r
3220 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3222 (screensize.X-drawdim.Width)/2,
\r
3223 (screensize.Y-drawdim.Height)/2
\r
3225 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3226 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3232 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3233 displaycenter + core::vector2d<s32>(10,0),
\r
3234 video::SColor(255,255,255,255));
\r
3235 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3236 displaycenter + core::vector2d<s32>(0,10),
\r
3237 video::SColor(255,255,255,255));
\r
3242 //TimeTaker //timer11("//timer11");
\r
3248 guienv->drawAll();
\r
3254 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3255 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3260 TimeTaker timer("endScene");
\r
3261 driver->endScene();
\r
3262 endscenetime = timer.stop(true);
\r
3265 drawtime = drawtimer.stop(true);
\r
3272 Refresh map plot if player has moved considerably
\r
3274 if(g_refresh_map_plot)
\r
3276 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3277 v3f p = client.getPlayerPosition() / BS;
\r
3278 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3280 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3281 old_player_pos = p;
\r
3283 g_refresh_map_plot = false;
\r
3286 static s16 lastFPS = 0;
\r
3287 //u16 fps = driver->getFPS();
\r
3288 u16 fps = (1.0/dtime_avg1);
\r
3290 if (lastFPS != fps)
\r
3292 core::stringw str = L"Minetest [";
\r
3293 str += driver->getName();
\r
3297 device->setWindowCaption(str.c_str());
\r
3303 device->yield();*/
\r
3306 //delete quick_inventory;
\r
3309 Disable texture fetches and other stuff that is queued
\r
3310 to be processed by the main loop.
\r
3312 This has to be done before client goes out of scope.
\r
3314 g_irrlicht->Shutdown(true);
\r
3316 } // client and server are deleted at this point
\r
3319 catch(con::PeerNotFoundException &e)
\r
3321 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3322 error_message = L"Connection timed out.";
\r
3325 } // Menu-game loop
\r
3330 In the end, delete the Irrlicht device.
\r
3335 Update configuration file
\r
3337 /*if(configpath != "")
\r
3339 g_settings.updateConfigFile(configpath.c_str());
\r
3342 END_DEBUG_EXCEPTION_HANDLER
\r
3344 debugstreams_deinit();
\r