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