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