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