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