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