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