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