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