LCOV - code coverage report
Current view: top level - src - game.cpp (source / functions) Hit Total Coverage
Test: report Lines: 1059 2095 50.5 %
Date: 2015-07-11 18:23:49 Functions: 72 133 54.1 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include "game.h"
      21             : 
      22             : #include <iomanip>
      23             : #include "camera.h"
      24             : #include "client.h"
      25             : #include "client/tile.h"     // For TextureSource
      26             : #include "clientmap.h"
      27             : #include "clouds.h"
      28             : #include "config.h"
      29             : #include "content_cao.h"
      30             : #include "drawscene.h"
      31             : #include "event_manager.h"
      32             : #include "fontengine.h"
      33             : #include "itemdef.h"
      34             : #include "log.h"
      35             : #include "filesys.h"
      36             : #include "gettext.h"
      37             : #include "guiChatConsole.h"
      38             : #include "guiFormSpecMenu.h"
      39             : #include "guiKeyChangeMenu.h"
      40             : #include "guiPasswordChange.h"
      41             : #include "guiVolumeChange.h"
      42             : #include "hud.h"
      43             : #include "logoutputbuffer.h"
      44             : #include "mainmenumanager.h"
      45             : #include "mapblock.h"
      46             : #include "nodedef.h"         // Needed for determining pointing to nodes
      47             : #include "nodemetadata.h"
      48             : #include "particles.h"
      49             : #include "profiler.h"
      50             : #include "quicktune_shortcutter.h"
      51             : #include "server.h"
      52             : #include "settings.h"
      53             : #include "shader.h"          // For ShaderSource
      54             : #include "sky.h"
      55             : #include "subgame.h"
      56             : #include "tool.h"
      57             : #include "util/directiontables.h"
      58             : #include "util/pointedthing.h"
      59             : #include "version.h"
      60             : #include "minimap.h"
      61             : 
      62             : #include "sound.h"
      63             : 
      64             : #if USE_SOUND
      65             :         #include "sound_openal.h"
      66             : #endif
      67             : 
      68             : #ifdef HAVE_TOUCHSCREENGUI
      69             :         #include "touchscreengui.h"
      70             : #endif
      71             : 
      72             : extern Settings *g_settings;
      73             : extern Profiler *g_profiler;
      74             : 
      75             : /*
      76             :         Text input system
      77             : */
      78             : 
      79           0 : struct TextDestNodeMetadata : public TextDest {
      80           0 :         TextDestNodeMetadata(v3s16 p, Client *client)
      81           0 :         {
      82           0 :                 m_p = p;
      83           0 :                 m_client = client;
      84           0 :         }
      85             :         // This is deprecated I guess? -celeron55
      86           0 :         void gotText(std::wstring text)
      87             :         {
      88           0 :                 std::string ntext = wide_to_narrow(text);
      89           0 :                 infostream << "Submitting 'text' field of node at (" << m_p.X << ","
      90           0 :                            << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
      91           0 :                 StringMap fields;
      92           0 :                 fields["text"] = ntext;
      93           0 :                 m_client->sendNodemetaFields(m_p, "", fields);
      94           0 :         }
      95           0 :         void gotText(const StringMap &fields)
      96             :         {
      97           0 :                 m_client->sendNodemetaFields(m_p, "", fields);
      98           0 :         }
      99             : 
     100             :         v3s16 m_p;
     101             :         Client *m_client;
     102             : };
     103             : 
     104           0 : struct TextDestPlayerInventory : public TextDest {
     105           0 :         TextDestPlayerInventory(Client *client)
     106           0 :         {
     107           0 :                 m_client = client;
     108           0 :                 m_formname = "";
     109           0 :         }
     110           0 :         TextDestPlayerInventory(Client *client, std::string formname)
     111           0 :         {
     112           0 :                 m_client = client;
     113           0 :                 m_formname = formname;
     114           0 :         }
     115           0 :         void gotText(const StringMap &fields)
     116             :         {
     117           0 :                 m_client->sendInventoryFields(m_formname, fields);
     118           0 :         }
     119             : 
     120             :         Client *m_client;
     121             : };
     122             : 
     123           2 : struct LocalFormspecHandler : public TextDest {
     124             :         LocalFormspecHandler();
     125           1 :         LocalFormspecHandler(std::string formname) :
     126           1 :                 m_client(0)
     127             :         {
     128           1 :                 m_formname = formname;
     129           1 :         }
     130             : 
     131           0 :         LocalFormspecHandler(std::string formname, Client *client) :
     132           0 :                 m_client(client)
     133             :         {
     134           0 :                 m_formname = formname;
     135           0 :         }
     136             : 
     137           0 :         void gotText(std::wstring message)
     138             :         {
     139           0 :                 errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
     140           0 :         }
     141             : 
     142           1 :         void gotText(const StringMap &fields)
     143             :         {
     144           1 :                 if (m_formname == "MT_PAUSE_MENU") {
     145           1 :                         if (fields.find("btn_sound") != fields.end()) {
     146           0 :                                 g_gamecallback->changeVolume();
     147           1 :                                 return;
     148             :                         }
     149             : 
     150           1 :                         if (fields.find("btn_key_config") != fields.end()) {
     151           0 :                                 g_gamecallback->keyConfig();
     152           0 :                                 return;
     153             :                         }
     154             : 
     155           1 :                         if (fields.find("btn_exit_menu") != fields.end()) {
     156           0 :                                 g_gamecallback->disconnect();
     157           0 :                                 return;
     158             :                         }
     159             : 
     160           1 :                         if (fields.find("btn_exit_os") != fields.end()) {
     161           1 :                                 g_gamecallback->exitToOS();
     162           1 :                                 return;
     163             :                         }
     164             : 
     165           0 :                         if (fields.find("btn_change_password") != fields.end()) {
     166           0 :                                 g_gamecallback->changePassword();
     167           0 :                                 return;
     168             :                         }
     169             : 
     170           0 :                         if (fields.find("quit") != fields.end()) {
     171           0 :                                 return;
     172             :                         }
     173             : 
     174           0 :                         if (fields.find("btn_continue") != fields.end()) {
     175           0 :                                 return;
     176             :                         }
     177             :                 }
     178             : 
     179           0 :                 if (m_formname == "MT_CHAT_MENU") {
     180             :                         assert(m_client != 0);
     181             : 
     182           0 :                         if ((fields.find("btn_send") != fields.end()) ||
     183           0 :                                         (fields.find("quit") != fields.end())) {
     184           0 :                                 StringMap::const_iterator it = fields.find("f_text");
     185           0 :                                 if (it != fields.end())
     186           0 :                                         m_client->typeChatMessage(narrow_to_wide(it->second));
     187             : 
     188           0 :                                 return;
     189             :                         }
     190             :                 }
     191             : 
     192           0 :                 if (m_formname == "MT_DEATH_SCREEN") {
     193             :                         assert(m_client != 0);
     194             : 
     195           0 :                         if ((fields.find("btn_respawn") != fields.end())) {
     196           0 :                                 m_client->sendRespawn();
     197           0 :                                 return;
     198             :                         }
     199             : 
     200           0 :                         if (fields.find("quit") != fields.end()) {
     201           0 :                                 m_client->sendRespawn();
     202           0 :                                 return;
     203             :                         }
     204             :                 }
     205             : 
     206             :                 // don't show error message for unhandled cursor keys
     207           0 :                 if ((fields.find("key_up") != fields.end()) ||
     208           0 :                                 (fields.find("key_down") != fields.end()) ||
     209           0 :                                 (fields.find("key_left") != fields.end()) ||
     210           0 :                                 (fields.find("key_right") != fields.end())) {
     211           0 :                         return;
     212             :                 }
     213             : 
     214           0 :                 errorstream << "LocalFormspecHandler::gotText unhandled >"
     215           0 :                         << m_formname << "< event" << std::endl;
     216             : 
     217           0 :                 int i = 0;
     218           0 :                 StringMap::const_iterator it;
     219           0 :                 for (it = fields.begin(); it != fields.end(); ++it) {
     220           0 :                         errorstream << "\t" << i << ": " << it->first
     221           0 :                                 << "=" << it->second << std::endl;
     222           0 :                         i++;
     223             :                 }
     224             :         }
     225             : 
     226             :         Client *m_client;
     227             : };
     228             : 
     229             : /* Form update callback */
     230             : 
     231           0 : class NodeMetadataFormSource: public IFormSource
     232             : {
     233             : public:
     234           0 :         NodeMetadataFormSource(ClientMap *map, v3s16 p):
     235             :                 m_map(map),
     236           0 :                 m_p(p)
     237             :         {
     238           0 :         }
     239           0 :         std::string getForm()
     240             :         {
     241           0 :                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
     242             : 
     243           0 :                 if (!meta)
     244           0 :                         return "";
     245             : 
     246           0 :                 return meta->getString("formspec");
     247             :         }
     248           0 :         std::string resolveText(std::string str)
     249             :         {
     250           0 :                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
     251             : 
     252           0 :                 if (!meta)
     253           0 :                         return str;
     254             : 
     255           0 :                 return meta->resolveString(str);
     256             :         }
     257             : 
     258             :         ClientMap *m_map;
     259             :         v3s16 m_p;
     260             : };
     261             : 
     262           0 : class PlayerInventoryFormSource: public IFormSource
     263             : {
     264             : public:
     265           0 :         PlayerInventoryFormSource(Client *client):
     266           0 :                 m_client(client)
     267             :         {
     268           0 :         }
     269           0 :         std::string getForm()
     270             :         {
     271           0 :                 LocalPlayer *player = m_client->getEnv().getLocalPlayer();
     272           0 :                 return player->inventory_formspec;
     273             :         }
     274             : 
     275             :         Client *m_client;
     276             : };
     277             : 
     278             : /*
     279             :         Check if a node is pointable
     280             : */
     281      293652 : inline bool isPointableNode(const MapNode &n,
     282             :                             Client *client, bool liquids_pointable)
     283             : {
     284      293652 :         const ContentFeatures &features = client->getNodeDefManager()->get(n);
     285      483849 :         return features.pointable ||
     286      293652 :                (liquids_pointable && features.isLiquid());
     287             : }
     288             : 
     289             : /*
     290             :         Find what the player is pointing at
     291             : */
     292        1166 : PointedThing getPointedThing(Client *client, v3f player_position,
     293             :                 v3f camera_direction, v3f camera_position, core::line3d<f32> shootline,
     294             :                 f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset,
     295             :                 std::vector<aabb3f> &hilightboxes, ClientActiveObject *&selected_object)
     296             : {
     297        1166 :         PointedThing result;
     298             : 
     299        1166 :         hilightboxes.clear();
     300        1166 :         selected_object = NULL;
     301             : 
     302        1166 :         INodeDefManager *nodedef = client->getNodeDefManager();
     303        1166 :         ClientMap &map = client->getEnv().getClientMap();
     304             : 
     305        1166 :         f32 mindistance = BS * 1001;
     306             : 
     307             :         // First try to find a pointed at active object
     308        1166 :         if (look_for_object) {
     309        2332 :                 selected_object = client->getSelectedActiveObject(d * BS,
     310        1166 :                                   camera_position, shootline);
     311             : 
     312        1166 :                 if (selected_object != NULL) {
     313           0 :                         if (selected_object->doShowSelectionBox()) {
     314           0 :                                 aabb3f *selection_box = selected_object->getSelectionBox();
     315             :                                 // Box should exist because object was
     316             :                                 // returned in the first place
     317             :                                 assert(selection_box);
     318             : 
     319           0 :                                 v3f pos = selected_object->getPosition();
     320           0 :                                 hilightboxes.push_back(aabb3f(
     321           0 :                                                                selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
     322           0 :                                                                selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
     323             :                         }
     324             : 
     325           0 :                         mindistance = (selected_object->getPosition() - camera_position).getLength();
     326             : 
     327           0 :                         result.type = POINTEDTHING_OBJECT;
     328           0 :                         result.object_id = selected_object->getId();
     329             :                 }
     330             :         }
     331             : 
     332             :         // That didn't work, try to find a pointed at node
     333             : 
     334             : 
     335        1166 :         v3s16 pos_i = floatToInt(player_position, BS);
     336             : 
     337             :         /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
     338             :                         <<std::endl;*/
     339             : 
     340        1166 :         s16 a = d;
     341        1166 :         s16 ystart = pos_i.Y + 0 - (camera_direction.Y < 0 ? a : 1);
     342        1166 :         s16 zstart = pos_i.Z - (camera_direction.Z < 0 ? a : 1);
     343        1166 :         s16 xstart = pos_i.X - (camera_direction.X < 0 ? a : 1);
     344        1166 :         s16 yend = pos_i.Y + 1 + (camera_direction.Y > 0 ? a : 1);
     345        1166 :         s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1);
     346        1166 :         s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1);
     347             : 
     348             :         // Prevent signed number overflow
     349        1166 :         if (yend == 32767)
     350           0 :                 yend = 32766;
     351             : 
     352        1166 :         if (zend == 32767)
     353           0 :                 zend = 32766;
     354             : 
     355        1166 :         if (xend == 32767)
     356           0 :                 xend = 32766;
     357             : 
     358        9325 :         for (s16 y = ystart; y <= yend; y++)
     359       57113 :                 for (s16 z = zstart; z <= zend; z++)
     360      342606 :                         for (s16 x = xstart; x <= xend; x++) {
     361      293652 :                                 MapNode n;
     362             :                                 bool is_valid_position;
     363             : 
     364      293652 :                                 n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position);
     365      293652 :                                 if (!is_valid_position)
     366      103455 :                                         continue;
     367             : 
     368      293652 :                                 if (!isPointableNode(n, client, liquids_pointable))
     369      103455 :                                         continue;
     370             : 
     371      380394 :                                 std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
     372             : 
     373      190197 :                                 v3s16 np(x, y, z);
     374      190197 :                                 v3f npf = intToFloat(np, BS);
     375             : 
     376      950985 :                                 for (std::vector<aabb3f>::const_iterator
     377      190197 :                                                 i = boxes.begin();
     378      760788 :                                                 i != boxes.end(); i++) {
     379      190197 :                                         aabb3f box = *i;
     380      190197 :                                         box.MinEdge += npf;
     381      190197 :                                         box.MaxEdge += npf;
     382             : 
     383     1331379 :                                         for (u16 j = 0; j < 6; j++) {
     384     1141182 :                                                 v3s16 facedir = g_6dirs[j];
     385     1141182 :                                                 aabb3f facebox = box;
     386             : 
     387     1141182 :                                                 f32 d = 0.001 * BS;
     388             : 
     389     1141182 :                                                 if (facedir.X > 0)
     390      190197 :                                                         facebox.MinEdge.X = facebox.MaxEdge.X - d;
     391      950985 :                                                 else if (facedir.X < 0)
     392      190197 :                                                         facebox.MaxEdge.X = facebox.MinEdge.X + d;
     393      760788 :                                                 else if (facedir.Y > 0)
     394      190197 :                                                         facebox.MinEdge.Y = facebox.MaxEdge.Y - d;
     395      570591 :                                                 else if (facedir.Y < 0)
     396      190197 :                                                         facebox.MaxEdge.Y = facebox.MinEdge.Y + d;
     397      380394 :                                                 else if (facedir.Z > 0)
     398      190197 :                                                         facebox.MinEdge.Z = facebox.MaxEdge.Z - d;
     399      190197 :                                                 else if (facedir.Z < 0)
     400      190197 :                                                         facebox.MaxEdge.Z = facebox.MinEdge.Z + d;
     401             : 
     402     1141182 :                                                 v3f centerpoint = facebox.getCenter();
     403     1141182 :                                                 f32 distance = (centerpoint - camera_position).getLength();
     404             : 
     405     1141182 :                                                 if (distance >= mindistance)
     406     1157318 :                                                         continue;
     407             : 
     408     1124571 :                                                 if (!facebox.intersectsWithLine(shootline))
     409     1124096 :                                                         continue;
     410             : 
     411         475 :                                                 v3s16 np_above = np + facedir;
     412             : 
     413         475 :                                                 result.type = POINTEDTHING_NODE;
     414         475 :                                                 result.node_undersurface = np;
     415         475 :                                                 result.node_abovesurface = np_above;
     416         475 :                                                 mindistance = distance;
     417             : 
     418         475 :                                                 hilightboxes.clear();
     419             : 
     420         475 :                                                 if (!g_settings->getBool("enable_node_highlighting")) {
     421           0 :                                                         for (std::vector<aabb3f>::const_iterator
     422           0 :                                                                         i2 = boxes.begin();
     423           0 :                                                                         i2 != boxes.end(); i2++) {
     424           0 :                                                                 aabb3f box = *i2;
     425           0 :                                                                 box.MinEdge += npf + v3f(-d, -d, -d) - intToFloat(camera_offset, BS);
     426           0 :                                                                 box.MaxEdge += npf + v3f(d, d, d) - intToFloat(camera_offset, BS);
     427           0 :                                                                 hilightboxes.push_back(box);
     428             :                                                         }
     429             :                                                 }
     430             :                                         }
     431             :                                 }
     432             :                         } // for coords
     433             : 
     434        1166 :         return result;
     435             : }
     436             : 
     437             : /* Profiler display */
     438             : 
     439           8 : void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
     440             :                 u32 show_profiler, u32 show_profiler_max, s32 screen_height)
     441             : {
     442           8 :         if (show_profiler == 0) {
     443           8 :                 guitext_profiler->setVisible(false);
     444             :         } else {
     445             : 
     446           0 :                 std::ostringstream os(std::ios_base::binary);
     447           0 :                 g_profiler->printPage(os, show_profiler, show_profiler_max);
     448           0 :                 std::wstring text = narrow_to_wide(os.str());
     449           0 :                 guitext_profiler->setText(text.c_str());
     450           0 :                 guitext_profiler->setVisible(true);
     451             : 
     452           0 :                 s32 w = fe->getTextWidth(text.c_str());
     453             : 
     454           0 :                 if (w < 400)
     455           0 :                         w = 400;
     456             : 
     457           0 :                 unsigned text_height = fe->getTextHeight();
     458             : 
     459           0 :                 core::position2di upper_left, lower_right;
     460             : 
     461           0 :                 upper_left.X  = 6;
     462           0 :                 upper_left.Y  = (text_height + 5) * 2;
     463           0 :                 lower_right.X = 12 + w;
     464           0 :                 lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS;
     465             : 
     466           0 :                 if (lower_right.Y > screen_height * 2 / 3)
     467           0 :                         lower_right.Y = screen_height * 2 / 3;
     468             : 
     469           0 :                 core::rect<s32> rect(upper_left, lower_right);
     470             : 
     471           0 :                 guitext_profiler->setRelativePosition(rect);
     472           0 :                 guitext_profiler->setVisible(true);
     473             :         }
     474           8 : }
     475             : 
     476           1 : class ProfilerGraph
     477             : {
     478             : private:
     479      198374 :         struct Piece {
     480             :                 Profiler::GraphValues values;
     481             :         };
     482             :         struct Meta {
     483             :                 float min;
     484             :                 float max;
     485             :                 video::SColor color;
     486           0 :                 Meta(float initial = 0,
     487             :                         video::SColor color = video::SColor(255, 255, 255, 255)):
     488             :                         min(initial),
     489             :                         max(initial),
     490           0 :                         color(color)
     491           0 :                 {}
     492             :         };
     493             :         std::vector<Piece> m_log;
     494             : public:
     495             :         u32 m_log_max_size;
     496             : 
     497           1 :         ProfilerGraph():
     498           1 :                 m_log_max_size(200)
     499           1 :         {}
     500             : 
     501        1166 :         void put(const Profiler::GraphValues &values)
     502             :         {
     503        2332 :                 Piece piece;
     504        1166 :                 piece.values = values;
     505        1166 :                 m_log.push_back(piece);
     506             : 
     507        3098 :                 while (m_log.size() > m_log_max_size)
     508         966 :                         m_log.erase(m_log.begin());
     509        1166 :         }
     510             : 
     511           0 :         void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
     512             :                   gui::IGUIFont *font) const
     513             :         {
     514           0 :                 std::map<std::string, Meta> m_meta;
     515             : 
     516           0 :                 for (std::vector<Piece>::const_iterator k = m_log.begin();
     517           0 :                                 k != m_log.end(); k++) {
     518           0 :                         const Piece &piece = *k;
     519             : 
     520           0 :                         for (Profiler::GraphValues::const_iterator i = piece.values.begin();
     521           0 :                                         i != piece.values.end(); i++) {
     522           0 :                                 const std::string &id = i->first;
     523           0 :                                 const float &value = i->second;
     524             :                                 std::map<std::string, Meta>::iterator j =
     525           0 :                                         m_meta.find(id);
     526             : 
     527           0 :                                 if (j == m_meta.end()) {
     528           0 :                                         m_meta[id] = Meta(value);
     529           0 :                                         continue;
     530             :                                 }
     531             : 
     532           0 :                                 if (value < j->second.min)
     533           0 :                                         j->second.min = value;
     534             : 
     535           0 :                                 if (value > j->second.max)
     536           0 :                                         j->second.max = value;
     537             :                         }
     538             :                 }
     539             : 
     540             :                 // Assign colors
     541             :                 static const video::SColor usable_colors[] = {
     542             :                         video::SColor(255, 255, 100, 100),
     543             :                         video::SColor(255, 90, 225, 90),
     544             :                         video::SColor(255, 100, 100, 255),
     545             :                         video::SColor(255, 255, 150, 50),
     546             :                         video::SColor(255, 220, 220, 100)
     547           0 :                 };
     548             :                 static const u32 usable_colors_count =
     549             :                         sizeof(usable_colors) / sizeof(*usable_colors);
     550           0 :                 u32 next_color_i = 0;
     551             : 
     552           0 :                 for (std::map<std::string, Meta>::iterator i = m_meta.begin();
     553           0 :                                 i != m_meta.end(); i++) {
     554           0 :                         Meta &meta = i->second;
     555           0 :                         video::SColor color(255, 200, 200, 200);
     556             : 
     557           0 :                         if (next_color_i < usable_colors_count)
     558           0 :                                 color = usable_colors[next_color_i++];
     559             : 
     560           0 :                         meta.color = color;
     561             :                 }
     562             : 
     563           0 :                 s32 graphh = 50;
     564           0 :                 s32 textx = x_left + m_log_max_size + 15;
     565           0 :                 s32 textx2 = textx + 200 - 15;
     566           0 :                 s32 meta_i = 0;
     567             : 
     568           0 :                 for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
     569           0 :                                 i != m_meta.end(); i++) {
     570           0 :                         const std::string &id = i->first;
     571           0 :                         const Meta &meta = i->second;
     572           0 :                         s32 x = x_left;
     573           0 :                         s32 y = y_bottom - meta_i * 50;
     574           0 :                         float show_min = meta.min;
     575           0 :                         float show_max = meta.max;
     576             : 
     577           0 :                         if (show_min >= -0.0001 && show_max >= -0.0001) {
     578           0 :                                 if (show_min <= show_max * 0.5)
     579           0 :                                         show_min = 0;
     580             :                         }
     581             : 
     582           0 :                         s32 texth = 15;
     583             :                         char buf[10];
     584           0 :                         snprintf(buf, 10, "%.3g", show_max);
     585           0 :                         font->draw(narrow_to_wide(buf).c_str(),
     586             :                                         core::rect<s32>(textx, y - graphh,
     587           0 :                                                    textx2, y - graphh + texth),
     588           0 :                                         meta.color);
     589           0 :                         snprintf(buf, 10, "%.3g", show_min);
     590           0 :                         font->draw(narrow_to_wide(buf).c_str(),
     591             :                                         core::rect<s32>(textx, y - texth,
     592             :                                                    textx2, y),
     593           0 :                                         meta.color);
     594           0 :                         font->draw(narrow_to_wide(id).c_str(),
     595           0 :                                         core::rect<s32>(textx, y - graphh / 2 - texth / 2,
     596           0 :                                                    textx2, y - graphh / 2 + texth / 2),
     597           0 :                                         meta.color);
     598           0 :                         s32 graph1y = y;
     599           0 :                         s32 graph1h = graphh;
     600           0 :                         bool relativegraph = (show_min != 0 && show_min != show_max);
     601           0 :                         float lastscaledvalue = 0.0;
     602           0 :                         bool lastscaledvalue_exists = false;
     603             : 
     604           0 :                         for (std::vector<Piece>::const_iterator j = m_log.begin();
     605           0 :                                         j != m_log.end(); j++) {
     606           0 :                                 const Piece &piece = *j;
     607           0 :                                 float value = 0;
     608           0 :                                 bool value_exists = false;
     609             :                                 Profiler::GraphValues::const_iterator k =
     610           0 :                                         piece.values.find(id);
     611             : 
     612           0 :                                 if (k != piece.values.end()) {
     613           0 :                                         value = k->second;
     614           0 :                                         value_exists = true;
     615             :                                 }
     616             : 
     617           0 :                                 if (!value_exists) {
     618           0 :                                         x++;
     619           0 :                                         lastscaledvalue_exists = false;
     620           0 :                                         continue;
     621             :                                 }
     622             : 
     623           0 :                                 float scaledvalue = 1.0;
     624             : 
     625           0 :                                 if (show_max != show_min)
     626           0 :                                         scaledvalue = (value - show_min) / (show_max - show_min);
     627             : 
     628           0 :                                 if (scaledvalue == 1.0 && value == 0) {
     629           0 :                                         x++;
     630           0 :                                         lastscaledvalue_exists = false;
     631           0 :                                         continue;
     632             :                                 }
     633             : 
     634           0 :                                 if (relativegraph) {
     635           0 :                                         if (lastscaledvalue_exists) {
     636           0 :                                                 s32 ivalue1 = lastscaledvalue * graph1h;
     637           0 :                                                 s32 ivalue2 = scaledvalue * graph1h;
     638           0 :                                                 driver->draw2DLine(v2s32(x - 1, graph1y - ivalue1),
     639           0 :                                                                    v2s32(x, graph1y - ivalue2), meta.color);
     640             :                                         }
     641             : 
     642           0 :                                         lastscaledvalue = scaledvalue;
     643           0 :                                         lastscaledvalue_exists = true;
     644             :                                 } else {
     645           0 :                                         s32 ivalue = scaledvalue * graph1h;
     646           0 :                                         driver->draw2DLine(v2s32(x, graph1y),
     647           0 :                                                            v2s32(x, graph1y - ivalue), meta.color);
     648             :                                 }
     649             : 
     650           0 :                                 x++;
     651             :                         }
     652             : 
     653           0 :                         meta_i++;
     654             :                 }
     655           0 :         }
     656             : };
     657             : 
     658           0 : class NodeDugEvent: public MtEvent
     659             : {
     660             : public:
     661             :         v3s16 p;
     662             :         MapNode n;
     663             : 
     664           0 :         NodeDugEvent(v3s16 p, MapNode n):
     665             :                 p(p),
     666           0 :                 n(n)
     667           0 :         {}
     668           0 :         const char *getType() const
     669             :         {
     670           0 :                 return "NodeDug";
     671             :         }
     672             : };
     673             : 
     674           1 : class SoundMaker
     675             : {
     676             :         ISoundManager *m_sound;
     677             :         INodeDefManager *m_ndef;
     678             : public:
     679             :         float m_player_step_timer;
     680             : 
     681             :         SimpleSoundSpec m_player_step_sound;
     682             :         SimpleSoundSpec m_player_leftpunch_sound;
     683             :         SimpleSoundSpec m_player_rightpunch_sound;
     684             : 
     685           1 :         SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
     686             :                 m_sound(sound),
     687             :                 m_ndef(ndef),
     688           1 :                 m_player_step_timer(0)
     689             :         {
     690           1 :         }
     691             : 
     692          38 :         void playPlayerStep()
     693             :         {
     694          38 :                 if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
     695          37 :                         m_player_step_timer = 0.03;
     696          37 :                         m_sound->playSound(m_player_step_sound, false);
     697             :                 }
     698          38 :         }
     699             : 
     700          37 :         static void viewBobbingStep(MtEvent *e, void *data)
     701             :         {
     702          37 :                 SoundMaker *sm = (SoundMaker *)data;
     703          37 :                 sm->playPlayerStep();
     704          37 :         }
     705             : 
     706           1 :         static void playerRegainGround(MtEvent *e, void *data)
     707             :         {
     708           1 :                 SoundMaker *sm = (SoundMaker *)data;
     709           1 :                 sm->playPlayerStep();
     710           1 :         }
     711             : 
     712           0 :         static void playerJump(MtEvent *e, void *data)
     713             :         {
     714             :                 //SoundMaker *sm = (SoundMaker*)data;
     715           0 :         }
     716             : 
     717           0 :         static void cameraPunchLeft(MtEvent *e, void *data)
     718             :         {
     719           0 :                 SoundMaker *sm = (SoundMaker *)data;
     720           0 :                 sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
     721           0 :         }
     722             : 
     723           0 :         static void cameraPunchRight(MtEvent *e, void *data)
     724             :         {
     725           0 :                 SoundMaker *sm = (SoundMaker *)data;
     726           0 :                 sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
     727           0 :         }
     728             : 
     729           0 :         static void nodeDug(MtEvent *e, void *data)
     730             :         {
     731           0 :                 SoundMaker *sm = (SoundMaker *)data;
     732           0 :                 NodeDugEvent *nde = (NodeDugEvent *)e;
     733           0 :                 sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
     734           0 :         }
     735             : 
     736           0 :         static void playerDamage(MtEvent *e, void *data)
     737             :         {
     738           0 :                 SoundMaker *sm = (SoundMaker *)data;
     739           0 :                 sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
     740           0 :         }
     741             : 
     742           0 :         static void playerFallingDamage(MtEvent *e, void *data)
     743             :         {
     744           0 :                 SoundMaker *sm = (SoundMaker *)data;
     745           0 :                 sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
     746           0 :         }
     747             : 
     748           1 :         void registerReceiver(MtEventManager *mgr)
     749             :         {
     750           1 :                 mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
     751           1 :                 mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
     752           1 :                 mgr->reg("PlayerJump", SoundMaker::playerJump, this);
     753           1 :                 mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
     754           1 :                 mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
     755           1 :                 mgr->reg("NodeDug", SoundMaker::nodeDug, this);
     756           1 :                 mgr->reg("PlayerDamage", SoundMaker::playerDamage, this);
     757           1 :                 mgr->reg("PlayerFallingDamage", SoundMaker::playerFallingDamage, this);
     758           1 :         }
     759             : 
     760        1166 :         void step(float dtime)
     761             :         {
     762        1166 :                 m_player_step_timer -= dtime;
     763        1166 :         }
     764             : };
     765             : 
     766             : // Locally stored sounds don't need to be preloaded because of this
     767           2 : class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
     768             : {
     769             :         std::set<std::string> m_fetched;
     770             : public:
     771           0 :         void fetchSounds(const std::string &name,
     772             :                         std::set<std::string> &dst_paths,
     773             :                         std::set<std::string> &dst_datas)
     774             :         {
     775           0 :                 if (m_fetched.count(name))
     776           0 :                         return;
     777             : 
     778           0 :                 m_fetched.insert(name);
     779           0 :                 std::string base = porting::path_share + DIR_DELIM + "testsounds";
     780           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".ogg");
     781           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
     782           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
     783           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
     784           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
     785           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
     786           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
     787           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
     788           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
     789           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
     790           0 :                 dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
     791             :         }
     792             : };
     793             : 
     794             : class GameGlobalShaderConstantSetter : public IShaderConstantSetter
     795             : {
     796             :         Sky *m_sky;
     797             :         bool *m_force_fog_off;
     798             :         f32 *m_fog_range;
     799             :         Client *m_client;
     800             :         bool m_fogEnabled;
     801             : 
     802             : public:
     803           0 :         void onSettingsChange(const std::string &name)
     804             :         {
     805           0 :                 if (name == "enable_fog")
     806           0 :                         m_fogEnabled = g_settings->getBool("enable_fog");
     807           0 :         }
     808             : 
     809           0 :         static void SettingsCallback(const std::string name, void *userdata)
     810             :         {
     811           0 :                 reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
     812           0 :         }
     813             : 
     814           1 :         GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
     815             :                         f32 *fog_range, Client *client) :
     816             :                 m_sky(sky),
     817             :                 m_force_fog_off(force_fog_off),
     818             :                 m_fog_range(fog_range),
     819           1 :                 m_client(client)
     820             :         {
     821           1 :                 g_settings->registerChangedCallback("enable_fog", SettingsCallback, this);
     822           1 :                 m_fogEnabled = g_settings->getBool("enable_fog");
     823           1 :         }
     824             : 
     825           2 :         ~GameGlobalShaderConstantSetter()
     826           2 :         {
     827           1 :                 g_settings->deregisterChangedCallback("enable_fog", SettingsCallback, this);
     828           2 :         }
     829             : 
     830     1426078 :         virtual void onSetConstants(video::IMaterialRendererServices *services,
     831             :                         bool is_highlevel)
     832             :         {
     833     1426078 :                 if (!is_highlevel)
     834           0 :                         return;
     835             : 
     836             :                 // Background color
     837     1426078 :                 video::SColor bgcolor = m_sky->getBgColor();
     838     1426078 :                 video::SColorf bgcolorf(bgcolor);
     839             :                 float bgcolorfa[4] = {
     840     1426078 :                         bgcolorf.r,
     841     1426078 :                         bgcolorf.g,
     842     1426078 :                         bgcolorf.b,
     843     1426078 :                         bgcolorf.a,
     844     5704312 :                 };
     845     1426078 :                 services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
     846             : 
     847             :                 // Fog distance
     848     1426078 :                 float fog_distance = 10000 * BS;
     849             : 
     850     1426078 :                 if (m_fogEnabled && !*m_force_fog_off)
     851     1426078 :                         fog_distance = *m_fog_range;
     852             : 
     853     1426078 :                 services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
     854             : 
     855             :                 // Day-night ratio
     856     1426078 :                 u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
     857     1426078 :                 float daynight_ratio_f = (float)daynight_ratio / 1000.0;
     858     1426078 :                 services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
     859             : 
     860     1426078 :                 u32 animation_timer = porting::getTimeMs() % 100000;
     861     1426078 :                 float animation_timer_f = (float)animation_timer / 100000.0;
     862     1426078 :                 services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
     863     1426078 :                 services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
     864             : 
     865     1426078 :                 LocalPlayer *player = m_client->getEnv().getLocalPlayer();
     866     1426078 :                 v3f eye_position = player->getEyePosition();
     867     1426078 :                 services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
     868     1426078 :                 services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
     869             : 
     870     1426078 :                 v3f minimap_yaw_vec = m_client->getMapper()->getYawVec();
     871     1426078 :                 services->setPixelShaderConstant("yawVec", (irr::f32 *)&minimap_yaw_vec, 3);
     872             : 
     873             :                 // Uniform sampler layers
     874     1426078 :                 int layer0 = 0;
     875     1426078 :                 int layer1 = 1;
     876     1426078 :                 int layer2 = 2;
     877             :                 // before 1.8 there isn't a "integer interface", only float
     878             : #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
     879             :                 services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1);
     880             :                 services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1);
     881             :                 services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1);
     882             : #else
     883     1426078 :                 services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1);
     884     1426078 :                 services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1);
     885     1426078 :                 services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1);
     886             : #endif
     887             :         }
     888             : };
     889             : 
     890           0 : bool nodePlacementPrediction(Client &client,
     891             :                 const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
     892             : {
     893           0 :         std::string prediction = playeritem_def.node_placement_prediction;
     894           0 :         INodeDefManager *nodedef = client.ndef();
     895           0 :         ClientMap &map = client.getEnv().getClientMap();
     896           0 :         MapNode node;
     897             :         bool is_valid_position;
     898             : 
     899           0 :         node = map.getNodeNoEx(nodepos, &is_valid_position);
     900           0 :         if (!is_valid_position)
     901           0 :                 return false;
     902             : 
     903           0 :         if (prediction != "" && !nodedef->get(node).rightclickable) {
     904           0 :                 verbosestream << "Node placement prediction for "
     905           0 :                               << playeritem_def.name << " is "
     906           0 :                               << prediction << std::endl;
     907           0 :                 v3s16 p = neighbourpos;
     908             : 
     909             :                 // Place inside node itself if buildable_to
     910           0 :                 MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position);
     911           0 :                 if (is_valid_position)
     912             :                 {
     913           0 :                         if (nodedef->get(n_under).buildable_to)
     914           0 :                                 p = nodepos;
     915             :                         else {
     916           0 :                                 node = map.getNodeNoEx(p, &is_valid_position);
     917           0 :                                 if (is_valid_position &&!nodedef->get(node).buildable_to)
     918           0 :                                         return false;
     919             :                         }
     920             :                 }
     921             : 
     922             :                 // Find id of predicted node
     923             :                 content_t id;
     924           0 :                 bool found = nodedef->getId(prediction, id);
     925             : 
     926           0 :                 if (!found) {
     927           0 :                         errorstream << "Node placement prediction failed for "
     928           0 :                                     << playeritem_def.name << " (places "
     929           0 :                                     << prediction
     930           0 :                                     << ") - Name not known" << std::endl;
     931           0 :                         return false;
     932             :                 }
     933             : 
     934             :                 // Predict param2 for facedir and wallmounted nodes
     935           0 :                 u8 param2 = 0;
     936             : 
     937           0 :                 if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) {
     938           0 :                         v3s16 dir = nodepos - neighbourpos;
     939             : 
     940           0 :                         if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
     941           0 :                                 param2 = dir.Y < 0 ? 1 : 0;
     942           0 :                         } else if (abs(dir.X) > abs(dir.Z)) {
     943           0 :                                 param2 = dir.X < 0 ? 3 : 2;
     944             :                         } else {
     945           0 :                                 param2 = dir.Z < 0 ? 5 : 4;
     946             :                         }
     947             :                 }
     948             : 
     949           0 :                 if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) {
     950           0 :                         v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
     951             : 
     952           0 :                         if (abs(dir.X) > abs(dir.Z)) {
     953           0 :                                 param2 = dir.X < 0 ? 3 : 1;
     954             :                         } else {
     955           0 :                                 param2 = dir.Z < 0 ? 2 : 0;
     956             :                         }
     957             :                 }
     958             : 
     959             :                 assert(param2 <= 5);
     960             : 
     961             :                 //Check attachment if node is in group attached_node
     962           0 :                 if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
     963             :                         static v3s16 wallmounted_dirs[8] = {
     964             :                                 v3s16(0, 1, 0),
     965             :                                 v3s16(0, -1, 0),
     966             :                                 v3s16(1, 0, 0),
     967             :                                 v3s16(-1, 0, 0),
     968             :                                 v3s16(0, 0, 1),
     969             :                                 v3s16(0, 0, -1),
     970           0 :                         };
     971           0 :                         v3s16 pp;
     972             : 
     973           0 :                         if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
     974           0 :                                 pp = p + wallmounted_dirs[param2];
     975             :                         else
     976           0 :                                 pp = p + v3s16(0, -1, 0);
     977             : 
     978           0 :                         if (!nodedef->get(map.getNodeNoEx(pp)).walkable)
     979           0 :                                 return false;
     980             :                 }
     981             : 
     982             :                 // Add node to client map
     983           0 :                 MapNode n(id, 0, param2);
     984             : 
     985             :                 try {
     986           0 :                         LocalPlayer *player = client.getEnv().getLocalPlayer();
     987             : 
     988             :                         // Dont place node when player would be inside new node
     989             :                         // NOTE: This is to be eventually implemented by a mod as client-side Lua
     990           0 :                         if (!nodedef->get(n).walkable ||
     991           0 :                                         g_settings->getBool("enable_build_where_you_stand") ||
     992           0 :                                         (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
     993           0 :                                         (nodedef->get(n).walkable &&
     994           0 :                                          neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
     995           0 :                                          neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
     996             : 
     997             :                                 // This triggers the required mesh update too
     998           0 :                                 client.addNode(p, n);
     999           0 :                                 return true;
    1000             :                         }
    1001           0 :                 } catch (InvalidPositionException &e) {
    1002           0 :                         errorstream << "Node placement prediction failed for "
    1003           0 :                                     << playeritem_def.name << " (places "
    1004           0 :                                     << prediction
    1005           0 :                                     << ") - Position not loaded" << std::endl;
    1006             :                 }
    1007             :         }
    1008             : 
    1009           0 :         return false;
    1010             : }
    1011             : 
    1012           1 : static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
    1013             :                 InventoryManager *invmgr, IGameDef *gamedef,
    1014             :                 IWritableTextureSource *tsrc, IrrlichtDevice *device,
    1015             :                 IFormSource *fs_src, TextDest *txt_dest, Client *client)
    1016             : {
    1017             : 
    1018           1 :         if (*cur_formspec == 0) {
    1019             :                 *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
    1020           1 :                                                     invmgr, gamedef, tsrc, fs_src, txt_dest, client);
    1021           1 :                 (*cur_formspec)->doPause = false;
    1022             : 
    1023             :                 /*
    1024             :                         Caution: do not call (*cur_formspec)->drop() here --
    1025             :                         the reference might outlive the menu, so we will
    1026             :                         periodically check if *cur_formspec is the only
    1027             :                         remaining reference (i.e. the menu was removed)
    1028             :                         and delete it in that case.
    1029             :                 */
    1030             : 
    1031             :         } else {
    1032           0 :                 (*cur_formspec)->setFormSource(fs_src);
    1033           0 :                 (*cur_formspec)->setTextDest(txt_dest);
    1034             :         }
    1035           1 : }
    1036             : 
    1037             : #ifdef __ANDROID__
    1038             : #define SIZE_TAG "size[11,5.5]"
    1039             : #else
    1040             : #define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
    1041             : #endif
    1042             : 
    1043           0 : static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
    1044             :                 InventoryManager *invmgr, IGameDef *gamedef,
    1045             :                 IWritableTextureSource *tsrc, IrrlichtDevice *device,
    1046             :                 Client *client, std::string text)
    1047             : {
    1048             :         std::string formspec =
    1049             :                 FORMSPEC_VERSION_STRING
    1050             :                 SIZE_TAG
    1051           0 :                 "field[3,2.35;6,0.5;f_text;;" + text + "]"
    1052           0 :                 "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
    1053             :                 ;
    1054             : 
    1055             :         /* Create menu */
    1056             :         /* Note: FormspecFormSource and LocalFormspecHandler
    1057             :          * are deleted by guiFormSpecMenu                     */
    1058           0 :         FormspecFormSource *fs_src = new FormspecFormSource(formspec);
    1059           0 :         LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
    1060             : 
    1061           0 :         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
    1062           0 : }
    1063             : 
    1064           0 : static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
    1065             :                 InventoryManager *invmgr, IGameDef *gamedef,
    1066             :                 IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
    1067             : {
    1068             :         std::string formspec =
    1069           0 :                 std::string(FORMSPEC_VERSION_STRING) +
    1070             :                 SIZE_TAG
    1071             :                 "bgcolor[#320000b4;true]"
    1072           0 :                 "label[4.85,1.35;" + gettext("You died.") + "]"
    1073           0 :                 "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
    1074             :                 ;
    1075             : 
    1076             :         /* Create menu */
    1077             :         /* Note: FormspecFormSource and LocalFormspecHandler
    1078             :          * are deleted by guiFormSpecMenu                     */
    1079           0 :         FormspecFormSource *fs_src = new FormspecFormSource(formspec);
    1080           0 :         LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
    1081             : 
    1082           0 :         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,  fs_src, txt_dst, NULL);
    1083           0 : }
    1084             : 
    1085             : /******************************************************************************/
    1086           1 : static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
    1087             :                 InventoryManager *invmgr, IGameDef *gamedef,
    1088             :                 IWritableTextureSource *tsrc, IrrlichtDevice *device,
    1089             :                 bool singleplayermode)
    1090             : {
    1091             : #ifdef __ANDROID__
    1092             :         std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
    1093             :                                    "No menu visible:\n"
    1094             :                                    "- single tap: button activate\n"
    1095             :                                    "- double tap: place/use\n"
    1096             :                                    "- slide finger: look around\n"
    1097             :                                    "Menu/Inventory visible:\n"
    1098             :                                    "- double tap (outside):\n"
    1099             :                                    " -->close\n"
    1100             :                                    "- touch stack, touch slot:\n"
    1101             :                                    " --> move stack\n"
    1102             :                                    "- touch&drag, tap 2nd finger\n"
    1103             :                                    " --> place single item to slot\n"
    1104             :                                                              ));
    1105             : #else
    1106           3 :         std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
    1107             :                                    "- WASD: move\n"
    1108             :                                    "- Space: jump/climb\n"
    1109             :                                    "- Shift: sneak/go down\n"
    1110             :                                    "- Q: drop item\n"
    1111             :                                    "- I: inventory\n"
    1112             :                                    "- Mouse: turn/look\n"
    1113             :                                    "- Mouse left: dig/punch\n"
    1114             :                                    "- Mouse right: place/use\n"
    1115             :                                    "- Mouse wheel: select item\n"
    1116             :                                    "- T: chat\n"
    1117           3 :                                                              ));
    1118             : #endif
    1119             : 
    1120           1 :         float ypos = singleplayermode ? 0.5 : 0.1;
    1121           2 :         std::ostringstream os;
    1122             : 
    1123           1 :         os << FORMSPEC_VERSION_STRING  << SIZE_TAG
    1124           2 :            << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
    1125           2 :            << wide_to_narrow(wstrgettext("Continue"))     << "]";
    1126             : 
    1127           1 :         if (!singleplayermode) {
    1128           1 :                 os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
    1129           2 :                    << wide_to_narrow(wstrgettext("Change Password")) << "]";
    1130             :         }
    1131             : 
    1132             : #ifndef __ANDROID__
    1133           1 :         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
    1134           2 :                         << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
    1135           1 :         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
    1136           2 :                         << wide_to_narrow(wstrgettext("Change Keys"))  << "]";
    1137             : #endif
    1138           1 :         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
    1139           2 :                         << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
    1140           1 :         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
    1141           3 :                         << wide_to_narrow(wstrgettext("Exit to OS"))   << "]"
    1142           1 :                         << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
    1143           1 :                         << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n"
    1144           2 :                         << g_build_info << "\n"
    1145           3 :                         << "path_user = " << wrap_rows(porting::path_user, 20)
    1146           1 :                         << "\n;]";
    1147             : 
    1148             :         /* Create menu */
    1149             :         /* Note: FormspecFormSource and LocalFormspecHandler  *
    1150             :          * are deleted by guiFormSpecMenu                     */
    1151           1 :         FormspecFormSource *fs_src = new FormspecFormSource(os.str());
    1152           1 :         LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
    1153             : 
    1154           1 :         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,  fs_src, txt_dst, NULL);
    1155           2 :         std::string con("btn_continue");
    1156           1 :         (*cur_formspec)->setFocus(con);
    1157           1 :         (*cur_formspec)->doPause = true;
    1158           1 : }
    1159             : 
    1160             : /******************************************************************************/
    1161        1166 : static void updateChat(Client &client, f32 dtime, bool show_debug,
    1162             :                 const v2u32 &screensize, bool show_chat, u32 show_profiler,
    1163             :                 ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat)
    1164             : {
    1165             :         // Add chat log output for errors to be shown in chat
    1166        1166 :         static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
    1167             : 
    1168             :         // Get new messages from error log buffer
    1169        1166 :         while (!chat_log_error_buf.empty()) {
    1170           0 :                 chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
    1171             :         }
    1172             : 
    1173             :         // Get new messages from client
    1174        2332 :         std::wstring message;
    1175             : 
    1176        1170 :         while (client.getChatMessage(message)) {
    1177           2 :                 chat_backend.addUnparsedMessage(message);
    1178             :         }
    1179             : 
    1180             :         // Remove old messages
    1181        1166 :         chat_backend.step(dtime);
    1182             : 
    1183             :         // Display all messages in a static text element
    1184        1166 :         unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
    1185        2332 :         std::wstring recent_chat       = chat_backend.getRecentChat();
    1186        1166 :         unsigned int line_height       = g_fontengine->getLineHeight();
    1187             : 
    1188        1166 :         guitext_chat->setText(recent_chat.c_str());
    1189             : 
    1190             :         // Update gui element size and position
    1191        1166 :         s32 chat_y = 5 + line_height;
    1192             : 
    1193        1166 :         if (show_debug)
    1194           0 :                 chat_y += line_height;
    1195             : 
    1196             :         // first pass to calculate height of text to be set
    1197        2332 :         s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10,
    1198        3498 :                              porting::getWindowSize().X - 20);
    1199        1166 :         core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
    1200        1166 :         guitext_chat->setRelativePosition(rect);
    1201             : 
    1202             :         //now use real height of text and adjust rect according to this size
    1203        2332 :         rect = core::rect<s32>(10, chat_y, width,
    1204        2332 :                                chat_y + guitext_chat->getTextHeight());
    1205             : 
    1206             : 
    1207        1166 :         guitext_chat->setRelativePosition(rect);
    1208             :         // Don't show chat if disabled or empty or profiler is enabled
    1209        1166 :         guitext_chat->setVisible(
    1210        2332 :                 show_chat && recent_chat_count != 0 && !show_profiler);
    1211        1166 : }
    1212             : 
    1213             : 
    1214             : /****************************************************************************
    1215             :  Fast key cache for main game loop
    1216             :  ****************************************************************************/
    1217             : 
    1218             : /* This is faster than using getKeySetting with the tradeoff that functions
    1219             :  * using it must make sure that it's initialised before using it and there is
    1220             :  * no error handling (for example bounds checking). This is really intended for
    1221             :  * use only in the main running loop of the client (the_game()) where the faster
    1222             :  * (up to 10x faster) key lookup is an asset. Other parts of the codebase
    1223             :  * (e.g. formspecs) should continue using getKeySetting().
    1224             :  */
    1225           1 : struct KeyCache {
    1226             : 
    1227           1 :         KeyCache() { populate(); }
    1228             : 
    1229             :         enum {
    1230             :                 // Player movement
    1231             :                 KEYMAP_ID_FORWARD,
    1232             :                 KEYMAP_ID_BACKWARD,
    1233             :                 KEYMAP_ID_LEFT,
    1234             :                 KEYMAP_ID_RIGHT,
    1235             :                 KEYMAP_ID_JUMP,
    1236             :                 KEYMAP_ID_SPECIAL1,
    1237             :                 KEYMAP_ID_SNEAK,
    1238             : 
    1239             :                 // Other
    1240             :                 KEYMAP_ID_DROP,
    1241             :                 KEYMAP_ID_INVENTORY,
    1242             :                 KEYMAP_ID_CHAT,
    1243             :                 KEYMAP_ID_CMD,
    1244             :                 KEYMAP_ID_CONSOLE,
    1245             :                 KEYMAP_ID_MINIMAP,
    1246             :                 KEYMAP_ID_FREEMOVE,
    1247             :                 KEYMAP_ID_FASTMOVE,
    1248             :                 KEYMAP_ID_NOCLIP,
    1249             :                 KEYMAP_ID_CINEMATIC,
    1250             :                 KEYMAP_ID_SCREENSHOT,
    1251             :                 KEYMAP_ID_TOGGLE_HUD,
    1252             :                 KEYMAP_ID_TOGGLE_CHAT,
    1253             :                 KEYMAP_ID_TOGGLE_FORCE_FOG_OFF,
    1254             :                 KEYMAP_ID_TOGGLE_UPDATE_CAMERA,
    1255             :                 KEYMAP_ID_TOGGLE_DEBUG,
    1256             :                 KEYMAP_ID_TOGGLE_PROFILER,
    1257             :                 KEYMAP_ID_CAMERA_MODE,
    1258             :                 KEYMAP_ID_INCREASE_VIEWING_RANGE,
    1259             :                 KEYMAP_ID_DECREASE_VIEWING_RANGE,
    1260             :                 KEYMAP_ID_RANGESELECT,
    1261             : 
    1262             :                 KEYMAP_ID_QUICKTUNE_NEXT,
    1263             :                 KEYMAP_ID_QUICKTUNE_PREV,
    1264             :                 KEYMAP_ID_QUICKTUNE_INC,
    1265             :                 KEYMAP_ID_QUICKTUNE_DEC,
    1266             : 
    1267             :                 KEYMAP_ID_DEBUG_STACKS,
    1268             : 
    1269             :                 // Fake keycode for array size and internal checks
    1270             :                 KEYMAP_INTERNAL_ENUM_COUNT
    1271             : 
    1272             : 
    1273             :         };
    1274             : 
    1275             :         void populate();
    1276             : 
    1277             :         KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT];
    1278             : };
    1279             : 
    1280           1 : void KeyCache::populate()
    1281             : {
    1282           1 :         key[KEYMAP_ID_FORWARD]      = getKeySetting("keymap_forward");
    1283           1 :         key[KEYMAP_ID_BACKWARD]     = getKeySetting("keymap_backward");
    1284           1 :         key[KEYMAP_ID_LEFT]         = getKeySetting("keymap_left");
    1285           1 :         key[KEYMAP_ID_RIGHT]        = getKeySetting("keymap_right");
    1286           1 :         key[KEYMAP_ID_JUMP]         = getKeySetting("keymap_jump");
    1287           1 :         key[KEYMAP_ID_SPECIAL1]     = getKeySetting("keymap_special1");
    1288           1 :         key[KEYMAP_ID_SNEAK]        = getKeySetting("keymap_sneak");
    1289             : 
    1290           1 :         key[KEYMAP_ID_DROP]         = getKeySetting("keymap_drop");
    1291           1 :         key[KEYMAP_ID_INVENTORY]    = getKeySetting("keymap_inventory");
    1292           1 :         key[KEYMAP_ID_CHAT]         = getKeySetting("keymap_chat");
    1293           1 :         key[KEYMAP_ID_CMD]          = getKeySetting("keymap_cmd");
    1294           1 :         key[KEYMAP_ID_CONSOLE]      = getKeySetting("keymap_console");
    1295           1 :         key[KEYMAP_ID_MINIMAP]      = getKeySetting("keymap_minimap");
    1296           1 :         key[KEYMAP_ID_FREEMOVE]     = getKeySetting("keymap_freemove");
    1297           1 :         key[KEYMAP_ID_FASTMOVE]     = getKeySetting("keymap_fastmove");
    1298           1 :         key[KEYMAP_ID_NOCLIP]       = getKeySetting("keymap_noclip");
    1299           1 :         key[KEYMAP_ID_CINEMATIC]    = getKeySetting("keymap_cinematic");
    1300           1 :         key[KEYMAP_ID_SCREENSHOT]   = getKeySetting("keymap_screenshot");
    1301           1 :         key[KEYMAP_ID_TOGGLE_HUD]   = getKeySetting("keymap_toggle_hud");
    1302           1 :         key[KEYMAP_ID_TOGGLE_CHAT]  = getKeySetting("keymap_toggle_chat");
    1303             :         key[KEYMAP_ID_TOGGLE_FORCE_FOG_OFF]
    1304           1 :                         = getKeySetting("keymap_toggle_force_fog_off");
    1305             :         key[KEYMAP_ID_TOGGLE_UPDATE_CAMERA]
    1306           1 :                         = getKeySetting("keymap_toggle_update_camera");
    1307             :         key[KEYMAP_ID_TOGGLE_DEBUG]
    1308           1 :                         = getKeySetting("keymap_toggle_debug");
    1309             :         key[KEYMAP_ID_TOGGLE_PROFILER]
    1310           1 :                         = getKeySetting("keymap_toggle_profiler");
    1311             :         key[KEYMAP_ID_CAMERA_MODE]
    1312           1 :                         = getKeySetting("keymap_camera_mode");
    1313             :         key[KEYMAP_ID_INCREASE_VIEWING_RANGE]
    1314           1 :                         = getKeySetting("keymap_increase_viewing_range_min");
    1315             :         key[KEYMAP_ID_DECREASE_VIEWING_RANGE]
    1316           1 :                         = getKeySetting("keymap_decrease_viewing_range_min");
    1317             :         key[KEYMAP_ID_RANGESELECT]
    1318           1 :                         = getKeySetting("keymap_rangeselect");
    1319             : 
    1320           1 :         key[KEYMAP_ID_QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next");
    1321           1 :         key[KEYMAP_ID_QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev");
    1322           1 :         key[KEYMAP_ID_QUICKTUNE_INC]  = getKeySetting("keymap_quicktune_inc");
    1323           1 :         key[KEYMAP_ID_QUICKTUNE_DEC]  = getKeySetting("keymap_quicktune_dec");
    1324             : 
    1325           1 :         key[KEYMAP_ID_DEBUG_STACKS]   = getKeySetting("keymap_print_debug_stacks");
    1326           1 : }
    1327             : 
    1328             : 
    1329             : /****************************************************************************
    1330             : 
    1331             :  ****************************************************************************/
    1332             : 
    1333             : const float object_hit_delay = 0.2;
    1334             : 
    1335             : struct FpsControl {
    1336             :         u32 last_time, busy_time, sleep_time;
    1337             : };
    1338             : 
    1339             : 
    1340             : /* The reason the following structs are not anonymous structs within the
    1341             :  * class is that they are not used by the majority of member functions and
    1342             :  * many functions that do require objects of thse types do not modify them
    1343             :  * (so they can be passed as a const qualified parameter)
    1344             :  */
    1345             : struct CameraOrientation {
    1346             :         f32 camera_yaw;    // "right/left"
    1347             :         f32 camera_pitch;  // "up/down"
    1348             : };
    1349             : 
    1350             : struct GameRunData {
    1351             :         u16 dig_index;
    1352             :         u16 new_playeritem;
    1353             :         PointedThing pointed_old;
    1354             :         bool digging;
    1355             :         bool ldown_for_dig;
    1356             :         bool left_punch;
    1357             :         bool update_wielded_item_trigger;
    1358             :         bool reset_jump_timer;
    1359             :         float nodig_delay_timer;
    1360             :         float dig_time;
    1361             :         float dig_time_complete;
    1362             :         float repeat_rightclick_timer;
    1363             :         float object_hit_delay_timer;
    1364             :         float time_from_last_punch;
    1365             :         ClientActiveObject *selected_object;
    1366             : 
    1367             :         float jump_timer;
    1368             :         float damage_flash;
    1369             :         float update_draw_list_timer;
    1370             :         float statustext_time;
    1371             : 
    1372             :         f32 fog_range;
    1373             : 
    1374             :         v3f update_draw_list_last_cam_dir;
    1375             : 
    1376             :         u32 profiler_current_page;
    1377             :         u32 profiler_max_page;     // Number of pages
    1378             : 
    1379             :         float time_of_day;
    1380             :         float time_of_day_smooth;
    1381             : };
    1382             : 
    1383             : struct Jitter {
    1384             :         f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
    1385             : };
    1386             : 
    1387             : struct RunStats {
    1388             :         u32 drawtime;
    1389             :         u32 beginscenetime;
    1390             :         u32 endscenetime;
    1391             : 
    1392             :         Jitter dtime_jitter, busy_time_jitter;
    1393             : };
    1394             : 
    1395             : /* Flags that can, or may, change during main game loop
    1396             :  */
    1397             : struct VolatileRunFlags {
    1398             :         bool invert_mouse;
    1399             :         bool show_chat;
    1400             :         bool show_hud;
    1401             :         bool show_minimap;
    1402             :         bool force_fog_off;
    1403             :         bool show_debug;
    1404             :         bool show_profiler_graph;
    1405             :         bool disable_camera_update;
    1406             :         bool first_loop_after_window_activation;
    1407             :         bool camera_offset_changed;
    1408             : };
    1409             : 
    1410             : 
    1411             : /****************************************************************************
    1412             :  THE GAME
    1413             :  ****************************************************************************/
    1414             : 
    1415             : /* This is not intended to be a public class. If a public class becomes
    1416             :  * desirable then it may be better to create another 'wrapper' class that
    1417             :  * hides most of the stuff in this class (nothing in this class is required
    1418             :  * by any other file) but exposes the public methods/data only.
    1419             :  */
    1420             : class Game
    1421             : {
    1422             : public:
    1423             :         Game();
    1424             :         ~Game();
    1425             : 
    1426             :         bool startup(bool *kill,
    1427             :                         bool random_input,
    1428             :                         InputHandler *input,
    1429             :                         IrrlichtDevice *device,
    1430             :                         const std::string &map_dir,
    1431             :                         const std::string &playername,
    1432             :                         const std::string &password,
    1433             :                         // If address is "", local server is used and address is updated
    1434             :                         std::string *address,
    1435             :                         u16 port,
    1436             :                         std::string &error_message,
    1437             :                         ChatBackend *chat_backend,
    1438             :                         const SubgameSpec &gamespec,    // Used for local game
    1439             :                         bool simple_singleplayer_mode);
    1440             : 
    1441             :         void run();
    1442             :         void shutdown();
    1443             : 
    1444             : protected:
    1445             : 
    1446             :         void extendedResourceCleanup();
    1447             : 
    1448             :         // Basic initialisation
    1449             :         bool init(const std::string &map_dir, std::string *address,
    1450             :                         u16 port,
    1451             :                         const SubgameSpec &gamespec);
    1452             :         bool initSound();
    1453             :         bool createSingleplayerServer(const std::string map_dir,
    1454             :                         const SubgameSpec &gamespec, u16 port, std::string *address);
    1455             : 
    1456             :         // Client creation
    1457             :         bool createClient(const std::string &playername,
    1458             :                         const std::string &password, std::string *address, u16 port);
    1459             :         bool initGui();
    1460             : 
    1461             :         // Client connection
    1462             :         bool connectToServer(const std::string &playername,
    1463             :                         const std::string &password, std::string *address, u16 port,
    1464             :                         bool *connect_ok, bool *aborted);
    1465             :         bool getServerContent(bool *aborted);
    1466             : 
    1467             :         // Main loop
    1468             : 
    1469             :         void updateInteractTimers(GameRunData *runData, f32 dtime);
    1470             :         bool checkConnection();
    1471             :         bool handleCallbacks();
    1472             :         void processQueues();
    1473             :         void updateProfilers(const GameRunData &runData, const RunStats &stats,
    1474             :                         const FpsControl &draw_times, f32 dtime);
    1475             :         void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times,
    1476             :                         f32 dtime);
    1477             :         void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
    1478             : 
    1479             :         void processUserInput(VolatileRunFlags *flags, GameRunData *runData,
    1480             :                         f32 dtime);
    1481             :         void processKeyboardInput(VolatileRunFlags *flags,
    1482             :                         float *statustext_time,
    1483             :                         float *jump_timer,
    1484             :                         bool *reset_jump_timer,
    1485             :                         u32 *profiler_current_page,
    1486             :                         u32 profiler_max_page);
    1487             :         void processItemSelection(u16 *new_playeritem);
    1488             : 
    1489             :         void dropSelectedItem();
    1490             :         void openInventory();
    1491             :         void openConsole();
    1492             :         void toggleFreeMove(float *statustext_time);
    1493             :         void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
    1494             :         void toggleFast(float *statustext_time);
    1495             :         void toggleNoClip(float *statustext_time);
    1496             :         void toggleCinematic(float *statustext_time);
    1497             : 
    1498             :         void toggleChat(float *statustext_time, bool *flag);
    1499             :         void toggleHud(float *statustext_time, bool *flag);
    1500             :         void toggleMinimap(float *statustext_time, bool *flag1, bool *flag2,
    1501             :                         bool shift_pressed);
    1502             :         void toggleFog(float *statustext_time, bool *flag);
    1503             :         void toggleDebug(float *statustext_time, bool *show_debug,
    1504             :                         bool *show_profiler_graph);
    1505             :         void toggleUpdateCamera(float *statustext_time, bool *flag);
    1506             :         void toggleProfiler(float *statustext_time, u32 *profiler_current_page,
    1507             :                         u32 profiler_max_page);
    1508             : 
    1509             :         void increaseViewRange(float *statustext_time);
    1510             :         void decreaseViewRange(float *statustext_time);
    1511             :         void toggleFullViewRange(float *statustext_time);
    1512             : 
    1513             :         void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
    1514             :         void updateCameraOrientation(CameraOrientation *cam,
    1515             :                         const VolatileRunFlags &flags);
    1516             :         void updatePlayerControl(const CameraOrientation &cam);
    1517             :         void step(f32 *dtime);
    1518             :         void processClientEvents(CameraOrientation *cam, float *damage_flash);
    1519             :         void updateCamera(VolatileRunFlags *flags, u32 busy_time, f32 dtime,
    1520             :                         float time_from_last_punch);
    1521             :         void updateSound(f32 dtime);
    1522             :         void processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
    1523             :                         GameRunData *runData, f32 dtime, bool show_hud,
    1524             :                         bool show_debug);
    1525             :         void handlePointingAtNode(GameRunData *runData,
    1526             :                         const PointedThing &pointed, const ItemDefinition &playeritem_def,
    1527             :                         const ToolCapabilities &playeritem_toolcap, f32 dtime);
    1528             :         void handlePointingAtObject(GameRunData *runData,
    1529             :                         const PointedThing &pointed, const ItemStack &playeritem,
    1530             :                         const v3f &player_position, bool show_debug);
    1531             :         void handleDigging(GameRunData *runData, const PointedThing &pointed,
    1532             :                         const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap,
    1533             :                         f32 dtime);
    1534             :         void updateFrame(std::vector<aabb3f> &highlight_boxes, ProfilerGraph *graph,
    1535             :                         RunStats *stats, GameRunData *runData,
    1536             :                         f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam);
    1537             :         void updateGui(float *statustext_time, const RunStats &stats,
    1538             :                         const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
    1539             :                         const CameraOrientation &cam);
    1540             :         void updateProfilerGraphs(ProfilerGraph *graph);
    1541             : 
    1542             :         // Misc
    1543             :         void limitFps(FpsControl *fps_timings, f32 *dtime);
    1544             : 
    1545             :         void showOverlayMessage(const wchar_t *msg, float dtime, int percent,
    1546             :                         bool draw_clouds = true);
    1547             : 
    1548             : private:
    1549             :         InputHandler *input;
    1550             : 
    1551             :         Client *client;
    1552             :         Server *server;
    1553             : 
    1554             :         IWritableTextureSource *texture_src;
    1555             :         IWritableShaderSource *shader_src;
    1556             : 
    1557             :         // When created, these will be filled with data received from the server
    1558             :         IWritableItemDefManager *itemdef_manager;
    1559             :         IWritableNodeDefManager *nodedef_manager;
    1560             : 
    1561             :         GameOnDemandSoundFetcher soundfetcher; // useful when testing
    1562             :         ISoundManager *sound;
    1563             :         bool sound_is_dummy;
    1564             :         SoundMaker *soundmaker;
    1565             : 
    1566             :         ChatBackend *chat_backend;
    1567             : 
    1568             :         GUIFormSpecMenu *current_formspec;
    1569             : 
    1570             :         EventManager *eventmgr;
    1571             :         QuicktuneShortcutter *quicktune;
    1572             : 
    1573             :         GUIChatConsole *gui_chat_console; // Free using ->Drop()
    1574             :         MapDrawControl *draw_control;
    1575             :         Camera *camera;
    1576             :         Clouds *clouds;                   // Free using ->Drop()
    1577             :         Sky *sky;                         // Free using ->Drop()
    1578             :         Inventory *local_inventory;
    1579             :         Hud *hud;
    1580             :         Mapper *mapper;
    1581             : 
    1582             :         /* 'cache'
    1583             :            This class does take ownership/responsibily for cleaning up etc of any of
    1584             :            these items (e.g. device)
    1585             :         */
    1586             :         IrrlichtDevice *device;
    1587             :         video::IVideoDriver *driver;
    1588             :         scene::ISceneManager *smgr;
    1589             :         bool *kill;
    1590             :         std::string *error_message;
    1591             :         IGameDef *gamedef;                     // Convenience (same as *client)
    1592             :         scene::ISceneNode *skybox;
    1593             : 
    1594             :         bool random_input;
    1595             :         bool simple_singleplayer_mode;
    1596             :         /* End 'cache' */
    1597             : 
    1598             :         /* Pre-calculated values
    1599             :          */
    1600             :         int crack_animation_length;
    1601             : 
    1602             :         /* GUI stuff
    1603             :          */
    1604             :         gui::IGUIStaticText *guitext;          // First line of debug text
    1605             :         gui::IGUIStaticText *guitext2;         // Second line of debug text
    1606             :         gui::IGUIStaticText *guitext_info;     // At the middle of the screen
    1607             :         gui::IGUIStaticText *guitext_status;
    1608             :         gui::IGUIStaticText *guitext_chat;         // Chat text
    1609             :         gui::IGUIStaticText *guitext_profiler; // Profiler text
    1610             : 
    1611             :         std::wstring infotext;
    1612             :         std::wstring statustext;
    1613             : 
    1614             :         KeyCache keycache;
    1615             : 
    1616             :         IntervalLimiter profiler_interval;
    1617             : 
    1618             :         /* TODO: Add a callback function so these can be updated when a setting
    1619             :          *       changes.  At this point in time it doesn't matter (e.g. /set
    1620             :          *       is documented to change server settings only)
    1621             :          *
    1622             :          * TODO: Local caching of settings is not optimal and should at some stage
    1623             :          *       be updated to use a global settings object for getting thse values
    1624             :          *       (as opposed to the this local caching). This can be addressed in
    1625             :          *       a later release.
    1626             :          */
    1627             :         bool m_cache_doubletap_jump;
    1628             :         bool m_cache_enable_node_highlighting;
    1629             :         bool m_cache_enable_clouds;
    1630             :         bool m_cache_enable_particles;
    1631             :         bool m_cache_enable_fog;
    1632             :         f32  m_cache_mouse_sensitivity;
    1633             :         f32  m_repeat_right_click_time;
    1634             : 
    1635             : #ifdef __ANDROID__
    1636             :         bool m_cache_hold_aux1;
    1637             : #endif
    1638             : 
    1639             : };
    1640             : 
    1641           1 : Game::Game() :
    1642             :         client(NULL),
    1643             :         server(NULL),
    1644             :         texture_src(NULL),
    1645             :         shader_src(NULL),
    1646             :         itemdef_manager(NULL),
    1647             :         nodedef_manager(NULL),
    1648             :         sound(NULL),
    1649             :         sound_is_dummy(false),
    1650             :         soundmaker(NULL),
    1651             :         chat_backend(NULL),
    1652             :         current_formspec(NULL),
    1653             :         eventmgr(NULL),
    1654             :         quicktune(NULL),
    1655             :         gui_chat_console(NULL),
    1656             :         draw_control(NULL),
    1657             :         camera(NULL),
    1658             :         clouds(NULL),
    1659             :         sky(NULL),
    1660             :         local_inventory(NULL),
    1661             :         hud(NULL),
    1662           1 :         mapper(NULL)
    1663             : {
    1664           1 :         m_cache_doubletap_jump            = g_settings->getBool("doubletap_jump");
    1665           1 :         m_cache_enable_node_highlighting  = g_settings->getBool("enable_node_highlighting");
    1666           1 :         m_cache_enable_clouds             = g_settings->getBool("enable_clouds");
    1667           1 :         m_cache_enable_particles          = g_settings->getBool("enable_particles");
    1668           1 :         m_cache_enable_fog                = g_settings->getBool("enable_fog");
    1669           1 :         m_cache_mouse_sensitivity         = g_settings->getFloat("mouse_sensitivity");
    1670           1 :         m_repeat_right_click_time         = g_settings->getFloat("repeat_rightclick_time");
    1671             : 
    1672           1 :         m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
    1673             : 
    1674             : #ifdef __ANDROID__
    1675             :         m_cache_hold_aux1 = false;      // This is initialised properly later
    1676             : #endif
    1677             : 
    1678           1 : }
    1679             : 
    1680             : 
    1681             : 
    1682             : /****************************************************************************
    1683             :  MinetestApp Public
    1684             :  ****************************************************************************/
    1685             : 
    1686           2 : Game::~Game()
    1687             : {
    1688           1 :         delete client;
    1689           1 :         delete soundmaker;
    1690           1 :         if (!sound_is_dummy)
    1691           1 :                 delete sound;
    1692             : 
    1693           1 :         delete server; // deleted first to stop all server threads
    1694             : 
    1695           1 :         delete hud;
    1696           1 :         delete local_inventory;
    1697           1 :         delete camera;
    1698           1 :         delete quicktune;
    1699           1 :         delete eventmgr;
    1700           1 :         delete texture_src;
    1701           1 :         delete shader_src;
    1702           1 :         delete nodedef_manager;
    1703           1 :         delete itemdef_manager;
    1704           1 :         delete draw_control;
    1705             : 
    1706           1 :         extendedResourceCleanup();
    1707           1 : }
    1708             : 
    1709           1 : bool Game::startup(bool *kill,
    1710             :                 bool random_input,
    1711             :                 InputHandler *input,
    1712             :                 IrrlichtDevice *device,
    1713             :                 const std::string &map_dir,
    1714             :                 const std::string &playername,
    1715             :                 const std::string &password,
    1716             :                 std::string *address,     // can change if simple_singleplayer_mode
    1717             :                 u16 port,
    1718             :                 std::string &error_message,
    1719             :                 ChatBackend *chat_backend,
    1720             :                 const SubgameSpec &gamespec,
    1721             :                 bool simple_singleplayer_mode)
    1722             : {
    1723             :         // "cache"
    1724           1 :         this->device        = device;
    1725           1 :         this->kill          = kill;
    1726           1 :         this->error_message = &error_message;
    1727           1 :         this->random_input  = random_input;
    1728           1 :         this->input         = input;
    1729           1 :         this->chat_backend  = chat_backend;
    1730           1 :         this->simple_singleplayer_mode = simple_singleplayer_mode;
    1731             : 
    1732           1 :         driver              = device->getVideoDriver();
    1733           1 :         smgr                = device->getSceneManager();
    1734             : 
    1735           1 :         smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
    1736             : 
    1737           1 :         if (!init(map_dir, address, port, gamespec))
    1738           0 :                 return false;
    1739             : 
    1740           1 :         if (!createClient(playername, password, address, port))
    1741           0 :                 return false;
    1742             : 
    1743           1 :         return true;
    1744             : }
    1745             : 
    1746             : 
    1747           1 : void Game::run()
    1748             : {
    1749           2 :         ProfilerGraph graph;
    1750           1 :         RunStats stats              = { 0 };
    1751           1 :         CameraOrientation cam_view_target  = { 0 };
    1752           1 :         CameraOrientation cam_view  = { 0 };
    1753           1 :         GameRunData runData         = { 0 };
    1754           1 :         FpsControl draw_times       = { 0 };
    1755           1 :         VolatileRunFlags flags      = { 0 };
    1756             :         f32 dtime; // in seconds
    1757             : 
    1758           1 :         runData.time_from_last_punch  = 10.0;
    1759           1 :         runData.profiler_max_page = 3;
    1760           1 :         runData.update_wielded_item_trigger = true;
    1761             : 
    1762           1 :         flags.show_chat = true;
    1763           1 :         flags.show_hud = true;
    1764           1 :         flags.show_minimap = g_settings->getBool("enable_minimap");
    1765           1 :         flags.show_debug = g_settings->getBool("show_debug");
    1766           1 :         flags.invert_mouse = g_settings->getBool("invert_mouse");
    1767           1 :         flags.first_loop_after_window_activation = true;
    1768             : 
    1769             :         /* Clear the profiler */
    1770           2 :         Profiler::GraphValues dummyvalues;
    1771           1 :         g_profiler->graphGet(dummyvalues);
    1772             : 
    1773           1 :         draw_times.last_time = device->getTimer()->getTime();
    1774             : 
    1775           3 :         shader_src->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
    1776             :                         sky,
    1777             :                         &flags.force_fog_off,
    1778             :                         &runData.fog_range,
    1779           3 :                         client));
    1780             : 
    1781           2 :         std::vector<aabb3f> highlight_boxes;
    1782             : 
    1783           1 :         set_light_table(g_settings->getFloat("display_gamma"));
    1784             : 
    1785             : #ifdef __ANDROID__
    1786             :         m_cache_hold_aux1 = g_settings->getBool("fast_move")
    1787             :                         && client->checkPrivilege("fast");
    1788             : #endif
    1789             : 
    1790        2333 :         while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
    1791             : 
    1792             :                 /* Must be called immediately after a device->run() call because it
    1793             :                  * uses device->getTimer()->getTime()
    1794             :                  */
    1795        1166 :                 limitFps(&draw_times, &dtime);
    1796             : 
    1797        1166 :                 updateStats(&stats, draw_times, dtime);
    1798        1166 :                 updateInteractTimers(&runData, dtime);
    1799             : 
    1800        1166 :                 if (!checkConnection())
    1801           0 :                         break;
    1802        1166 :                 if (!handleCallbacks())
    1803           0 :                         break;
    1804             : 
    1805        1166 :                 processQueues();
    1806             : 
    1807        1166 :                 infotext = L"";
    1808        1166 :                 hud->resizeHotbar();
    1809             : 
    1810        1166 :                 updateProfilers(runData, stats, draw_times, dtime);
    1811        1166 :                 processUserInput(&flags, &runData, dtime);
    1812             :                 // Update camera before player movement to avoid camera lag of one frame
    1813        1166 :                 updateCameraDirection(&cam_view_target, &flags);
    1814        1166 :                 float cam_smoothing = 0;
    1815        1166 :                 if (g_settings->getBool("cinematic"))
    1816           0 :                         cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
    1817             :                 else
    1818        1166 :                         cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
    1819        1166 :                 cam_smoothing = rangelim(cam_smoothing, 0.01f, 1.0f);
    1820        2332 :                 cam_view.camera_yaw += (cam_view_target.camera_yaw -
    1821        3498 :                                 cam_view.camera_yaw) * cam_smoothing;
    1822        2332 :                 cam_view.camera_pitch += (cam_view_target.camera_pitch -
    1823        3498 :                                 cam_view.camera_pitch) * cam_smoothing;
    1824        1166 :                 updatePlayerControl(cam_view);
    1825        1166 :                 step(&dtime);
    1826        1166 :                 processClientEvents(&cam_view_target, &runData.damage_flash);
    1827        2332 :                 updateCamera(&flags, draw_times.busy_time, dtime,
    1828        2332 :                                 runData.time_from_last_punch);
    1829        1166 :                 updateSound(dtime);
    1830        2332 :                 processPlayerInteraction(highlight_boxes, &runData, dtime,
    1831        3498 :                                 flags.show_hud, flags.show_debug);
    1832             :                 updateFrame(highlight_boxes, &graph, &stats, &runData, dtime,
    1833        1166 :                                 flags, cam_view);
    1834        1166 :                 updateProfilerGraphs(&graph);
    1835             :         }
    1836           1 : }
    1837             : 
    1838             : 
    1839           1 : void Game::shutdown()
    1840             : {
    1841           1 :         showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
    1842             : 
    1843           1 :         if (clouds)
    1844           1 :                 clouds->drop();
    1845             : 
    1846           1 :         if (gui_chat_console)
    1847           1 :                 gui_chat_console->drop();
    1848             : 
    1849           1 :         if (sky)
    1850           1 :                 sky->drop();
    1851             : 
    1852             :         /* cleanup menus */
    1853           1 :         while (g_menumgr.menuCount() > 0) {
    1854           0 :                 g_menumgr.m_stack.front()->setVisible(false);
    1855           0 :                 g_menumgr.deletingMenu(g_menumgr.m_stack.front());
    1856             :         }
    1857             : 
    1858           1 :         if (current_formspec) {
    1859           1 :                 current_formspec->drop();
    1860           1 :                 current_formspec = NULL;
    1861             :         }
    1862             : 
    1863           1 :         chat_backend->addMessage(L"", L"# Disconnected.");
    1864           1 :         chat_backend->addMessage(L"", L"");
    1865             : 
    1866           1 :         if (client) {
    1867           1 :                 client->Stop();
    1868           5 :                 while (!client->isShutdown()) {
    1869             :                         assert(texture_src != NULL);
    1870             :                         assert(shader_src != NULL);
    1871           2 :                         texture_src->processQueue();
    1872           2 :                         shader_src->processQueue();
    1873           2 :                         sleep_ms(100);
    1874             :                 }
    1875             :         }
    1876           1 : }
    1877             : 
    1878             : 
    1879             : /****************************************************************************/
    1880             : /****************************************************************************
    1881             :  Startup
    1882             :  ****************************************************************************/
    1883             : /****************************************************************************/
    1884             : 
    1885           1 : bool Game::init(
    1886             :                 const std::string &map_dir,
    1887             :                 std::string *address,
    1888             :                 u16 port,
    1889             :                 const SubgameSpec &gamespec)
    1890             : {
    1891           1 :         showOverlayMessage(wgettext("Loading..."), 0, 0);
    1892             : 
    1893           1 :         texture_src = createTextureSource(device);
    1894           1 :         shader_src = createShaderSource(device);
    1895             : 
    1896           1 :         itemdef_manager = createItemDefManager();
    1897           1 :         nodedef_manager = createNodeDefManager();
    1898             : 
    1899           1 :         eventmgr = new EventManager();
    1900           1 :         quicktune = new QuicktuneShortcutter();
    1901             : 
    1902           1 :         if (!(texture_src && shader_src && itemdef_manager && nodedef_manager
    1903           1 :                         && eventmgr && quicktune))
    1904           0 :                 return false;
    1905             : 
    1906           1 :         if (!initSound())
    1907           0 :                 return false;
    1908             : 
    1909             :         // Create a server if not connecting to an existing one
    1910           1 :         if (*address == "") {
    1911           0 :                 if (!createSingleplayerServer(map_dir, gamespec, port, address))
    1912           0 :                         return false;
    1913             :         }
    1914             : 
    1915           1 :         return true;
    1916             : }
    1917             : 
    1918           1 : bool Game::initSound()
    1919             : {
    1920             : #if USE_SOUND
    1921           1 :         if (g_settings->getBool("enable_sound")) {
    1922           1 :                 infostream << "Attempting to use OpenAL audio" << std::endl;
    1923           1 :                 sound = createOpenALSoundManager(&soundfetcher);
    1924           1 :                 if (!sound)
    1925           0 :                         infostream << "Failed to initialize OpenAL audio" << std::endl;
    1926             :         } else
    1927           0 :                 infostream << "Sound disabled." << std::endl;
    1928             : #endif
    1929             : 
    1930           1 :         if (!sound) {
    1931           0 :                 infostream << "Using dummy audio." << std::endl;
    1932           0 :                 sound = &dummySoundManager;
    1933           0 :                 sound_is_dummy = true;
    1934             :         }
    1935             : 
    1936           1 :         soundmaker = new SoundMaker(sound, nodedef_manager);
    1937           1 :         if (!soundmaker)
    1938           0 :                 return false;
    1939             : 
    1940           1 :         soundmaker->registerReceiver(eventmgr);
    1941             : 
    1942           1 :         return true;
    1943             : }
    1944             : 
    1945           0 : bool Game::createSingleplayerServer(const std::string map_dir,
    1946             :                 const SubgameSpec &gamespec, u16 port, std::string *address)
    1947             : {
    1948           0 :         showOverlayMessage(wgettext("Creating server..."), 0, 5);
    1949             : 
    1950           0 :         std::string bind_str = g_settings->get("bind_address");
    1951           0 :         Address bind_addr(0, 0, 0, 0, port);
    1952             : 
    1953           0 :         if (g_settings->getBool("ipv6_server")) {
    1954           0 :                 bind_addr.setAddress((IPv6AddressBytes *) NULL);
    1955             :         }
    1956             : 
    1957             :         try {
    1958           0 :                 bind_addr.Resolve(bind_str.c_str());
    1959           0 :         } catch (ResolveError &e) {
    1960           0 :                 infostream << "Resolving bind address \"" << bind_str
    1961           0 :                            << "\" failed: " << e.what()
    1962           0 :                            << " -- Listening on all addresses." << std::endl;
    1963             :         }
    1964             : 
    1965           0 :         if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
    1966           0 :                 *error_message = "Unable to listen on " +
    1967           0 :                                 bind_addr.serializeString() +
    1968           0 :                                 " because IPv6 is disabled";
    1969           0 :                 errorstream << *error_message << std::endl;
    1970           0 :                 return false;
    1971             :         }
    1972             : 
    1973           0 :         server = new Server(map_dir, gamespec, simple_singleplayer_mode,
    1974           0 :                             bind_addr.isIPv6());
    1975             : 
    1976           0 :         server->start(bind_addr);
    1977             : 
    1978           0 :         return true;
    1979             : }
    1980             : 
    1981           1 : bool Game::createClient(const std::string &playername,
    1982             :                 const std::string &password, std::string *address, u16 port)
    1983             : {
    1984           1 :         showOverlayMessage(wgettext("Creating client..."), 0, 10);
    1985             : 
    1986           1 :         draw_control = new MapDrawControl;
    1987           1 :         if (!draw_control)
    1988           0 :                 return false;
    1989             : 
    1990             :         bool could_connect, connect_aborted;
    1991             : 
    1992           1 :         if (!connectToServer(playername, password, address, port,
    1993             :                         &could_connect, &connect_aborted))
    1994           0 :                 return false;
    1995             : 
    1996           1 :         if (!could_connect) {
    1997           0 :                 if (error_message->empty() && !connect_aborted) {
    1998             :                         // Should not happen if error messages are set properly
    1999           0 :                         *error_message = "Connection failed for unknown reason";
    2000           0 :                         errorstream << *error_message << std::endl;
    2001             :                 }
    2002           0 :                 return false;
    2003             :         }
    2004             : 
    2005           1 :         if (!getServerContent(&connect_aborted)) {
    2006           0 :                 if (error_message->empty() && !connect_aborted) {
    2007             :                         // Should not happen if error messages are set properly
    2008           0 :                         *error_message = "Connection failed for unknown reason";
    2009           0 :                         errorstream << *error_message << std::endl;
    2010             :                 }
    2011           0 :                 return false;
    2012             :         }
    2013             : 
    2014             :         // Update cached textures, meshes and materials
    2015           1 :         client->afterContentReceived(device);
    2016             : 
    2017             :         /* Camera
    2018             :          */
    2019           1 :         camera = new Camera(smgr, *draw_control, gamedef);
    2020           1 :         if (!camera || !camera->successfullyCreated(*error_message))
    2021           0 :                 return false;
    2022             : 
    2023             :         /* Clouds
    2024             :          */
    2025           1 :         if (m_cache_enable_clouds) {
    2026           1 :                 clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
    2027           1 :                 if (!clouds) {
    2028           0 :                         *error_message = "Memory allocation error (clouds)";
    2029           0 :                         errorstream << *error_message << std::endl;
    2030           0 :                         return false;
    2031             :                 }
    2032             :         }
    2033             : 
    2034             :         /* Skybox
    2035             :          */
    2036           1 :         sky = new Sky(smgr->getRootSceneNode(), smgr, -1, texture_src);
    2037           1 :         skybox = NULL;  // This is used/set later on in the main run loop
    2038             : 
    2039           1 :         local_inventory = new Inventory(itemdef_manager);
    2040             : 
    2041           1 :         if (!(sky && local_inventory)) {
    2042           0 :                 *error_message = "Memory allocation error (sky or local inventory)";
    2043           0 :                 errorstream << *error_message << std::endl;
    2044           0 :                 return false;
    2045             :         }
    2046             : 
    2047             :         /* Pre-calculated values
    2048             :          */
    2049           1 :         video::ITexture *t = texture_src->getTexture("crack_anylength.png");
    2050           1 :         if (t) {
    2051           1 :                 v2u32 size = t->getOriginalSize();
    2052           1 :                 crack_animation_length = size.Y / size.X;
    2053             :         } else {
    2054           0 :                 crack_animation_length = 5;
    2055             :         }
    2056             : 
    2057           1 :         if (!initGui())
    2058           0 :                 return false;
    2059             : 
    2060             :         /* Set window caption
    2061             :          */
    2062           2 :         std::wstring str = narrow_to_wide(PROJECT_NAME_C);
    2063           1 :         str += L" [";
    2064           1 :         str += driver->getName();
    2065           1 :         str += L"]";
    2066           1 :         device->setWindowCaption(str.c_str());
    2067             : 
    2068           1 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    2069           1 :         player->hurt_tilt_timer = 0;
    2070           1 :         player->hurt_tilt_strength = 0;
    2071             : 
    2072           1 :         hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
    2073             : 
    2074           1 :         if (!hud) {
    2075           0 :                 *error_message = "Memory error: could not create HUD";
    2076           0 :                 errorstream << *error_message << std::endl;
    2077           0 :                 return false;
    2078             :         }
    2079             : 
    2080           1 :         mapper = client->getMapper();
    2081           1 :         mapper->setMinimapMode(MINIMAP_MODE_OFF);
    2082             : 
    2083           1 :         return true;
    2084             : }
    2085             : 
    2086           1 : bool Game::initGui()
    2087             : {
    2088             :         // First line of debug text
    2089           4 :         guitext = guienv->addStaticText(
    2090           2 :                         narrow_to_wide(PROJECT_NAME_C).c_str(),
    2091             :                         core::rect<s32>(0, 0, 0, 0),
    2092           2 :                         false, false, guiroot);
    2093             : 
    2094             :         // Second line of debug text
    2095           3 :         guitext2 = guienv->addStaticText(
    2096             :                         L"",
    2097             :                         core::rect<s32>(0, 0, 0, 0),
    2098           2 :                         false, false, guiroot);
    2099             : 
    2100             :         // At the middle of the screen
    2101             :         // Object infos are shown in this
    2102           3 :         guitext_info = guienv->addStaticText(
    2103             :                         L"",
    2104           2 :                         core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
    2105           2 :                         false, true, guiroot);
    2106             : 
    2107             :         // Status text (displays info when showing and hiding GUI stuff, etc.)
    2108           3 :         guitext_status = guienv->addStaticText(
    2109             :                         L"<Status>",
    2110             :                         core::rect<s32>(0, 0, 0, 0),
    2111           2 :                         false, false, guiroot);
    2112           1 :         guitext_status->setVisible(false);
    2113             : 
    2114             :         // Chat text
    2115           3 :         guitext_chat = guienv->addStaticText(
    2116             :                         L"",
    2117             :                         core::rect<s32>(0, 0, 0, 0),
    2118             :                         //false, false); // Disable word wrap as of now
    2119           2 :                         false, true, guiroot);
    2120             :         // Remove stale "recent" chat messages from previous connections
    2121           1 :         chat_backend->clearRecentChat();
    2122             : 
    2123             :         // Chat backend and console
    2124           1 :         gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
    2125           1 :                         -1, chat_backend, client);
    2126           1 :         if (!gui_chat_console) {
    2127           0 :                 *error_message = "Could not allocate memory for chat console";
    2128           0 :                 errorstream << *error_message << std::endl;
    2129           0 :                 return false;
    2130             :         }
    2131             : 
    2132             :         // Profiler text (size is updated when text is updated)
    2133           3 :         guitext_profiler = guienv->addStaticText(
    2134             :                         L"<Profiler>",
    2135             :                         core::rect<s32>(0, 0, 0, 0),
    2136           2 :                         false, false, guiroot);
    2137           1 :         guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0));
    2138           1 :         guitext_profiler->setVisible(false);
    2139           1 :         guitext_profiler->setWordWrap(true);
    2140             : 
    2141             : #ifdef HAVE_TOUCHSCREENGUI
    2142             : 
    2143             :         if (g_touchscreengui)
    2144             :                 g_touchscreengui->init(texture_src, porting::getDisplayDensity());
    2145             : 
    2146             : #endif
    2147             : 
    2148           1 :         return true;
    2149             : }
    2150             : 
    2151           1 : bool Game::connectToServer(const std::string &playername,
    2152             :                 const std::string &password, std::string *address, u16 port,
    2153             :                 bool *connect_ok, bool *aborted)
    2154             : {
    2155           1 :         *connect_ok = false;    // Let's not be overly optimistic
    2156           1 :         *aborted = false;
    2157           1 :         bool local_server_mode = false;
    2158             : 
    2159           1 :         showOverlayMessage(wgettext("Resolving address..."), 0, 15);
    2160             : 
    2161           1 :         Address connect_address(0, 0, 0, 0, port);
    2162             : 
    2163             :         try {
    2164           1 :                 connect_address.Resolve(address->c_str());
    2165             : 
    2166           1 :                 if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
    2167             :                         //connect_address.Resolve("localhost");
    2168           0 :                         if (connect_address.isIPv6()) {
    2169           0 :                                 IPv6AddressBytes addr_bytes;
    2170           0 :                                 addr_bytes.bytes[15] = 1;
    2171           0 :                                 connect_address.setAddress(&addr_bytes);
    2172             :                         } else {
    2173           0 :                                 connect_address.setAddress(127, 0, 0, 1);
    2174             :                         }
    2175           0 :                         local_server_mode = true;
    2176             :                 }
    2177           0 :         } catch (ResolveError &e) {
    2178           0 :                 *error_message = std::string("Couldn't resolve address: ") + e.what();
    2179           0 :                 errorstream << *error_message << std::endl;
    2180           0 :                 return false;
    2181             :         }
    2182             : 
    2183           1 :         if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
    2184           0 :                 *error_message = "Unable to connect to " +
    2185           0 :                                 connect_address.serializeString() +
    2186           0 :                                 " because IPv6 is disabled";
    2187           0 :                 errorstream << *error_message << std::endl;
    2188           0 :                 return false;
    2189             :         }
    2190             : 
    2191             :         client = new Client(device,
    2192             :                         playername.c_str(), password,
    2193           1 :                         *draw_control, texture_src, shader_src,
    2194           1 :                         itemdef_manager, nodedef_manager, sound, eventmgr,
    2195           3 :                         connect_address.isIPv6());
    2196             : 
    2197           1 :         if (!client)
    2198           0 :                 return false;
    2199             : 
    2200           1 :         gamedef = client;       // Client acts as our GameDef
    2201             : 
    2202           1 :         infostream << "Connecting to server at ";
    2203           1 :         connect_address.print(&infostream);
    2204           1 :         infostream << std::endl;
    2205             : 
    2206           1 :         client->connect(connect_address, *address,
    2207           2 :                 simple_singleplayer_mode || local_server_mode);
    2208             : 
    2209             :         /*
    2210             :                 Wait for server to accept connection
    2211             :         */
    2212             : 
    2213             :         try {
    2214           1 :                 input->clear();
    2215             : 
    2216           1 :                 FpsControl fps_control = { 0 };
    2217             :                 f32 dtime;
    2218           1 :                 f32 wait_time = 0; // in seconds
    2219             : 
    2220           1 :                 fps_control.last_time = device->getTimer()->getTime();
    2221             : 
    2222          17 :                 while (device->run()) {
    2223             : 
    2224           9 :                         limitFps(&fps_control, &dtime);
    2225             : 
    2226             :                         // Update client and server
    2227           9 :                         client->step(dtime);
    2228             : 
    2229           9 :                         if (server != NULL)
    2230           0 :                                 server->step(dtime);
    2231             : 
    2232             :                         // End condition
    2233           9 :                         if (client->getState() == LC_Init) {
    2234           1 :                                 *connect_ok = true;
    2235           1 :                                 break;
    2236             :                         }
    2237             : 
    2238             :                         // Break conditions
    2239           8 :                         if (client->accessDenied()) {
    2240           0 :                                 *error_message = "Access denied. Reason: "
    2241           0 :                                                 + client->accessDeniedReason();
    2242           0 :                                 errorstream << *error_message << std::endl;
    2243           0 :                                 break;
    2244             :                         }
    2245             : 
    2246           8 :                         if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
    2247           0 :                                 *aborted = true;
    2248           0 :                                 infostream << "Connect aborted [Escape]" << std::endl;
    2249           0 :                                 break;
    2250             :                         }
    2251             : 
    2252           8 :                         wait_time += dtime;
    2253             :                         // Only time out if we aren't waiting for the server we started
    2254           8 :                         if ((*address != "") && (wait_time > 10)) {
    2255           0 :                                 *error_message = "Connection timed out.";
    2256           0 :                                 errorstream << *error_message << std::endl;
    2257           0 :                                 break;
    2258             :                         }
    2259             : 
    2260             :                         // Update status
    2261           8 :                         showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
    2262             :                 }
    2263           0 :         } catch (con::PeerNotFoundException &e) {
    2264             :                 // TODO: Should something be done here? At least an info/error
    2265             :                 // message?
    2266           0 :                 return false;
    2267             :         }
    2268             : 
    2269           1 :         return true;
    2270             : }
    2271             : 
    2272           1 : bool Game::getServerContent(bool *aborted)
    2273             : {
    2274           1 :         input->clear();
    2275             : 
    2276           1 :         FpsControl fps_control = { 0 };
    2277             :         f32 dtime; // in seconds
    2278             : 
    2279           1 :         fps_control.last_time = device->getTimer()->getTime();
    2280             : 
    2281          35 :         while (device->run()) {
    2282             : 
    2283          18 :                 limitFps(&fps_control, &dtime);
    2284             : 
    2285             :                 // Update client and server
    2286          18 :                 client->step(dtime);
    2287             : 
    2288          18 :                 if (server != NULL)
    2289           0 :                         server->step(dtime);
    2290             : 
    2291             :                 // End condition
    2292          19 :                 if (client->mediaReceived() && client->itemdefReceived() &&
    2293           1 :                                 client->nodedefReceived()) {
    2294           1 :                         break;
    2295             :                 }
    2296             : 
    2297             :                 // Error conditions
    2298          17 :                 if (!checkConnection())
    2299           0 :                         return false;
    2300             : 
    2301          17 :                 if (client->getState() < LC_Init) {
    2302           0 :                         *error_message = "Client disconnected";
    2303           0 :                         errorstream << *error_message << std::endl;
    2304           0 :                         return false;
    2305             :                 }
    2306             : 
    2307          17 :                 if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
    2308           0 :                         *aborted = true;
    2309           0 :                         infostream << "Connect aborted [Escape]" << std::endl;
    2310           0 :                         return false;
    2311             :                 }
    2312             : 
    2313             :                 // Display status
    2314          17 :                 int progress = 25;
    2315             : 
    2316          17 :                 if (!client->itemdefReceived()) {
    2317          12 :                         const wchar_t *text = wgettext("Item definitions...");
    2318          12 :                         progress = 25;
    2319          12 :                         draw_load_screen(text, device, guienv, dtime, progress);
    2320          12 :                         delete[] text;
    2321           5 :                 } else if (!client->nodedefReceived()) {
    2322           4 :                         const wchar_t *text = wgettext("Node definitions...");
    2323           4 :                         progress = 30;
    2324           4 :                         draw_load_screen(text, device, guienv, dtime, progress);
    2325           4 :                         delete[] text;
    2326             :                 } else {
    2327           2 :                         std::stringstream message;
    2328           1 :                         message.precision(3);
    2329           1 :                         message << gettext("Media...");
    2330             : 
    2331           2 :                         if ((USE_CURL == 0) ||
    2332           2 :                                         (!g_settings->getBool("enable_remote_media_server"))) {
    2333           0 :                                 float cur = client->getCurRate();
    2334           0 :                                 std::string cur_unit = gettext("KiB/s");
    2335             : 
    2336           0 :                                 if (cur > 900) {
    2337           0 :                                         cur /= 1024.0;
    2338           0 :                                         cur_unit = gettext("MiB/s");
    2339             :                                 }
    2340             : 
    2341           0 :                                 message << " (" << cur << ' ' << cur_unit << ")";
    2342             :                         }
    2343             : 
    2344           1 :                         progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
    2345           2 :                         draw_load_screen(narrow_to_wide(message.str()), device,
    2346           2 :                                         guienv, dtime, progress);
    2347             :                 }
    2348             :         }
    2349             : 
    2350           1 :         return true;
    2351             : }
    2352             : 
    2353             : 
    2354             : /****************************************************************************/
    2355             : /****************************************************************************
    2356             :  Run
    2357             :  ****************************************************************************/
    2358             : /****************************************************************************/
    2359             : 
    2360        1166 : inline void Game::updateInteractTimers(GameRunData *runData, f32 dtime)
    2361             : {
    2362        1166 :         if (runData->nodig_delay_timer >= 0)
    2363           1 :                 runData->nodig_delay_timer -= dtime;
    2364             : 
    2365        1166 :         if (runData->object_hit_delay_timer >= 0)
    2366           1 :                 runData->object_hit_delay_timer -= dtime;
    2367             : 
    2368        1166 :         runData->time_from_last_punch += dtime;
    2369        1166 : }
    2370             : 
    2371             : 
    2372             : /* returns false if game should exit, otherwise true
    2373             :  */
    2374        1183 : inline bool Game::checkConnection()
    2375             : {
    2376        1183 :         if (client->accessDenied()) {
    2377           0 :                 *error_message = "Access denied. Reason: "
    2378           0 :                                 + client->accessDeniedReason();
    2379           0 :                 errorstream << *error_message << std::endl;
    2380           0 :                 return false;
    2381             :         }
    2382             : 
    2383        1183 :         return true;
    2384             : }
    2385             : 
    2386             : 
    2387             : /* returns false if game should exit, otherwise true
    2388             :  */
    2389        1166 : inline bool Game::handleCallbacks()
    2390             : {
    2391        1166 :         if (g_gamecallback->disconnect_requested) {
    2392           0 :                 g_gamecallback->disconnect_requested = false;
    2393           0 :                 return false;
    2394             :         }
    2395             : 
    2396        1166 :         if (g_gamecallback->changepassword_requested) {
    2397             :                 (new GUIPasswordChange(guienv, guiroot, -1,
    2398           0 :                                        &g_menumgr, client))->drop();
    2399           0 :                 g_gamecallback->changepassword_requested = false;
    2400             :         }
    2401             : 
    2402        1166 :         if (g_gamecallback->changevolume_requested) {
    2403             :                 (new GUIVolumeChange(guienv, guiroot, -1,
    2404           0 :                                      &g_menumgr, client))->drop();
    2405           0 :                 g_gamecallback->changevolume_requested = false;
    2406             :         }
    2407             : 
    2408        1166 :         if (g_gamecallback->keyconfig_requested) {
    2409             :                 (new GUIKeyChangeMenu(guienv, guiroot, -1,
    2410           0 :                                       &g_menumgr))->drop();
    2411           0 :                 g_gamecallback->keyconfig_requested = false;
    2412             :         }
    2413             : 
    2414        1166 :         if (g_gamecallback->keyconfig_changed) {
    2415           0 :                 keycache.populate(); // update the cache with new settings
    2416           0 :                 g_gamecallback->keyconfig_changed = false;
    2417             :         }
    2418             : 
    2419        1166 :         return true;
    2420             : }
    2421             : 
    2422             : 
    2423        1166 : void Game::processQueues()
    2424             : {
    2425        1166 :         texture_src->processQueue();
    2426        1166 :         itemdef_manager->processQueue(gamedef);
    2427        1166 :         shader_src->processQueue();
    2428        1166 : }
    2429             : 
    2430             : 
    2431        1166 : void Game::updateProfilers(const GameRunData &runData, const RunStats &stats,
    2432             :                 const FpsControl &draw_times, f32 dtime)
    2433             : {
    2434             :         float profiler_print_interval =
    2435        1166 :                         g_settings->getFloat("profiler_print_interval");
    2436        1166 :         bool print_to_log = true;
    2437             : 
    2438        1166 :         if (profiler_print_interval == 0) {
    2439        1166 :                 print_to_log = false;
    2440        1166 :                 profiler_print_interval = 5;
    2441             :         }
    2442             : 
    2443        1166 :         if (profiler_interval.step(dtime, profiler_print_interval)) {
    2444           8 :                 if (print_to_log) {
    2445           0 :                         infostream << "Profiler:" << std::endl;
    2446           0 :                         g_profiler->print(infostream);
    2447             :                 }
    2448             : 
    2449          16 :                 update_profiler_gui(guitext_profiler, g_fontengine,
    2450           8 :                                 runData.profiler_current_page, runData.profiler_max_page,
    2451          24 :                                 driver->getScreenSize().Height);
    2452             : 
    2453           8 :                 g_profiler->clear();
    2454             :         }
    2455             : 
    2456        1166 :         addProfilerGraphs(stats, draw_times, dtime);
    2457        1166 : }
    2458             : 
    2459             : 
    2460        1166 : void Game::addProfilerGraphs(const RunStats &stats,
    2461             :                 const FpsControl &draw_times, f32 dtime)
    2462             : {
    2463        2332 :         g_profiler->graphAdd("mainloop_other",
    2464        2332 :                         draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f);
    2465             : 
    2466        1166 :         if (draw_times.sleep_time != 0)
    2467          82 :                 g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f);
    2468        1166 :         g_profiler->graphAdd("mainloop_dtime", dtime);
    2469             : 
    2470        1166 :         g_profiler->add("Elapsed time", dtime);
    2471        1166 :         g_profiler->avg("FPS", 1. / dtime);
    2472        1166 : }
    2473             : 
    2474             : 
    2475        1166 : void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
    2476             :                 f32 dtime)
    2477             : {
    2478             : 
    2479             :         f32 jitter;
    2480             :         Jitter *jp;
    2481             : 
    2482             :         /* Time average and jitter calculation
    2483             :          */
    2484        1166 :         jp = &stats->dtime_jitter;
    2485        1166 :         jp->avg = jp->avg * 0.96 + dtime * 0.04;
    2486             : 
    2487        1166 :         jitter = dtime - jp->avg;
    2488             : 
    2489        1166 :         if (jitter > jp->max)
    2490          56 :                 jp->max = jitter;
    2491             : 
    2492        1166 :         jp->counter += dtime;
    2493             : 
    2494        1166 :         if (jp->counter > 0.0) {
    2495          15 :                 jp->counter -= 3.0;
    2496          15 :                 jp->max_sample = jp->max;
    2497          15 :                 jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
    2498          15 :                 jp->max = 0.0;
    2499             :         }
    2500             : 
    2501             :         /* Busytime average and jitter calculation
    2502             :          */
    2503        1166 :         jp = &stats->busy_time_jitter;
    2504        1166 :         jp->avg = jp->avg + draw_times.busy_time * 0.02;
    2505             : 
    2506        1166 :         jitter = draw_times.busy_time - jp->avg;
    2507             : 
    2508        1166 :         if (jitter > jp->max)
    2509           1 :                 jp->max = jitter;
    2510        1166 :         if (jitter < jp->min)
    2511         491 :                 jp->min = jitter;
    2512             : 
    2513        1166 :         jp->counter += dtime;
    2514             : 
    2515        1166 :         if (jp->counter > 0.0) {
    2516          15 :                 jp->counter -= 3.0;
    2517          15 :                 jp->max_sample = jp->max;
    2518          15 :                 jp->min_sample = jp->min;
    2519          15 :                 jp->max = 0.0;
    2520          15 :                 jp->min = 0.0;
    2521             :         }
    2522        1166 : }
    2523             : 
    2524             : 
    2525             : 
    2526             : /****************************************************************************
    2527             :  Input handling
    2528             :  ****************************************************************************/
    2529             : 
    2530        1166 : void Game::processUserInput(VolatileRunFlags *flags,
    2531             :                 GameRunData *runData, f32 dtime)
    2532             : {
    2533             :         // Reset input if window not active or some menu is active
    2534        2332 :         if (device->isWindowActive() == false
    2535        1166 :                         || noMenuActive() == false
    2536        2313 :                         || guienv->hasFocus(gui_chat_console)) {
    2537          19 :                 input->clear();
    2538             :         }
    2539             : 
    2540        1166 :         if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
    2541           0 :                 gui_chat_console->closeConsoleAtOnce();
    2542             :         }
    2543             : 
    2544             :         // Input handler step() (used by the random input generator)
    2545        1166 :         input->step(dtime);
    2546             : 
    2547             : #ifdef HAVE_TOUCHSCREENGUI
    2548             : 
    2549             :         if (g_touchscreengui) {
    2550             :                 g_touchscreengui->step(dtime);
    2551             :         }
    2552             : 
    2553             : #endif
    2554             : #ifdef __ANDROID__
    2555             : 
    2556             :         if (current_formspec != 0)
    2557             :                 current_formspec->getAndroidUIInput();
    2558             : 
    2559             : #endif
    2560             : 
    2561             :         // Increase timer for double tap of "keymap_jump"
    2562        1166 :         if (m_cache_doubletap_jump && runData->jump_timer <= 0.2)
    2563           0 :                 runData->jump_timer += dtime;
    2564             : 
    2565        1166 :         processKeyboardInput(
    2566             :                         flags,
    2567             :                         &runData->statustext_time,
    2568             :                         &runData->jump_timer,
    2569             :                         &runData->reset_jump_timer,
    2570             :                         &runData->profiler_current_page,
    2571        1166 :                         runData->profiler_max_page);
    2572             : 
    2573        1166 :         processItemSelection(&runData->new_playeritem);
    2574        1166 : }
    2575             : 
    2576             : 
    2577        1166 : void Game::processKeyboardInput(VolatileRunFlags *flags,
    2578             :                 float *statustext_time,
    2579             :                 float *jump_timer,
    2580             :                 bool *reset_jump_timer,
    2581             :                 u32 *profiler_current_page,
    2582             :                 u32 profiler_max_page)
    2583             : {
    2584             : 
    2585             :         //TimeTaker tt("process kybd input", NULL, PRECISION_NANO);
    2586             : 
    2587        1166 :         if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) {
    2588           0 :                 dropSelectedItem();
    2589        1166 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) {
    2590           0 :                 openInventory();
    2591        1166 :         } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
    2592           3 :                 show_pause_menu(&current_formspec, client, gamedef, texture_src, device,
    2593           4 :                                 simple_singleplayer_mode);
    2594        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) {
    2595           0 :                 show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
    2596           0 :                                 client, "");
    2597        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) {
    2598           0 :                 show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
    2599           0 :                                 client, "/");
    2600        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) {
    2601           0 :                 openConsole();
    2602        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) {
    2603           0 :                 toggleFreeMove(statustext_time);
    2604        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) {
    2605           0 :                 toggleFreeMoveAlt(statustext_time, jump_timer);
    2606           0 :                 *reset_jump_timer = true;
    2607        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FASTMOVE])) {
    2608           0 :                 toggleFast(statustext_time);
    2609        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) {
    2610           0 :                 toggleNoClip(statustext_time);
    2611        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) {
    2612           0 :                 toggleCinematic(statustext_time);
    2613        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) {
    2614           0 :                 client->makeScreenshot(device);
    2615        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) {
    2616           0 :                 toggleHud(statustext_time, &flags->show_hud);
    2617        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_MINIMAP])) {
    2618           0 :                 toggleMinimap(statustext_time, &flags->show_minimap, &flags->show_hud,
    2619           0 :                         input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]));
    2620        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) {
    2621           0 :                 toggleChat(statustext_time, &flags->show_chat);
    2622        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_FORCE_FOG_OFF])) {
    2623           0 :                 toggleFog(statustext_time, &flags->force_fog_off);
    2624        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_UPDATE_CAMERA])) {
    2625           0 :                 toggleUpdateCamera(statustext_time, &flags->disable_camera_update);
    2626        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_DEBUG])) {
    2627           0 :                 toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph);
    2628        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_PROFILER])) {
    2629           0 :                 toggleProfiler(statustext_time, profiler_current_page, profiler_max_page);
    2630        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INCREASE_VIEWING_RANGE])) {
    2631           0 :                 increaseViewRange(statustext_time);
    2632        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DECREASE_VIEWING_RANGE])) {
    2633           0 :                 decreaseViewRange(statustext_time);
    2634        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) {
    2635           0 :                 toggleFullViewRange(statustext_time);
    2636        1165 :         } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT]))
    2637           0 :                 quicktune->next();
    2638        1165 :         else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV]))
    2639           0 :                 quicktune->prev();
    2640        1165 :         else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC]))
    2641           0 :                 quicktune->inc();
    2642        1165 :         else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC]))
    2643           0 :                 quicktune->dec();
    2644        1165 :         else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) {
    2645             :                 // Print debug stacks
    2646           0 :                 dstream << "-----------------------------------------"
    2647           0 :                         << std::endl;
    2648           0 :                 dstream << DTIME << "Printing debug stacks:" << std::endl;
    2649           0 :                 dstream << "-----------------------------------------"
    2650           0 :                         << std::endl;
    2651           0 :                 debug_stacks_print();
    2652             :         }
    2653             : 
    2654        1166 :         if (!input->isKeyDown(getKeySetting("keymap_jump")) && *reset_jump_timer) {
    2655           0 :                 *reset_jump_timer = false;
    2656           0 :                 *jump_timer = 0.0;
    2657             :         }
    2658             : 
    2659             :         //tt.stop();
    2660             : 
    2661        1166 :         if (quicktune->hasMessage()) {
    2662           0 :                 std::string msg = quicktune->getMessage();
    2663           0 :                 statustext = narrow_to_wide(msg);
    2664           0 :                 *statustext_time = 0;
    2665             :         }
    2666        1166 : }
    2667             : 
    2668             : 
    2669        1166 : void Game::processItemSelection(u16 *new_playeritem)
    2670             : {
    2671        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    2672             : 
    2673             :         /* Item selection using mouse wheel
    2674             :          */
    2675        1166 :         *new_playeritem = client->getPlayerItem();
    2676             : 
    2677        1166 :         s32 wheel = input->getMouseWheel();
    2678        1166 :         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
    2679             :                                  player->hud_hotbar_itemcount - 1);
    2680             : 
    2681        1166 :         if (wheel < 0)
    2682          17 :                 *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
    2683        1149 :         else if (wheel > 0)
    2684           3 :                 *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
    2685             :         // else wheel == 0
    2686             : 
    2687             : 
    2688             :         /* Item selection using keyboard
    2689             :          */
    2690       12826 :         for (u16 i = 0; i < 10; i++) {
    2691             :                 static const KeyPress *item_keys[10] = {
    2692             :                         NumberKey + 1, NumberKey + 2, NumberKey + 3, NumberKey + 4,
    2693             :                         NumberKey + 5, NumberKey + 6, NumberKey + 7, NumberKey + 8,
    2694             :                         NumberKey + 9, NumberKey + 0,
    2695             :                 };
    2696             : 
    2697       11660 :                 if (input->wasKeyDown(*item_keys[i])) {
    2698           0 :                         if (i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount) {
    2699           0 :                                 *new_playeritem = i;
    2700           0 :                                 infostream << "Selected item: " << new_playeritem << std::endl;
    2701             :                         }
    2702           0 :                         break;
    2703             :                 }
    2704             :         }
    2705        1166 : }
    2706             : 
    2707             : 
    2708           0 : void Game::dropSelectedItem()
    2709             : {
    2710           0 :         IDropAction *a = new IDropAction();
    2711           0 :         a->count = 0;
    2712           0 :         a->from_inv.setCurrentPlayer();
    2713           0 :         a->from_list = "main";
    2714           0 :         a->from_i = client->getPlayerItem();
    2715           0 :         client->inventoryAction(a);
    2716           0 : }
    2717             : 
    2718             : 
    2719           0 : void Game::openInventory()
    2720             : {
    2721             :         /*
    2722             :          * Don't permit to open inventory is CAO or player doesn't exists.
    2723             :          * This prevent showing an empty inventory at player load
    2724             :          */
    2725             : 
    2726           0 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    2727           0 :         if (player == NULL || player->getCAO() == NULL)
    2728           0 :                 return;
    2729             : 
    2730           0 :         infostream << "the_game: " << "Launching inventory" << std::endl;
    2731             : 
    2732           0 :         PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
    2733           0 :         TextDest *txt_dst = new TextDestPlayerInventory(client);
    2734             : 
    2735           0 :         create_formspec_menu(&current_formspec, client, gamedef, texture_src,
    2736           0 :                         device, fs_src, txt_dst, client);
    2737             : 
    2738           0 :         InventoryLocation inventoryloc;
    2739           0 :         inventoryloc.setCurrentPlayer();
    2740           0 :         current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
    2741             : }
    2742             : 
    2743             : 
    2744           0 : void Game::openConsole()
    2745             : {
    2746           0 :         if (!gui_chat_console->isOpenInhibited()) {
    2747             :                 // Open up to over half of the screen
    2748           0 :                 gui_chat_console->openConsole(0.6);
    2749           0 :                 guienv->setFocus(gui_chat_console);
    2750             :         }
    2751           0 : }
    2752             : 
    2753             : 
    2754           0 : void Game::toggleFreeMove(float *statustext_time)
    2755             : {
    2756             :         static const wchar_t *msg[] = { L"free_move disabled", L"free_move enabled" };
    2757             : 
    2758           0 :         bool free_move = !g_settings->getBool("free_move");
    2759           0 :         g_settings->set("free_move", bool_to_cstr(free_move));
    2760             : 
    2761           0 :         *statustext_time = 0;
    2762           0 :         statustext = msg[free_move];
    2763           0 :         if (free_move && !client->checkPrivilege("fly"))
    2764           0 :                 statustext += L" (note: no 'fly' privilege)";
    2765           0 : }
    2766             : 
    2767             : 
    2768           0 : void Game::toggleFreeMoveAlt(float *statustext_time, float *jump_timer)
    2769             : {
    2770           0 :         if (m_cache_doubletap_jump && *jump_timer < 0.2f)
    2771           0 :                 toggleFreeMove(statustext_time);
    2772           0 : }
    2773             : 
    2774             : 
    2775           0 : void Game::toggleFast(float *statustext_time)
    2776             : {
    2777             :         static const wchar_t *msg[] = { L"fast_move disabled", L"fast_move enabled" };
    2778           0 :         bool fast_move = !g_settings->getBool("fast_move");
    2779           0 :         g_settings->set("fast_move", bool_to_cstr(fast_move));
    2780             : 
    2781           0 :         *statustext_time = 0;
    2782           0 :         statustext = msg[fast_move];
    2783             : 
    2784           0 :         bool has_fast_privs = client->checkPrivilege("fast");
    2785             : 
    2786           0 :         if (fast_move && !has_fast_privs)
    2787           0 :                 statustext += L" (note: no 'fast' privilege)";
    2788             : 
    2789             : #ifdef __ANDROID__
    2790             :         m_cache_hold_aux1 = fast_move && has_fast_privs;
    2791             : #endif
    2792           0 : }
    2793             : 
    2794             : 
    2795           0 : void Game::toggleNoClip(float *statustext_time)
    2796             : {
    2797             :         static const wchar_t *msg[] = { L"noclip disabled", L"noclip enabled" };
    2798           0 :         bool noclip = !g_settings->getBool("noclip");
    2799           0 :         g_settings->set("noclip", bool_to_cstr(noclip));
    2800             : 
    2801           0 :         *statustext_time = 0;
    2802           0 :         statustext = msg[noclip];
    2803             : 
    2804           0 :         if (noclip && !client->checkPrivilege("noclip"))
    2805           0 :                 statustext += L" (note: no 'noclip' privilege)";
    2806           0 : }
    2807             : 
    2808           0 : void Game::toggleCinematic(float *statustext_time)
    2809             : {
    2810             :         static const wchar_t *msg[] = { L"cinematic disabled", L"cinematic enabled" };
    2811           0 :         bool cinematic = !g_settings->getBool("cinematic");
    2812           0 :         g_settings->set("cinematic", bool_to_cstr(cinematic));
    2813             : 
    2814           0 :         *statustext_time = 0;
    2815           0 :         statustext = msg[cinematic];
    2816           0 : }
    2817             : 
    2818             : 
    2819           0 : void Game::toggleChat(float *statustext_time, bool *flag)
    2820             : {
    2821             :         static const wchar_t *msg[] = { L"Chat hidden", L"Chat shown" };
    2822             : 
    2823           0 :         *flag = !*flag;
    2824           0 :         *statustext_time = 0;
    2825           0 :         statustext = msg[*flag];
    2826           0 : }
    2827             : 
    2828             : 
    2829           0 : void Game::toggleHud(float *statustext_time, bool *flag)
    2830             : {
    2831             :         static const wchar_t *msg[] = { L"HUD hidden", L"HUD shown" };
    2832             : 
    2833           0 :         *flag = !*flag;
    2834           0 :         *statustext_time = 0;
    2835           0 :         statustext = msg[*flag];
    2836           0 :         if (g_settings->getBool("enable_node_highlighting"))
    2837           0 :                 client->setHighlighted(client->getHighlighted(), *flag);
    2838           0 : }
    2839             : 
    2840           0 : void Game::toggleMinimap(float *statustext_time, bool *flag, bool *show_hud, bool shift_pressed)
    2841             : {
    2842           0 :         if (*show_hud && g_settings->getBool("enable_minimap")) {
    2843           0 :                 if (shift_pressed) {
    2844           0 :                         mapper->toggleMinimapShape();
    2845           0 :                         return;
    2846             :                 }
    2847           0 :                 MinimapMode mode = mapper->getMinimapMode();
    2848           0 :                 mode = (MinimapMode)((int)(mode) + 1);
    2849           0 :                 *flag = true;
    2850           0 :                 switch (mode) {
    2851             :                         case MINIMAP_MODE_SURFACEx1:
    2852           0 :                                 statustext = L"Minimap in surface mode, Zoom x1";
    2853           0 :                                 break;
    2854             :                         case MINIMAP_MODE_SURFACEx2:
    2855           0 :                                 statustext = L"Minimap in surface mode, Zoom x2";
    2856           0 :                                 break;
    2857             :                         case MINIMAP_MODE_SURFACEx4:
    2858           0 :                                 statustext = L"Minimap in surface mode, Zoom x4";
    2859           0 :                                 break;
    2860             :                         case MINIMAP_MODE_RADARx1:
    2861           0 :                                 statustext = L"Minimap in radar mode, Zoom x1";
    2862           0 :                                 break;
    2863             :                         case MINIMAP_MODE_RADARx2:
    2864           0 :                                 statustext = L"Minimap in radar mode, Zoom x2";
    2865           0 :                                 break;
    2866             :                         case MINIMAP_MODE_RADARx4:
    2867           0 :                                 statustext = L"Minimap in radar mode, Zoom x4";
    2868           0 :                                 break;
    2869             :                         default:
    2870           0 :                                 mode = MINIMAP_MODE_OFF;
    2871           0 :                                 *flag = false;
    2872           0 :                                 statustext = L"Minimap hidden";
    2873             :                 }
    2874           0 :                 *statustext_time = 0;
    2875           0 :                 mapper->setMinimapMode(mode);
    2876             :         }
    2877             : }
    2878             : 
    2879           0 : void Game::toggleFog(float *statustext_time, bool *flag)
    2880             : {
    2881             :         static const wchar_t *msg[] = { L"Fog enabled", L"Fog disabled" };
    2882             : 
    2883           0 :         *flag = !*flag;
    2884           0 :         *statustext_time = 0;
    2885           0 :         statustext = msg[*flag];
    2886           0 : }
    2887             : 
    2888             : 
    2889           0 : void Game::toggleDebug(float *statustext_time, bool *show_debug,
    2890             :                 bool *show_profiler_graph)
    2891             : {
    2892             :         // Initial / 3x toggle: Chat only
    2893             :         // 1x toggle: Debug text with chat
    2894             :         // 2x toggle: Debug text with profiler graph
    2895           0 :         if (!*show_debug) {
    2896           0 :                 *show_debug = true;
    2897           0 :                 *show_profiler_graph = false;
    2898           0 :                 statustext = L"Debug info shown";
    2899           0 :         } else if (*show_profiler_graph) {
    2900           0 :                 *show_debug = false;
    2901           0 :                 *show_profiler_graph = false;
    2902           0 :                 statustext = L"Debug info and profiler graph hidden";
    2903             :         } else {
    2904           0 :                 *show_profiler_graph = true;
    2905           0 :                 statustext = L"Profiler graph shown";
    2906             :         }
    2907           0 :         *statustext_time = 0;
    2908           0 : }
    2909             : 
    2910             : 
    2911           0 : void Game::toggleUpdateCamera(float *statustext_time, bool *flag)
    2912             : {
    2913             :         static const wchar_t *msg[] = {
    2914             :                 L"Camera update enabled",
    2915             :                 L"Camera update disabled"
    2916             :         };
    2917             : 
    2918           0 :         *flag = !*flag;
    2919           0 :         *statustext_time = 0;
    2920           0 :         statustext = msg[*flag];
    2921           0 : }
    2922             : 
    2923             : 
    2924           0 : void Game::toggleProfiler(float *statustext_time, u32 *profiler_current_page,
    2925             :                 u32 profiler_max_page)
    2926             : {
    2927           0 :         *profiler_current_page = (*profiler_current_page + 1) % (profiler_max_page + 1);
    2928             : 
    2929             :         // FIXME: This updates the profiler with incomplete values
    2930           0 :         update_profiler_gui(guitext_profiler, g_fontengine, *profiler_current_page,
    2931           0 :                         profiler_max_page, driver->getScreenSize().Height);
    2932             : 
    2933           0 :         if (*profiler_current_page != 0) {
    2934           0 :                 std::wstringstream sstr;
    2935           0 :                 sstr << "Profiler shown (page " << *profiler_current_page
    2936           0 :                      << " of " << profiler_max_page << ")";
    2937           0 :                 statustext = sstr.str();
    2938             :         } else {
    2939           0 :                 statustext = L"Profiler hidden";
    2940             :         }
    2941           0 :         *statustext_time = 0;
    2942           0 : }
    2943             : 
    2944             : 
    2945           0 : void Game::increaseViewRange(float *statustext_time)
    2946             : {
    2947           0 :         s16 range = g_settings->getS16("viewing_range_nodes_min");
    2948           0 :         s16 range_new = range + 10;
    2949           0 :         g_settings->set("viewing_range_nodes_min", itos(range_new));
    2950           0 :         statustext = narrow_to_wide("Minimum viewing range changed to "
    2951           0 :                         + itos(range_new));
    2952           0 :         *statustext_time = 0;
    2953           0 : }
    2954             : 
    2955             : 
    2956           0 : void Game::decreaseViewRange(float *statustext_time)
    2957             : {
    2958           0 :         s16 range = g_settings->getS16("viewing_range_nodes_min");
    2959           0 :         s16 range_new = range - 10;
    2960             : 
    2961           0 :         if (range_new < 0)
    2962           0 :                 range_new = range;
    2963             : 
    2964           0 :         g_settings->set("viewing_range_nodes_min", itos(range_new));
    2965           0 :         statustext = narrow_to_wide("Minimum viewing range changed to "
    2966           0 :                         + itos(range_new));
    2967           0 :         *statustext_time = 0;
    2968           0 : }
    2969             : 
    2970             : 
    2971           0 : void Game::toggleFullViewRange(float *statustext_time)
    2972             : {
    2973             :         static const wchar_t *msg[] = {
    2974             :                 L"Disabled full viewing range",
    2975             :                 L"Enabled full viewing range"
    2976             :         };
    2977             : 
    2978           0 :         draw_control->range_all = !draw_control->range_all;
    2979           0 :         infostream << msg[draw_control->range_all] << std::endl;
    2980           0 :         statustext = msg[draw_control->range_all];
    2981           0 :         *statustext_time = 0;
    2982           0 : }
    2983             : 
    2984             : 
    2985        1166 : void Game::updateCameraDirection(CameraOrientation *cam,
    2986             :                 VolatileRunFlags *flags)
    2987             : {
    2988        1166 :         if ((device->isWindowActive() && noMenuActive()) || random_input) {
    2989             : 
    2990             : #ifndef __ANDROID__
    2991        1146 :                 if (!random_input) {
    2992             :                         // Mac OSX gets upset if this is set every frame
    2993        1146 :                         if (device->getCursorControl()->isVisible())
    2994           1 :                                 device->getCursorControl()->setVisible(false);
    2995             :                 }
    2996             : #endif
    2997             : 
    2998        1146 :                 if (flags->first_loop_after_window_activation)
    2999           1 :                         flags->first_loop_after_window_activation = false;
    3000             :                 else
    3001        1145 :                         updateCameraOrientation(cam, *flags);
    3002             : 
    3003        2292 :                 input->setMousePos((driver->getScreenSize().Width / 2),
    3004        2292 :                                 (driver->getScreenSize().Height / 2));
    3005             :         } else {
    3006             : 
    3007             : #ifndef ANDROID
    3008             :                 // Mac OSX gets upset if this is set every frame
    3009          20 :                 if (device->getCursorControl()->isVisible() == false)
    3010           1 :                         device->getCursorControl()->setVisible(true);
    3011             : #endif
    3012             : 
    3013          20 :                 if (!flags->first_loop_after_window_activation)
    3014           1 :                         flags->first_loop_after_window_activation = true;
    3015             : 
    3016             :         }
    3017        1166 : }
    3018             : 
    3019             : 
    3020        1145 : void Game::updateCameraOrientation(CameraOrientation *cam,
    3021             :                 const VolatileRunFlags &flags)
    3022             : {
    3023             : #ifdef HAVE_TOUCHSCREENGUI
    3024             :         if (g_touchscreengui) {
    3025             :                 cam->camera_yaw   = g_touchscreengui->getYaw();
    3026             :                 cam->camera_pitch = g_touchscreengui->getPitch();
    3027             :         } else {
    3028             : #endif
    3029        1145 :                 s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
    3030        1145 :                 s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
    3031             : 
    3032        2290 :                 if (flags.invert_mouse
    3033        1145 :                                 || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
    3034           0 :                         dy = -dy;
    3035             :                 }
    3036             : 
    3037        1145 :                 cam->camera_yaw   -= dx * m_cache_mouse_sensitivity;
    3038        1145 :                 cam->camera_pitch += dy * m_cache_mouse_sensitivity;
    3039             : 
    3040             : #ifdef HAVE_TOUCHSCREENGUI
    3041             :         }
    3042             : #endif
    3043             : 
    3044        1145 :         cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
    3045        1145 : }
    3046             : 
    3047             : 
    3048        1166 : void Game::updatePlayerControl(const CameraOrientation &cam)
    3049             : {
    3050             :         //TimeTaker tt("update player control", NULL, PRECISION_NANO);
    3051             : 
    3052             :         PlayerControl control(
    3053        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]),
    3054        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]),
    3055        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]),
    3056        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]),
    3057        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]),
    3058        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]),
    3059        1166 :                 input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]),
    3060        1166 :                 input->getLeftState(),
    3061        1166 :                 input->getRightState(),
    3062        1166 :                 cam.camera_pitch,
    3063        1166 :                 cam.camera_yaw
    3064       10494 :         );
    3065             : 
    3066             :         u32 keypress_bits =
    3067        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD])  & 0x1) << 0) |
    3068        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
    3069        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT])     & 0x1) << 2) |
    3070        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT])    & 0x1) << 3) |
    3071        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])     & 0x1) << 4) |
    3072        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
    3073        2332 :                         ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK])    & 0x1) << 6) |
    3074        1166 :                         ( (u32)(input->getLeftState()                                        & 0x1) << 7) |
    3075        1166 :                         ( (u32)(input->getRightState()                                       & 0x1) << 8
    3076        1166 :                 );
    3077             : 
    3078             : #ifdef ANDROID
    3079             :         /* For Android, simulate holding down AUX1 (fast move) if the user has
    3080             :          * the fast_move setting toggled on. If there is an aux1 key defined for
    3081             :          * Android then its meaning is inverted (i.e. holding aux1 means walk and
    3082             :          * not fast)
    3083             :          */
    3084             :         if (m_cache_hold_aux1) {
    3085             :                 control.aux1 = control.aux1 ^ true;
    3086             :                 keypress_bits ^= ((u32)(1U << 5));
    3087             :         }
    3088             : #endif
    3089             : 
    3090        1166 :         client->setPlayerControl(control);
    3091        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3092        1166 :         player->keyPressed = keypress_bits;
    3093             : 
    3094             :         //tt.stop();
    3095        1166 : }
    3096             : 
    3097             : 
    3098        1166 : inline void Game::step(f32 *dtime)
    3099             : {
    3100             :         bool can_be_and_is_paused =
    3101        1166 :                         (simple_singleplayer_mode && g_menumgr.pausesGame());
    3102             : 
    3103        1166 :         if (can_be_and_is_paused) {     // This is for a singleplayer server
    3104           0 :                 *dtime = 0;             // No time passes
    3105             :         } else {
    3106        1166 :                 if (server != NULL) {
    3107             :                         //TimeTaker timer("server->step(dtime)");
    3108           0 :                         server->step(*dtime);
    3109             :                 }
    3110             : 
    3111             :                 //TimeTaker timer("client.step(dtime)");
    3112        1166 :                 client->step(*dtime);
    3113             :         }
    3114        1166 : }
    3115             : 
    3116             : 
    3117        1166 : void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
    3118             : {
    3119        1166 :         ClientEvent event = client->getClientEvent();
    3120             : 
    3121        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3122             : 
    3123        1220 :         for ( ; event.type != CE_NONE; event = client->getClientEvent()) {
    3124             : 
    3125          27 :                 if (event.type == CE_PLAYER_DAMAGE &&
    3126           0 :                                 client->getHP() != 0) {
    3127             :                         //u16 damage = event.player_damage.amount;
    3128             :                         //infostream<<"Player damage: "<<damage<<std::endl;
    3129             : 
    3130           0 :                         *damage_flash += 100.0;
    3131           0 :                         *damage_flash += 8.0 * event.player_damage.amount;
    3132             : 
    3133           0 :                         player->hurt_tilt_timer = 1.5;
    3134           0 :                         player->hurt_tilt_strength = event.player_damage.amount / 4;
    3135           0 :                         player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
    3136             : 
    3137           0 :                         MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
    3138           0 :                         gamedef->event()->put(e);
    3139          27 :                 } else if (event.type == CE_PLAYER_FORCE_MOVE) {
    3140           1 :                         cam->camera_yaw = event.player_force_move.yaw;
    3141           1 :                         cam->camera_pitch = event.player_force_move.pitch;
    3142          26 :                 } else if (event.type == CE_DEATHSCREEN) {
    3143           0 :                         show_deathscreen(&current_formspec, client, gamedef, texture_src,
    3144           0 :                                          device, client);
    3145             : 
    3146           0 :                         chat_backend->addMessage(L"", L"You died.");
    3147             : 
    3148             :                         /* Handle visualization */
    3149           0 :                         *damage_flash = 0;
    3150           0 :                         player->hurt_tilt_timer = 0;
    3151           0 :                         player->hurt_tilt_strength = 0;
    3152             : 
    3153          26 :                 } else if (event.type == CE_SHOW_FORMSPEC) {
    3154             :                         FormspecFormSource *fs_src =
    3155           0 :                                 new FormspecFormSource(*(event.show_formspec.formspec));
    3156             :                         TextDestPlayerInventory *txt_dst =
    3157           0 :                                 new TextDestPlayerInventory(client, *(event.show_formspec.formname));
    3158             : 
    3159           0 :                         create_formspec_menu(&current_formspec, client, gamedef,
    3160           0 :                                              texture_src, device, fs_src, txt_dst, client);
    3161             : 
    3162           0 :                         delete(event.show_formspec.formspec);
    3163           0 :                         delete(event.show_formspec.formname);
    3164          52 :                 } else if ((event.type == CE_SPAWN_PARTICLE) ||
    3165          43 :                                 (event.type == CE_ADD_PARTICLESPAWNER) ||
    3166          17 :                                 (event.type == CE_DELETE_PARTICLESPAWNER)) {
    3167           9 :                         client->getParticleManager()->handleParticleEvent(&event, gamedef,
    3168           9 :                                         smgr, player);
    3169          17 :                 } else if (event.type == CE_HUDADD) {
    3170           2 :                         u32 id = event.hudadd.id;
    3171             : 
    3172           2 :                         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3173           2 :                         HudElement *e = player->getHud(id);
    3174             : 
    3175           2 :                         if (e != NULL) {
    3176           0 :                                 delete event.hudadd.pos;
    3177           0 :                                 delete event.hudadd.name;
    3178           0 :                                 delete event.hudadd.scale;
    3179           0 :                                 delete event.hudadd.text;
    3180           0 :                                 delete event.hudadd.align;
    3181           0 :                                 delete event.hudadd.offset;
    3182           0 :                                 delete event.hudadd.world_pos;
    3183           0 :                                 delete event.hudadd.size;
    3184           0 :                                 continue;
    3185             :                         }
    3186             : 
    3187           2 :                         e = new HudElement;
    3188           2 :                         e->type   = (HudElementType)event.hudadd.type;
    3189           2 :                         e->pos    = *event.hudadd.pos;
    3190           2 :                         e->name   = *event.hudadd.name;
    3191           2 :                         e->scale  = *event.hudadd.scale;
    3192           2 :                         e->text   = *event.hudadd.text;
    3193           2 :                         e->number = event.hudadd.number;
    3194           2 :                         e->item   = event.hudadd.item;
    3195           2 :                         e->dir    = event.hudadd.dir;
    3196           2 :                         e->align  = *event.hudadd.align;
    3197           2 :                         e->offset = *event.hudadd.offset;
    3198           2 :                         e->world_pos = *event.hudadd.world_pos;
    3199           2 :                         e->size = *event.hudadd.size;
    3200             : 
    3201           2 :                         u32 new_id = player->addHud(e);
    3202             :                         //if this isn't true our huds aren't consistent
    3203           2 :                         sanity_check(new_id == id);
    3204             : 
    3205           2 :                         delete event.hudadd.pos;
    3206           2 :                         delete event.hudadd.name;
    3207           2 :                         delete event.hudadd.scale;
    3208           2 :                         delete event.hudadd.text;
    3209           2 :                         delete event.hudadd.align;
    3210           2 :                         delete event.hudadd.offset;
    3211           2 :                         delete event.hudadd.world_pos;
    3212           2 :                         delete event.hudadd.size;
    3213          15 :                 } else if (event.type == CE_HUDRM) {
    3214           0 :                         HudElement *e = player->removeHud(event.hudrm.id);
    3215             : 
    3216           0 :                         if (e != NULL)
    3217           0 :                                 delete(e);
    3218          15 :                 } else if (event.type == CE_HUDCHANGE) {
    3219          15 :                         u32 id = event.hudchange.id;
    3220          15 :                         HudElement *e = player->getHud(id);
    3221             : 
    3222          15 :                         if (e == NULL) {
    3223           2 :                                 delete event.hudchange.v3fdata;
    3224           2 :                                 delete event.hudchange.v2fdata;
    3225           2 :                                 delete event.hudchange.sdata;
    3226           2 :                                 delete event.hudchange.v2s32data;
    3227           2 :                                 continue;
    3228             :                         }
    3229             : 
    3230          13 :                         switch (event.hudchange.stat) {
    3231             :                         case HUD_STAT_POS:
    3232           0 :                                 e->pos = *event.hudchange.v2fdata;
    3233           0 :                                 break;
    3234             : 
    3235             :                         case HUD_STAT_NAME:
    3236           0 :                                 e->name = *event.hudchange.sdata;
    3237           0 :                                 break;
    3238             : 
    3239             :                         case HUD_STAT_SCALE:
    3240           0 :                                 e->scale = *event.hudchange.v2fdata;
    3241           0 :                                 break;
    3242             : 
    3243             :                         case HUD_STAT_TEXT:
    3244          13 :                                 e->text = *event.hudchange.sdata;
    3245          13 :                                 break;
    3246             : 
    3247             :                         case HUD_STAT_NUMBER:
    3248           0 :                                 e->number = event.hudchange.data;
    3249           0 :                                 break;
    3250             : 
    3251             :                         case HUD_STAT_ITEM:
    3252           0 :                                 e->item = event.hudchange.data;
    3253           0 :                                 break;
    3254             : 
    3255             :                         case HUD_STAT_DIR:
    3256           0 :                                 e->dir = event.hudchange.data;
    3257           0 :                                 break;
    3258             : 
    3259             :                         case HUD_STAT_ALIGN:
    3260           0 :                                 e->align = *event.hudchange.v2fdata;
    3261           0 :                                 break;
    3262             : 
    3263             :                         case HUD_STAT_OFFSET:
    3264           0 :                                 e->offset = *event.hudchange.v2fdata;
    3265           0 :                                 break;
    3266             : 
    3267             :                         case HUD_STAT_WORLD_POS:
    3268           0 :                                 e->world_pos = *event.hudchange.v3fdata;
    3269           0 :                                 break;
    3270             : 
    3271             :                         case HUD_STAT_SIZE:
    3272           0 :                                 e->size = *event.hudchange.v2s32data;
    3273           0 :                                 break;
    3274             :                         }
    3275             : 
    3276          13 :                         delete event.hudchange.v3fdata;
    3277          13 :                         delete event.hudchange.v2fdata;
    3278          13 :                         delete event.hudchange.sdata;
    3279          13 :                         delete event.hudchange.v2s32data;
    3280           0 :                 } else if (event.type == CE_SET_SKY) {
    3281           0 :                         sky->setVisible(false);
    3282             : 
    3283           0 :                         if (skybox) {
    3284           0 :                                 skybox->remove();
    3285           0 :                                 skybox = NULL;
    3286             :                         }
    3287             : 
    3288             :                         // Handle according to type
    3289           0 :                         if (*event.set_sky.type == "regular") {
    3290           0 :                                 sky->setVisible(true);
    3291           0 :                         } else if (*event.set_sky.type == "skybox" &&
    3292           0 :                                         event.set_sky.params->size() == 6) {
    3293           0 :                                 sky->setFallbackBgColor(*event.set_sky.bgcolor);
    3294           0 :                                 skybox = smgr->addSkyBoxSceneNode(
    3295           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[0]),
    3296           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[1]),
    3297           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[2]),
    3298           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[3]),
    3299           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[4]),
    3300           0 :                                                  texture_src->getTextureForMesh((*event.set_sky.params)[5]));
    3301             :                         }
    3302             :                         // Handle everything else as plain color
    3303             :                         else {
    3304           0 :                                 if (*event.set_sky.type != "plain")
    3305           0 :                                         infostream << "Unknown sky type: "
    3306           0 :                                                    << (*event.set_sky.type) << std::endl;
    3307             : 
    3308           0 :                                 sky->setFallbackBgColor(*event.set_sky.bgcolor);
    3309             :                         }
    3310             : 
    3311           0 :                         delete event.set_sky.bgcolor;
    3312           0 :                         delete event.set_sky.type;
    3313           0 :                         delete event.set_sky.params;
    3314           0 :                 } else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
    3315           0 :                         bool enable = event.override_day_night_ratio.do_override;
    3316           0 :                         u32 value = event.override_day_night_ratio.ratio_f * 1000;
    3317           0 :                         client->getEnv().setDayNightRatioOverride(enable, value);
    3318             :                 }
    3319             :         }
    3320        1166 : }
    3321             : 
    3322             : 
    3323        1166 : void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time,
    3324             :                 f32 dtime, float time_from_last_punch)
    3325             : {
    3326        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3327             : 
    3328             :         /*
    3329             :                 For interaction purposes, get info about the held item
    3330             :                 - What item is it?
    3331             :                 - Is it a usable item?
    3332             :                 - Can it point to liquids?
    3333             :         */
    3334        2332 :         ItemStack playeritem;
    3335             :         {
    3336        1166 :                 InventoryList *mlist = local_inventory->getList("main");
    3337             : 
    3338        1166 :                 if (mlist && client->getPlayerItem() < mlist->getSize())
    3339        1165 :                         playeritem = mlist->getItem(client->getPlayerItem());
    3340             :         }
    3341             : 
    3342             :         ToolCapabilities playeritem_toolcap =
    3343        2332 :                 playeritem.getToolCapabilities(itemdef_manager);
    3344             : 
    3345        1166 :         v3s16 old_camera_offset = camera->getOffset();
    3346             : 
    3347        1166 :         if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) {
    3348           0 :                 GenericCAO *playercao = player->getCAO();
    3349             : 
    3350             :                 // If playercao not loaded, don't change camera
    3351           0 :                 if (playercao == NULL)
    3352           0 :                         return;
    3353             : 
    3354           0 :                 camera->toggleCameraMode();
    3355             : 
    3356           0 :                 playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
    3357           0 :                 playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
    3358             :         }
    3359             : 
    3360        1166 :         float full_punch_interval = playeritem_toolcap.full_punch_interval;
    3361        1166 :         float tool_reload_ratio = time_from_last_punch / full_punch_interval;
    3362             : 
    3363        1166 :         tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
    3364        1166 :         camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio,
    3365        2332 :                       client->getEnv());
    3366        1166 :         camera->step(dtime);
    3367             : 
    3368        1166 :         v3f camera_position = camera->getPosition();
    3369        1166 :         v3f camera_direction = camera->getDirection();
    3370        1166 :         f32 camera_fov = camera->getFovMax();
    3371        1166 :         v3s16 camera_offset = camera->getOffset();
    3372             : 
    3373        1166 :         flags->camera_offset_changed = (camera_offset != old_camera_offset);
    3374             : 
    3375        1166 :         if (!flags->disable_camera_update) {
    3376        2332 :                 client->getEnv().getClientMap().updateCamera(camera_position,
    3377        1166 :                                 camera_direction, camera_fov, camera_offset);
    3378             : 
    3379        1166 :                 if (flags->camera_offset_changed) {
    3380           0 :                         client->updateCameraOffset(camera_offset);
    3381           0 :                         client->getEnv().updateCameraOffset(camera_offset);
    3382             : 
    3383           0 :                         if (clouds)
    3384           0 :                                 clouds->updateCameraOffset(camera_offset);
    3385             :                 }
    3386             :         }
    3387             : }
    3388             : 
    3389             : 
    3390        1166 : void Game::updateSound(f32 dtime)
    3391             : {
    3392             :         // Update sound listener
    3393        1166 :         v3s16 camera_offset = camera->getOffset();
    3394        5830 :         sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS),
    3395             :                               v3f(0, 0, 0), // velocity
    3396        2332 :                               camera->getDirection(),
    3397        2332 :                               camera->getCameraNode()->getUpVector());
    3398        1166 :         sound->setListenerGain(g_settings->getFloat("sound_volume"));
    3399             : 
    3400             : 
    3401             :         //      Update sound maker
    3402        1166 :         soundmaker->step(dtime);
    3403             : 
    3404        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3405             : 
    3406        1166 :         ClientMap &map = client->getEnv().getClientMap();
    3407        1166 :         MapNode n = map.getNodeNoEx(player->getStandingNodePos());
    3408        1166 :         soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
    3409        1166 : }
    3410             : 
    3411             : 
    3412        1166 : void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
    3413             :                 GameRunData *runData, f32 dtime, bool show_hud, bool show_debug)
    3414             : {
    3415        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3416             : 
    3417        2332 :         ItemStack playeritem;
    3418             :         {
    3419        1166 :                 InventoryList *mlist = local_inventory->getList("main");
    3420             : 
    3421        1166 :                 if (mlist && client->getPlayerItem() < mlist->getSize())
    3422        1165 :                         playeritem = mlist->getItem(client->getPlayerItem());
    3423             :         }
    3424             : 
    3425             :         const ItemDefinition &playeritem_def =
    3426        1166 :                         playeritem.getDefinition(itemdef_manager);
    3427             : 
    3428        1166 :         v3f player_position  = player->getPosition();
    3429        1166 :         v3f camera_position  = camera->getPosition();
    3430        1166 :         v3f camera_direction = camera->getDirection();
    3431        1166 :         v3s16 camera_offset  = camera->getOffset();
    3432             : 
    3433             : 
    3434             :         /*
    3435             :                 Calculate what block is the crosshair pointing to
    3436             :         */
    3437             : 
    3438        1166 :         f32 d = playeritem_def.range; // max. distance
    3439        1166 :         f32 d_hand = itemdef_manager->get("").range;
    3440             : 
    3441        1166 :         if (d < 0 && d_hand >= 0)
    3442           0 :                 d = d_hand;
    3443        1166 :         else if (d < 0)
    3444        1166 :                 d = 4.0;
    3445             : 
    3446        1166 :         core::line3d<f32> shootline;
    3447             : 
    3448        1166 :         if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) {
    3449             : 
    3450        2332 :                 shootline = core::line3d<f32>(camera_position,
    3451        3498 :                                                 camera_position + camera_direction * BS * (d + 1));
    3452             : 
    3453             :         } else {
    3454             :             // prevent player pointing anything in front-view
    3455           0 :                 if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT)
    3456           0 :                         shootline = core::line3d<f32>(0, 0, 0, 0, 0, 0);
    3457             :         }
    3458             : 
    3459             : #ifdef HAVE_TOUCHSCREENGUI
    3460             : 
    3461             :         if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
    3462             :                 shootline = g_touchscreengui->getShootline();
    3463             :                 shootline.start += intToFloat(camera_offset, BS);
    3464             :                 shootline.end += intToFloat(camera_offset, BS);
    3465             :         }
    3466             : 
    3467             : #endif
    3468             : 
    3469             :         PointedThing pointed = getPointedThing(
    3470             :                         // input
    3471             :                         client, player_position, camera_direction,
    3472             :                         camera_position, shootline, d,
    3473        1166 :                         playeritem_def.liquids_pointable,
    3474        1166 :                         !runData->ldown_for_dig,
    3475             :                         camera_offset,
    3476             :                         // output
    3477             :                         highlight_boxes,
    3478        3498 :                         runData->selected_object);
    3479             : 
    3480        1166 :         if (pointed != runData->pointed_old) {
    3481          57 :                 infostream << "Pointing at " << pointed.dump() << std::endl;
    3482             : 
    3483          57 :                 if (m_cache_enable_node_highlighting) {
    3484          57 :                         if (pointed.type == POINTEDTHING_NODE) {
    3485          42 :                                 client->setHighlighted(pointed.node_undersurface, show_hud);
    3486             :                         } else {
    3487          15 :                                 client->setHighlighted(pointed.node_undersurface, false);
    3488             :                         }
    3489             :                 }
    3490             :         }
    3491             : 
    3492             :         /*
    3493             :                 Stop digging when
    3494             :                 - releasing left mouse button
    3495             :                 - pointing away from node
    3496             :         */
    3497        1166 :         if (runData->digging) {
    3498           0 :                 if (input->getLeftReleased()) {
    3499           0 :                         infostream << "Left button released"
    3500           0 :                                    << " (stopped digging)" << std::endl;
    3501           0 :                         runData->digging = false;
    3502           0 :                 } else if (pointed != runData->pointed_old) {
    3503           0 :                         if (pointed.type == POINTEDTHING_NODE
    3504           0 :                                         && runData->pointed_old.type == POINTEDTHING_NODE
    3505           0 :                                         && pointed.node_undersurface
    3506           0 :                                                         == runData->pointed_old.node_undersurface) {
    3507             :                                 // Still pointing to the same node, but a different face.
    3508             :                                 // Don't reset.
    3509             :                         } else {
    3510           0 :                                 infostream << "Pointing away from node"
    3511           0 :                                            << " (stopped digging)" << std::endl;
    3512           0 :                                 runData->digging = false;
    3513             :                         }
    3514             :                 }
    3515             : 
    3516           0 :                 if (!runData->digging) {
    3517           0 :                         client->interact(1, runData->pointed_old);
    3518           0 :                         client->setCrack(-1, v3s16(0, 0, 0));
    3519           0 :                         runData->dig_time = 0.0;
    3520             :                 }
    3521             :         }
    3522             : 
    3523        1166 :         if (!runData->digging && runData->ldown_for_dig && !input->getLeftState()) {
    3524           0 :                 runData->ldown_for_dig = false;
    3525             :         }
    3526             : 
    3527        1166 :         runData->left_punch = false;
    3528             : 
    3529        1166 :         soundmaker->m_player_leftpunch_sound.name = "";
    3530             : 
    3531        1166 :         if (input->getRightState())
    3532           0 :                 runData->repeat_rightclick_timer += dtime;
    3533             :         else
    3534        1166 :                 runData->repeat_rightclick_timer = 0;
    3535             : 
    3536        1166 :         if (playeritem_def.usable && input->getLeftState()) {
    3537           0 :                 if (input->getLeftClicked())
    3538           0 :                         client->interact(4, pointed);
    3539        1166 :         } else if (pointed.type == POINTEDTHING_NODE) {
    3540             :                 ToolCapabilities playeritem_toolcap =
    3541         570 :                                 playeritem.getToolCapabilities(itemdef_manager);
    3542             :                 handlePointingAtNode(runData, pointed, playeritem_def,
    3543         285 :                                 playeritem_toolcap, dtime);
    3544         881 :         } else if (pointed.type == POINTEDTHING_OBJECT) {
    3545           0 :                 handlePointingAtObject(runData, pointed, playeritem,
    3546           0 :                                 player_position, show_debug);
    3547         881 :         } else if (input->getLeftState()) {
    3548             :                 // When button is held down in air, show continuous animation
    3549           0 :                 runData->left_punch = true;
    3550             :         }
    3551             : 
    3552        1166 :         runData->pointed_old = pointed;
    3553             : 
    3554        1166 :         if (runData->left_punch || input->getLeftClicked())
    3555           0 :                 camera->setDigging(0); // left click animation
    3556             : 
    3557        1166 :         input->resetLeftClicked();
    3558        1166 :         input->resetRightClicked();
    3559             : 
    3560        1166 :         input->resetLeftReleased();
    3561        1166 :         input->resetRightReleased();
    3562        1166 : }
    3563             : 
    3564             : 
    3565         285 : void Game::handlePointingAtNode(GameRunData *runData,
    3566             :                 const PointedThing &pointed, const ItemDefinition &playeritem_def,
    3567             :                 const ToolCapabilities &playeritem_toolcap, f32 dtime)
    3568             : {
    3569         285 :         v3s16 nodepos = pointed.node_undersurface;
    3570         285 :         v3s16 neighbourpos = pointed.node_abovesurface;
    3571             : 
    3572             :         /*
    3573             :                 Check information text of node
    3574             :         */
    3575             : 
    3576         285 :         ClientMap &map = client->getEnv().getClientMap();
    3577         285 :         NodeMetadata *meta = map.getNodeMetadata(nodepos);
    3578             : 
    3579         285 :         if (meta) {
    3580          76 :                 infotext = narrow_to_wide(meta->getString("infotext"));
    3581             :         } else {
    3582         209 :                 MapNode n = map.getNodeNoEx(nodepos);
    3583             : 
    3584         209 :                 if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
    3585           0 :                         infotext = L"Unknown node: ";
    3586           0 :                         infotext += narrow_to_wide(nodedef_manager->get(n).name);
    3587             :                 }
    3588             :         }
    3589             : 
    3590        1425 :         if (runData->nodig_delay_timer <= 0.0 && input->getLeftState()
    3591         855 :                         && client->checkPrivilege("interact")) {
    3592           0 :                 handleDigging(runData, pointed, nodepos, playeritem_toolcap, dtime);
    3593             :         }
    3594             : 
    3595        1425 :         if ((input->getRightClicked() ||
    3596         570 :                         runData->repeat_rightclick_timer >= m_repeat_right_click_time) &&
    3597         285 :                         client->checkPrivilege("interact")) {
    3598           0 :                 runData->repeat_rightclick_timer = 0;
    3599           0 :                 infostream << "Ground right-clicked" << std::endl;
    3600             : 
    3601           0 :                 if (meta && meta->getString("formspec") != "" && !random_input
    3602           0 :                                 && !input->isKeyDown(getKeySetting("keymap_sneak"))) {
    3603           0 :                         infostream << "Launching custom inventory view" << std::endl;
    3604             : 
    3605           0 :                         InventoryLocation inventoryloc;
    3606           0 :                         inventoryloc.setNodeMeta(nodepos);
    3607             : 
    3608             :                         NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
    3609           0 :                                 &client->getEnv().getClientMap(), nodepos);
    3610           0 :                         TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
    3611             : 
    3612           0 :                         create_formspec_menu(&current_formspec, client, gamedef,
    3613           0 :                                              texture_src, device, fs_src, txt_dst, client);
    3614             : 
    3615           0 :                         current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
    3616             :                 } else {
    3617             :                         // Report right click to server
    3618             : 
    3619           0 :                         camera->setDigging(1);  // right click animation (always shown for feedback)
    3620             : 
    3621             :                         // If the wielded item has node placement prediction,
    3622             :                         // make that happen
    3623           0 :                         bool placed = nodePlacementPrediction(*client,
    3624             :                                         playeritem_def,
    3625           0 :                                         nodepos, neighbourpos);
    3626             : 
    3627           0 :                         if (placed) {
    3628             :                                 // Report to server
    3629           0 :                                 client->interact(3, pointed);
    3630             :                                 // Read the sound
    3631           0 :                                 soundmaker->m_player_rightpunch_sound =
    3632           0 :                                                 playeritem_def.sound_place;
    3633             :                         } else {
    3634           0 :                                 soundmaker->m_player_rightpunch_sound =
    3635           0 :                                                 SimpleSoundSpec();
    3636             :                         }
    3637             : 
    3638           0 :                         if (playeritem_def.node_placement_prediction == "" ||
    3639           0 :                                         nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable)
    3640           0 :                                 client->interact(3, pointed); // Report to server
    3641             :                 }
    3642             :         }
    3643         285 : }
    3644             : 
    3645             : 
    3646           0 : void Game::handlePointingAtObject(GameRunData *runData,
    3647             :                 const PointedThing &pointed,
    3648             :                 const ItemStack &playeritem,
    3649             :                 const v3f &player_position,
    3650             :                 bool show_debug)
    3651             : {
    3652           0 :         infotext = narrow_to_wide(runData->selected_object->infoText());
    3653             : 
    3654           0 :         if (infotext == L"" && show_debug) {
    3655           0 :                 infotext = narrow_to_wide(runData->selected_object->debugInfoText());
    3656             :         }
    3657             : 
    3658           0 :         if (input->getLeftState()) {
    3659           0 :                 bool do_punch = false;
    3660           0 :                 bool do_punch_damage = false;
    3661             : 
    3662           0 :                 if (runData->object_hit_delay_timer <= 0.0) {
    3663           0 :                         do_punch = true;
    3664           0 :                         do_punch_damage = true;
    3665           0 :                         runData->object_hit_delay_timer = object_hit_delay;
    3666             :                 }
    3667             : 
    3668           0 :                 if (input->getLeftClicked())
    3669           0 :                         do_punch = true;
    3670             : 
    3671           0 :                 if (do_punch) {
    3672           0 :                         infostream << "Left-clicked object" << std::endl;
    3673           0 :                         runData->left_punch = true;
    3674             :                 }
    3675             : 
    3676           0 :                 if (do_punch_damage) {
    3677             :                         // Report direct punch
    3678           0 :                         v3f objpos = runData->selected_object->getPosition();
    3679           0 :                         v3f dir = (objpos - player_position).normalize();
    3680             : 
    3681           0 :                         bool disable_send = runData->selected_object->directReportPunch(
    3682           0 :                                         dir, &playeritem, runData->time_from_last_punch);
    3683           0 :                         runData->time_from_last_punch = 0;
    3684             : 
    3685           0 :                         if (!disable_send)
    3686           0 :                                 client->interact(0, pointed);
    3687             :                 }
    3688           0 :         } else if (input->getRightClicked()) {
    3689           0 :                 infostream << "Right-clicked object" << std::endl;
    3690           0 :                 client->interact(3, pointed);  // place
    3691             :         }
    3692           0 : }
    3693             : 
    3694             : 
    3695           0 : void Game::handleDigging(GameRunData *runData,
    3696             :                 const PointedThing &pointed, const v3s16 &nodepos,
    3697             :                 const ToolCapabilities &playeritem_toolcap, f32 dtime)
    3698             : {
    3699           0 :         if (!runData->digging) {
    3700           0 :                 infostream << "Started digging" << std::endl;
    3701           0 :                 client->interact(0, pointed);
    3702           0 :                 runData->digging = true;
    3703           0 :                 runData->ldown_for_dig = true;
    3704             :         }
    3705             : 
    3706           0 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3707           0 :         ClientMap &map = client->getEnv().getClientMap();
    3708           0 :         MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos);
    3709             : 
    3710             :         // NOTE: Similar piece of code exists on the server side for
    3711             :         // cheat detection.
    3712             :         // Get digging parameters
    3713           0 :         DigParams params = getDigParams(nodedef_manager->get(n).groups,
    3714           0 :                         &playeritem_toolcap);
    3715             : 
    3716             :         // If can't dig, try hand
    3717           0 :         if (!params.diggable) {
    3718           0 :                 const ItemDefinition &hand = itemdef_manager->get("");
    3719           0 :                 const ToolCapabilities *tp = hand.tool_capabilities;
    3720             : 
    3721           0 :                 if (tp)
    3722           0 :                         params = getDigParams(nodedef_manager->get(n).groups, tp);
    3723             :         }
    3724             : 
    3725           0 :         if (params.diggable == false) {
    3726             :                 // I guess nobody will wait for this long
    3727           0 :                 runData->dig_time_complete = 10000000.0;
    3728             :         } else {
    3729           0 :                 runData->dig_time_complete = params.time;
    3730             : 
    3731           0 :                 if (m_cache_enable_particles) {
    3732             :                         const ContentFeatures &features =
    3733           0 :                                         client->getNodeDefManager()->get(n);
    3734           0 :                         client->getParticleManager()->addPunchingParticles(gamedef, smgr,
    3735           0 :                                         player, nodepos, features.tiles);
    3736             :                 }
    3737             :         }
    3738             : 
    3739           0 :         if (runData->dig_time_complete >= 0.001) {
    3740           0 :                 runData->dig_index = (float)crack_animation_length
    3741           0 :                                 * runData->dig_time
    3742           0 :                                 / runData->dig_time_complete;
    3743             :         } else {
    3744             :                 // This is for torches
    3745           0 :                 runData->dig_index = crack_animation_length;
    3746             :         }
    3747             : 
    3748           0 :         SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
    3749             : 
    3750           0 :         if (sound_dig.exists() && params.diggable) {
    3751           0 :                 if (sound_dig.name == "__group") {
    3752           0 :                         if (params.main_group != "") {
    3753           0 :                                 soundmaker->m_player_leftpunch_sound.gain = 0.5;
    3754           0 :                                 soundmaker->m_player_leftpunch_sound.name =
    3755           0 :                                                 std::string("default_dig_") +
    3756           0 :                                                 params.main_group;
    3757             :                         }
    3758             :                 } else {
    3759           0 :                         soundmaker->m_player_leftpunch_sound = sound_dig;
    3760             :                 }
    3761             :         }
    3762             : 
    3763             :         // Don't show cracks if not diggable
    3764           0 :         if (runData->dig_time_complete >= 100000.0) {
    3765           0 :         } else if (runData->dig_index < crack_animation_length) {
    3766             :                 //TimeTaker timer("client.setTempMod");
    3767             :                 //infostream<<"dig_index="<<dig_index<<std::endl;
    3768           0 :                 client->setCrack(runData->dig_index, nodepos);
    3769             :         } else {
    3770           0 :                 infostream << "Digging completed" << std::endl;
    3771           0 :                 client->interact(2, pointed);
    3772           0 :                 client->setCrack(-1, v3s16(0, 0, 0));
    3773             :                 bool is_valid_position;
    3774           0 :                 MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position);
    3775           0 :                 if (is_valid_position)
    3776           0 :                         client->removeNode(nodepos);
    3777             : 
    3778           0 :                 if (m_cache_enable_particles) {
    3779             :                         const ContentFeatures &features =
    3780           0 :                                 client->getNodeDefManager()->get(wasnode);
    3781           0 :                         client->getParticleManager()->addDiggingParticles(gamedef, smgr,
    3782           0 :                                         player, nodepos, features.tiles);
    3783             :                 }
    3784             : 
    3785           0 :                 runData->dig_time = 0;
    3786           0 :                 runData->digging = false;
    3787             : 
    3788             :                 runData->nodig_delay_timer =
    3789           0 :                                 runData->dig_time_complete / (float)crack_animation_length;
    3790             : 
    3791             :                 // We don't want a corresponding delay to
    3792             :                 // very time consuming nodes
    3793           0 :                 if (runData->nodig_delay_timer > 0.3)
    3794           0 :                         runData->nodig_delay_timer = 0.3;
    3795             : 
    3796             :                 // We want a slight delay to very little
    3797             :                 // time consuming nodes
    3798           0 :                 const float mindelay = 0.15;
    3799             : 
    3800           0 :                 if (runData->nodig_delay_timer < mindelay)
    3801           0 :                         runData->nodig_delay_timer = mindelay;
    3802             : 
    3803             :                 // Send event to trigger sound
    3804           0 :                 MtEvent *e = new NodeDugEvent(nodepos, wasnode);
    3805           0 :                 gamedef->event()->put(e);
    3806             :         }
    3807             : 
    3808           0 :         if (runData->dig_time_complete < 100000.0) {
    3809           0 :                 runData->dig_time += dtime;
    3810             :         } else {
    3811           0 :                 runData->dig_time = 0;
    3812           0 :                 client->setCrack(-1, nodepos);
    3813             :         }
    3814             : 
    3815           0 :         camera->setDigging(0);  // left click animation
    3816           0 : }
    3817             : 
    3818             : 
    3819        1166 : void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
    3820             :                 ProfilerGraph *graph, RunStats *stats, GameRunData *runData,
    3821             :                 f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
    3822             : {
    3823        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    3824             : 
    3825             :         /*
    3826             :                 Fog range
    3827             :         */
    3828             : 
    3829        1166 :         if (draw_control->range_all) {
    3830           0 :                 runData->fog_range = 100000 * BS;
    3831             :         } else {
    3832        1166 :                 runData->fog_range = draw_control->wanted_range * BS
    3833        1166 :                                 + 0.0 * MAP_BLOCKSIZE * BS;
    3834        2170 :                 runData->fog_range = MYMIN(
    3835             :                                 runData->fog_range,
    3836        2170 :                                 (draw_control->farthest_drawn + 20) * BS);
    3837        1166 :                 runData->fog_range *= 0.9;
    3838             :         }
    3839             : 
    3840             :         /*
    3841             :                 Calculate general brightness
    3842             :         */
    3843        1166 :         u32 daynight_ratio = client->getEnv().getDayNightRatio();
    3844        1166 :         float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
    3845             :         float direct_brightness;
    3846             :         bool sunlight_seen;
    3847             : 
    3848        1166 :         if (g_settings->getBool("free_move")) {
    3849           0 :                 direct_brightness = time_brightness;
    3850           0 :                 sunlight_seen = true;
    3851             :         } else {
    3852        2332 :                 ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
    3853        1166 :                 float old_brightness = sky->getBrightness();
    3854        1166 :                 direct_brightness = client->getEnv().getClientMap()
    3855        2332 :                                 .getBackgroundBrightness(MYMIN(runData->fog_range * 1.2, 60 * BS),
    3856        1166 :                                         daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
    3857        1166 :                                     / 255.0;
    3858             :         }
    3859             : 
    3860        1166 :         float time_of_day = runData->time_of_day;
    3861        1166 :         float time_of_day_smooth = runData->time_of_day_smooth;
    3862             : 
    3863        1166 :         time_of_day = client->getEnv().getTimeOfDayF();
    3864             : 
    3865        1166 :         const float maxsm = 0.05;
    3866        1166 :         const float todsm = 0.05;
    3867             : 
    3868        1167 :         if (fabs(time_of_day - time_of_day_smooth) > maxsm &&
    3869           2 :                         fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
    3870           1 :                         fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
    3871           1 :                 time_of_day_smooth = time_of_day;
    3872             : 
    3873        1166 :         if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
    3874           0 :                 time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
    3875           0 :                                 + (time_of_day + 1.0) * todsm;
    3876             :         else
    3877        1166 :                 time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
    3878        1166 :                                 + time_of_day * todsm;
    3879             : 
    3880        1166 :         runData->time_of_day = time_of_day;
    3881        1166 :         runData->time_of_day_smooth = time_of_day_smooth;
    3882             : 
    3883        2332 :         sky->update(time_of_day_smooth, time_brightness, direct_brightness,
    3884        1166 :                         sunlight_seen, camera->getCameraMode(), player->getYaw(),
    3885        1166 :                         player->getPitch());
    3886             : 
    3887             :         /*
    3888             :                 Update clouds
    3889             :         */
    3890        1166 :         if (clouds) {
    3891        1166 :                 v3f player_position = player->getPosition();
    3892        1166 :                 if (sky->getCloudsVisible()) {
    3893        1166 :                         clouds->setVisible(true);
    3894        1166 :                         clouds->step(dtime);
    3895        2332 :                         clouds->update(v2f(player_position.X, player_position.Z),
    3896        2332 :                                        sky->getCloudColor());
    3897             :                 } else {
    3898           0 :                         clouds->setVisible(false);
    3899             :                 }
    3900             :         }
    3901             : 
    3902             :         /*
    3903             :                 Update particles
    3904             :         */
    3905        1166 :         client->getParticleManager()->step(dtime);
    3906             : 
    3907             :         /*
    3908             :                 Fog
    3909             :         */
    3910             : 
    3911        1166 :         if (m_cache_enable_fog && !flags.force_fog_off) {
    3912        4664 :                 driver->setFog(
    3913        1166 :                                 sky->getBgColor(),
    3914             :                                 video::EFT_FOG_LINEAR,
    3915        1166 :                                 runData->fog_range * 0.4,
    3916             :                                 runData->fog_range * 1.0,
    3917             :                                 0.01,
    3918             :                                 false, // pixel fog
    3919             :                                 false // range fog
    3920        2332 :                 );
    3921             :         } else {
    3922           0 :                 driver->setFog(
    3923           0 :                                 sky->getBgColor(),
    3924             :                                 video::EFT_FOG_LINEAR,
    3925             :                                 100000 * BS,
    3926             :                                 110000 * BS,
    3927             :                                 0.01,
    3928             :                                 false, // pixel fog
    3929             :                                 false // range fog
    3930           0 :                 );
    3931             :         }
    3932             : 
    3933             :         /*
    3934             :                 Get chat messages from client
    3935             :         */
    3936             : 
    3937        1166 :         v2u32 screensize = driver->getScreenSize();
    3938             : 
    3939        3498 :         updateChat(*client, dtime, flags.show_debug, screensize,
    3940        1166 :                         flags.show_chat, runData->profiler_current_page,
    3941        2332 :                         *chat_backend, guitext_chat);
    3942             : 
    3943             :         /*
    3944             :                 Inventory
    3945             :         */
    3946             : 
    3947        1166 :         if (client->getPlayerItem() != runData->new_playeritem)
    3948          20 :                 client->selectPlayerItem(runData->new_playeritem);
    3949             : 
    3950             :         // Update local inventory if it has changed
    3951        1166 :         if (client->getLocalInventoryUpdated()) {
    3952             :                 //infostream<<"Updating local inventory"<<std::endl;
    3953          24 :                 client->getLocalInventory(*local_inventory);
    3954          24 :                 runData->update_wielded_item_trigger = true;
    3955             :         }
    3956             : 
    3957        1166 :         if (runData->update_wielded_item_trigger) {
    3958             :                 // Update wielded tool
    3959          24 :                 InventoryList *mlist = local_inventory->getList("main");
    3960             : 
    3961          24 :                 if (mlist && (client->getPlayerItem() < mlist->getSize())) {
    3962          48 :                         ItemStack item = mlist->getItem(client->getPlayerItem());
    3963          24 :                         camera->wield(item);
    3964             :                 }
    3965          24 :                 runData->update_wielded_item_trigger = false;
    3966             :         }
    3967             : 
    3968             :         /*
    3969             :                 Update block draw list every 200ms or when camera direction has
    3970             :                 changed much
    3971             :         */
    3972        1166 :         runData->update_draw_list_timer += dtime;
    3973             : 
    3974        1166 :         v3f camera_direction = camera->getDirection();
    3975        2332 :         if (runData->update_draw_list_timer >= 0.2
    3976        1041 :                         || runData->update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
    3977        2153 :                         || flags.camera_offset_changed) {
    3978         179 :                 runData->update_draw_list_timer = 0;
    3979         179 :                 client->getEnv().getClientMap().updateDrawList(driver);
    3980         179 :                 runData->update_draw_list_last_cam_dir = camera_direction;
    3981             :         }
    3982             : 
    3983        1166 :         updateGui(&runData->statustext_time, *stats, *runData, dtime, flags, cam);
    3984             : 
    3985             :         /*
    3986             :            make sure menu is on top
    3987             :            1. Delete formspec menu reference if menu was removed
    3988             :            2. Else, make sure formspec menu is on top
    3989             :         */
    3990        1166 :         if (current_formspec) {
    3991          20 :                 if (current_formspec->getReferenceCount() == 1) {
    3992           0 :                         current_formspec->drop();
    3993           0 :                         current_formspec = NULL;
    3994          20 :                 } else if (!noMenuActive()) {
    3995          20 :                         guiroot->bringToFront(current_formspec);
    3996             :                 }
    3997             :         }
    3998             : 
    3999             :         /*
    4000             :                 Drawing begins
    4001             :         */
    4002             : 
    4003        1166 :         video::SColor skycolor = sky->getSkyColor();
    4004             : 
    4005        2332 :         TimeTaker tt_draw("mainloop: draw");
    4006             :         {
    4007        2332 :                 TimeTaker timer("beginScene");
    4008        1166 :                 driver->beginScene(true, true, skycolor);
    4009        1166 :                 stats->beginscenetime = timer.stop(true);
    4010             :         }
    4011             : 
    4012        4664 :         draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper,
    4013        1166 :                         guienv, highlight_boxes, screensize, skycolor, flags.show_hud,
    4014        3498 :                         flags.show_minimap);
    4015             : 
    4016             :         /*
    4017             :                 Profiler graph
    4018             :         */
    4019        1166 :         if (flags.show_profiler_graph)
    4020           0 :                 graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
    4021             : 
    4022             :         /*
    4023             :                 Damage flash
    4024             :         */
    4025        1166 :         if (runData->damage_flash > 0.0) {
    4026           0 :                 video::SColor color(std::min(runData->damage_flash, 180.0f),
    4027             :                                 180,
    4028             :                                 0,
    4029           0 :                                 0);
    4030           0 :                 driver->draw2DRectangle(color,
    4031           0 :                                         core::rect<s32>(0, 0, screensize.X, screensize.Y),
    4032           0 :                                         NULL);
    4033             : 
    4034           0 :                 runData->damage_flash -= 100.0 * dtime;
    4035             :         }
    4036             : 
    4037             :         /*
    4038             :                 Damage camera tilt
    4039             :         */
    4040        1166 :         if (player->hurt_tilt_timer > 0.0) {
    4041           0 :                 player->hurt_tilt_timer -= dtime * 5;
    4042             : 
    4043           0 :                 if (player->hurt_tilt_timer < 0)
    4044           0 :                         player->hurt_tilt_strength = 0;
    4045             :         }
    4046             : 
    4047             :         /*
    4048             :                 Update minimap pos
    4049             :         */
    4050        1166 :         if (flags.show_minimap && flags.show_hud) {
    4051        1166 :                 mapper->setPos(floatToInt(player->getPosition(), BS));
    4052             :         }
    4053             : 
    4054             :         /*
    4055             :                 End scene
    4056             :         */
    4057             :         {
    4058        2332 :                 TimeTaker timer("endScene");
    4059        1166 :                 driver->endScene();
    4060        1166 :                 stats->endscenetime = timer.stop(true);
    4061             :         }
    4062             : 
    4063        1166 :         stats->drawtime = tt_draw.stop(true);
    4064        1166 :         g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f);
    4065        1166 : }
    4066             : 
    4067             : 
    4068           0 : inline static const char *yawToDirectionString(int yaw)
    4069             : {
    4070             :         // NOTE: TODO: This can be done mathematically without the else/else-if
    4071             :         // cascade.
    4072             : 
    4073             :         const char *player_direction;
    4074             : 
    4075           0 :         yaw = wrapDegrees_0_360(yaw);
    4076             : 
    4077           0 :         if (yaw >= 45 && yaw < 135)
    4078           0 :                 player_direction = "West [-X]";
    4079           0 :         else if (yaw >= 135 && yaw < 225)
    4080           0 :                 player_direction = "South [-Z]";
    4081           0 :         else if (yaw >= 225 && yaw < 315)
    4082           0 :                 player_direction = "East [+X]";
    4083             :         else
    4084           0 :                 player_direction = "North [+Z]";
    4085             : 
    4086           0 :         return player_direction;
    4087             : }
    4088             : 
    4089             : 
    4090        1166 : void Game::updateGui(float *statustext_time, const RunStats &stats,
    4091             :                 const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
    4092             :                 const CameraOrientation &cam)
    4093             : {
    4094        1166 :         v2u32 screensize = driver->getScreenSize();
    4095        1166 :         LocalPlayer *player = client->getEnv().getLocalPlayer();
    4096        1166 :         v3f player_position = player->getPosition();
    4097             : 
    4098        1166 :         if (flags.show_debug) {
    4099             :                 static float drawtime_avg = 0;
    4100           0 :                 drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05;
    4101             : 
    4102           0 :                 u16 fps = 1.0 / stats.dtime_jitter.avg;
    4103             :                 //s32 fps = driver->getFPS();
    4104             : 
    4105           0 :                 std::ostringstream os(std::ios_base::binary);
    4106           0 :                 os << std::fixed
    4107           0 :                    << PROJECT_NAME_C " " << g_version_hash
    4108           0 :                    << " FPS = " << fps
    4109           0 :                    << " (R: range_all=" << draw_control->range_all << ")"
    4110           0 :                    << std::setprecision(0)
    4111           0 :                    << " drawtime = " << drawtime_avg
    4112           0 :                    << std::setprecision(1)
    4113           0 :                    << ", dtime_jitter = "
    4114           0 :                    << (stats.dtime_jitter.max_fraction * 100.0) << " %"
    4115           0 :                    << std::setprecision(1)
    4116           0 :                    << ", v_range = " << draw_control->wanted_range
    4117           0 :                    << std::setprecision(3)
    4118           0 :                    << ", RTT = " << client->getRTT();
    4119           0 :                 guitext->setText(narrow_to_wide(os.str()).c_str());
    4120           0 :                 guitext->setVisible(true);
    4121        1166 :         } else if (flags.show_hud || flags.show_chat) {
    4122        2332 :                 std::ostringstream os(std::ios_base::binary);
    4123        1166 :                 os << PROJECT_NAME_C " " << g_version_hash;
    4124        1166 :                 guitext->setText(narrow_to_wide(os.str()).c_str());
    4125        2332 :                 guitext->setVisible(true);
    4126             :         } else {
    4127           0 :                 guitext->setVisible(false);
    4128             :         }
    4129             : 
    4130        1166 :         if (guitext->isVisible()) {
    4131             :                 core::rect<s32> rect(
    4132             :                                 5,              5,
    4133        2332 :                                 screensize.X,   5 + g_fontengine->getTextHeight()
    4134        2332 :                 );
    4135        1166 :                 guitext->setRelativePosition(rect);
    4136             :         }
    4137             : 
    4138        1166 :         if (flags.show_debug) {
    4139           0 :                 std::ostringstream os(std::ios_base::binary);
    4140           0 :                 os << std::setprecision(1) << std::fixed
    4141           0 :                    << "(" << (player_position.X / BS)
    4142           0 :                    << ", " << (player_position.Y / BS)
    4143           0 :                    << ", " << (player_position.Z / BS)
    4144           0 :                    << ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
    4145           0 :                    << " " << yawToDirectionString(cam.camera_yaw)
    4146           0 :                    << ") (seed = " << ((u64)client->getMapSeed())
    4147           0 :                    << ")";
    4148             : 
    4149           0 :                 if (runData.pointed_old.type == POINTEDTHING_NODE) {
    4150           0 :                         ClientMap &map = client->getEnv().getClientMap();
    4151           0 :                         const INodeDefManager *nodedef = client->getNodeDefManager();
    4152           0 :                         MapNode n = map.getNodeNoEx(runData.pointed_old.node_undersurface);
    4153           0 :                         if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") {
    4154           0 :                                 const ContentFeatures &features = nodedef->get(n);
    4155           0 :                                 os << " (pointing_at = " << nodedef->get(n).name
    4156           0 :                                    << " - " << features.tiledef[0].name.c_str()
    4157           0 :                                    << ")";
    4158             :                         }
    4159             :                 }
    4160             : 
    4161           0 :                 guitext2->setText(narrow_to_wide(os.str()).c_str());
    4162           0 :                 guitext2->setVisible(true);
    4163             : 
    4164             :                 core::rect<s32> rect(
    4165           0 :                                 5,             5 + g_fontengine->getTextHeight(),
    4166           0 :                                 screensize.X,  5 + g_fontengine->getTextHeight() * 2
    4167           0 :                 );
    4168           0 :                 guitext2->setRelativePosition(rect);
    4169             :         } else {
    4170        1166 :                 guitext2->setVisible(false);
    4171             :         }
    4172             : 
    4173        1166 :         guitext_info->setText(infotext.c_str());
    4174        1166 :         guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0);
    4175             : 
    4176        1166 :         float statustext_time_max = 1.5;
    4177             : 
    4178        1166 :         if (!statustext.empty()) {
    4179           0 :                 *statustext_time += dtime;
    4180             : 
    4181           0 :                 if (*statustext_time >= statustext_time_max) {
    4182           0 :                         statustext = L"";
    4183           0 :                         *statustext_time = 0;
    4184             :                 }
    4185             :         }
    4186             : 
    4187        1166 :         guitext_status->setText(statustext.c_str());
    4188        1166 :         guitext_status->setVisible(!statustext.empty());
    4189             : 
    4190        1166 :         if (!statustext.empty()) {
    4191           0 :                 s32 status_width  = guitext_status->getTextWidth();
    4192           0 :                 s32 status_height = guitext_status->getTextHeight();
    4193           0 :                 s32 status_y = screensize.Y - 150;
    4194           0 :                 s32 status_x = (screensize.X - status_width) / 2;
    4195             :                 core::rect<s32> rect(
    4196             :                                 status_x , status_y - status_height,
    4197             :                                 status_x + status_width, status_y
    4198           0 :                 );
    4199           0 :                 guitext_status->setRelativePosition(rect);
    4200             : 
    4201             :                 // Fade out
    4202           0 :                 video::SColor initial_color(255, 0, 0, 0);
    4203             : 
    4204           0 :                 if (guienv->getSkin())
    4205           0 :                         initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
    4206             : 
    4207           0 :                 video::SColor final_color = initial_color;
    4208           0 :                 final_color.setAlpha(0);
    4209             :                 video::SColor fade_color = initial_color.getInterpolated_quadratic(
    4210             :                                 initial_color, final_color,
    4211           0 :                                 pow(*statustext_time / statustext_time_max, 2.0f));
    4212           0 :                 guitext_status->setOverrideColor(fade_color);
    4213           0 :                 guitext_status->enableOverrideColor(true);
    4214             :         }
    4215        1166 : }
    4216             : 
    4217             : 
    4218             : /* Log times and stuff for visualization */
    4219        1166 : inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
    4220             : {
    4221        2332 :         Profiler::GraphValues values;
    4222        1166 :         g_profiler->graphGet(values);
    4223        1166 :         graph->put(values);
    4224        1166 : }
    4225             : 
    4226             : 
    4227             : 
    4228             : /****************************************************************************
    4229             :  Misc
    4230             :  ****************************************************************************/
    4231             : 
    4232             : /* On some computers framerate doesn't seem to be automatically limited
    4233             :  */
    4234        1193 : inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
    4235             : {
    4236             :         // not using getRealTime is necessary for wine
    4237        1193 :         device->getTimer()->tick(); // Maker sure device time is up-to-date
    4238        1193 :         u32 time = device->getTimer()->getTime();
    4239        1193 :         u32 last_time = fps_timings->last_time;
    4240             : 
    4241        1193 :         if (time > last_time)  // Make sure time hasn't overflowed
    4242        1190 :                 fps_timings->busy_time = time - last_time;
    4243             :         else
    4244           3 :                 fps_timings->busy_time = 0;
    4245             : 
    4246        1193 :         u32 frametime_min = 1000 / (g_menumgr.pausesGame()
    4247        1250 :                         ? g_settings->getFloat("pause_fps_max")
    4248        2405 :                         : g_settings->getFloat("fps_max"));
    4249             : 
    4250        1193 :         if (fps_timings->busy_time < frametime_min) {
    4251         106 :                 fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
    4252         106 :                 device->sleep(fps_timings->sleep_time);
    4253             :         } else {
    4254        1087 :                 fps_timings->sleep_time = 0;
    4255             :         }
    4256             : 
    4257             :         /* Get the new value of the device timer. Note that device->sleep() may
    4258             :          * not sleep for the entire requested time as sleep may be interrupted and
    4259             :          * therefore it is arguably more accurate to get the new time from the
    4260             :          * device rather than calculating it by adding sleep_time to time.
    4261             :          */
    4262             : 
    4263        1193 :         device->getTimer()->tick(); // Update device timer
    4264        1193 :         time = device->getTimer()->getTime();
    4265             : 
    4266        1193 :         if (time > last_time)  // Make sure last_time hasn't overflowed
    4267        1193 :                 *dtime = (time - last_time) / 1000.0;
    4268             :         else
    4269           0 :                 *dtime = 0;
    4270             : 
    4271        1193 :         fps_timings->last_time = time;
    4272        1193 : }
    4273             : 
    4274             : // Note: This will free (using delete[])! \p msg. If you want to use it later,
    4275             : // pass a copy of it to this function
    4276             : // Note: \p msg must be allocated using new (not malloc())
    4277          12 : void Game::showOverlayMessage(const wchar_t *msg, float dtime,
    4278             :                 int percent, bool draw_clouds)
    4279             : {
    4280          12 :         draw_load_screen(msg, device, guienv, dtime, percent, draw_clouds);
    4281          12 :         delete[] msg;
    4282          12 : }
    4283             : 
    4284             : 
    4285             : /****************************************************************************/
    4286             : /****************************************************************************
    4287             :  Shutdown / cleanup
    4288             :  ****************************************************************************/
    4289             : /****************************************************************************/
    4290             : 
    4291           1 : void Game::extendedResourceCleanup()
    4292             : {
    4293             :         // Extended resource accounting
    4294           1 :         infostream << "Irrlicht resources after cleanup:" << std::endl;
    4295           1 :         infostream << "\tRemaining meshes   : "
    4296           2 :                    << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
    4297           1 :         infostream << "\tRemaining textures : "
    4298           2 :                    << driver->getTextureCount() << std::endl;
    4299             : 
    4300           4 :         for (unsigned int i = 0; i < driver->getTextureCount(); i++) {
    4301           3 :                 irr::video::ITexture *texture = driver->getTextureByIndex(i);
    4302           3 :                 infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
    4303           3 :                            << std::endl;
    4304             :         }
    4305             : 
    4306           1 :         clearTextureNameCache();
    4307           1 :         infostream << "\tRemaining materials: "
    4308           2 :                << driver-> getMaterialRendererCount()
    4309           1 :                        << " (note: irrlicht doesn't support removing renderers)" << std::endl;
    4310           1 : }
    4311             : 
    4312             : 
    4313             : /****************************************************************************/
    4314             : /****************************************************************************
    4315             :  extern function for launching the game
    4316             :  ****************************************************************************/
    4317             : /****************************************************************************/
    4318             : 
    4319           1 : void the_game(bool *kill,
    4320             :                 bool random_input,
    4321             :                 InputHandler *input,
    4322             :                 IrrlichtDevice *device,
    4323             : 
    4324             :                 const std::string &map_dir,
    4325             :                 const std::string &playername,
    4326             :                 const std::string &password,
    4327             :                 const std::string &address,         // If empty local server is created
    4328             :                 u16 port,
    4329             : 
    4330             :                 std::string &error_message,
    4331             :                 ChatBackend &chat_backend,
    4332             :                 const SubgameSpec &gamespec,        // Used for local game
    4333             :                 bool simple_singleplayer_mode)
    4334             : {
    4335           2 :         Game game;
    4336             : 
    4337             :         /* Make a copy of the server address because if a local singleplayer server
    4338             :          * is created then this is updated and we don't want to change the value
    4339             :          * passed to us by the calling function
    4340             :          */
    4341           2 :         std::string server_address = address;
    4342             : 
    4343             :         try {
    4344             : 
    4345           1 :                 if (game.startup(kill, random_input, input, device, map_dir,
    4346             :                                 playername, password, &server_address, port,
    4347             :                                 error_message, &chat_backend, gamespec,
    4348             :                                 simple_singleplayer_mode)) {
    4349           1 :                         game.run();
    4350           1 :                         game.shutdown();
    4351             :                 }
    4352             : 
    4353           0 :         } catch (SerializationError &e) {
    4354             :                 error_message = std::string("A serialization error occurred:\n")
    4355           0 :                                 + e.what() + "\n\nThe server is probably "
    4356           0 :                                 " running a different version of " PROJECT_NAME_C ".";
    4357           0 :                 errorstream << error_message << std::endl;
    4358           0 :         } catch (ServerError &e) {
    4359           0 :                 error_message = e.what();
    4360           0 :                 errorstream << "ServerError: " << error_message << std::endl;
    4361           0 :         } catch (ModError &e) {
    4362           0 :                 error_message = e.what() + strgettext("\nCheck debug.txt for details.");
    4363           0 :                 errorstream << "ModError: " << error_message << std::endl;
    4364             :         }
    4365           4 : }
    4366             : 

Generated by: LCOV version 1.11