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