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