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