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