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