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