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