]> git.lizzy.rs Git - minetest.git/blob - src/client/game.cpp
54028fd1d17aadbf709d4408ea823df02433d1e0
[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 = fmtgettext("Unable to listen on %s because IPv6 is disabled",
1286                         bind_addr.serializeString().c_str());
1287                 errorstream << *error_message << std::endl;
1288                 return false;
1289         }
1290
1291         server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr,
1292                         false, nullptr, error_message);
1293         server->start();
1294
1295         return true;
1296 }
1297
1298 bool Game::createClient(const GameStartData &start_data)
1299 {
1300         showOverlayMessage(N_("Creating client..."), 0, 10);
1301
1302         draw_control = new MapDrawControl();
1303         if (!draw_control)
1304                 return false;
1305
1306         bool could_connect, connect_aborted;
1307 #ifdef HAVE_TOUCHSCREENGUI
1308         if (g_touchscreengui) {
1309                 g_touchscreengui->init(texture_src);
1310                 g_touchscreengui->hide();
1311         }
1312 #endif
1313         if (!connectToServer(start_data, &could_connect, &connect_aborted))
1314                 return false;
1315
1316         if (!could_connect) {
1317                 if (error_message->empty() && !connect_aborted) {
1318                         // Should not happen if error messages are set properly
1319                         *error_message = gettext("Connection failed for unknown reason");
1320                         errorstream << *error_message << std::endl;
1321                 }
1322                 return false;
1323         }
1324
1325         if (!getServerContent(&connect_aborted)) {
1326                 if (error_message->empty() && !connect_aborted) {
1327                         // Should not happen if error messages are set properly
1328                         *error_message = gettext("Connection failed for unknown reason");
1329                         errorstream << *error_message << std::endl;
1330                 }
1331                 return false;
1332         }
1333
1334         auto *scsf = new GameGlobalShaderConstantSetterFactory(
1335                         &m_flags.force_fog_off, &runData.fog_range, client);
1336         shader_src->addShaderConstantSetterFactory(scsf);
1337
1338         // Update cached textures, meshes and materials
1339         client->afterContentReceived();
1340
1341         /* Camera
1342          */
1343         camera = new Camera(*draw_control, client, m_rendering_engine);
1344         if (client->modsLoaded())
1345                 client->getScript()->on_camera_ready(camera);
1346         client->setCamera(camera);
1347
1348         /* Clouds
1349          */
1350         if (m_cache_enable_clouds)
1351                 clouds = new Clouds(smgr, -1, time(0));
1352
1353         /* Skybox
1354          */
1355         sky = new Sky(-1, m_rendering_engine, texture_src, shader_src);
1356         scsf->setSky(sky);
1357         skybox = NULL;  // This is used/set later on in the main run loop
1358
1359         /* Pre-calculated values
1360          */
1361         video::ITexture *t = texture_src->getTexture("crack_anylength.png");
1362         if (t) {
1363                 v2u32 size = t->getOriginalSize();
1364                 crack_animation_length = size.Y / size.X;
1365         } else {
1366                 crack_animation_length = 5;
1367         }
1368
1369         if (!initGui())
1370                 return false;
1371
1372         /* Set window caption
1373          */
1374         std::wstring str = utf8_to_wide(PROJECT_NAME_C);
1375         str += L" ";
1376         str += utf8_to_wide(g_version_hash);
1377         {
1378                 const wchar_t *text = nullptr;
1379                 if (simple_singleplayer_mode)
1380                         text = wgettext("Singleplayer");
1381                 else
1382                         text = wgettext("Multiplayer");
1383                 str += L" [";
1384                 str += text;
1385                 str += L"]";
1386                 delete[] text;
1387         }
1388         str += L" [";
1389         str += driver->getName();
1390         str += L"]";
1391
1392         device->setWindowCaption(str.c_str());
1393
1394         LocalPlayer *player = client->getEnv().getLocalPlayer();
1395         player->hurt_tilt_timer = 0;
1396         player->hurt_tilt_strength = 0;
1397
1398         hud = new Hud(client, player, &player->inventory);
1399
1400         mapper = client->getMinimap();
1401
1402         if (mapper && client->modsLoaded())
1403                 client->getScript()->on_minimap_ready(mapper);
1404
1405         return true;
1406 }
1407
1408 bool Game::initGui()
1409 {
1410         m_game_ui->init();
1411
1412         // Remove stale "recent" chat messages from previous connections
1413         chat_backend->clearRecentChat();
1414
1415         // Make sure the size of the recent messages buffer is right
1416         chat_backend->applySettings();
1417
1418         // Chat backend and console
1419         gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
1420                         -1, chat_backend, client, &g_menumgr);
1421
1422 #ifdef HAVE_TOUCHSCREENGUI
1423
1424         if (g_touchscreengui)
1425                 g_touchscreengui->show();
1426
1427 #endif
1428
1429         return true;
1430 }
1431
1432 bool Game::connectToServer(const GameStartData &start_data,
1433                 bool *connect_ok, bool *connection_aborted)
1434 {
1435         *connect_ok = false;    // Let's not be overly optimistic
1436         *connection_aborted = false;
1437         bool local_server_mode = false;
1438
1439         showOverlayMessage(N_("Resolving address..."), 0, 15);
1440
1441         Address connect_address(0, 0, 0, 0, start_data.socket_port);
1442
1443         try {
1444                 connect_address.Resolve(start_data.address.c_str());
1445
1446                 if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
1447                         //connect_address.Resolve("localhost");
1448                         if (connect_address.isIPv6()) {
1449                                 IPv6AddressBytes addr_bytes;
1450                                 addr_bytes.bytes[15] = 1;
1451                                 connect_address.setAddress(&addr_bytes);
1452                         } else {
1453                                 connect_address.setAddress(127, 0, 0, 1);
1454                         }
1455                         local_server_mode = true;
1456                 }
1457         } catch (ResolveError &e) {
1458                 *error_message = fmtgettext("Couldn't resolve address: %s", e.what());
1459
1460                 errorstream << *error_message << std::endl;
1461                 return false;
1462         }
1463
1464         if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
1465                 *error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
1466                 errorstream << *error_message << std::endl;
1467                 return false;
1468         }
1469
1470         client = new Client(start_data.name.c_str(),
1471                         start_data.password, start_data.address,
1472                         *draw_control, texture_src, shader_src,
1473                         itemdef_manager, nodedef_manager, sound, eventmgr,
1474                         m_rendering_engine, connect_address.isIPv6(), m_game_ui.get());
1475
1476         client->m_simple_singleplayer_mode = simple_singleplayer_mode;
1477
1478         infostream << "Connecting to server at ";
1479         connect_address.print(&infostream);
1480         infostream << std::endl;
1481
1482         client->connect(connect_address,
1483                 simple_singleplayer_mode || local_server_mode);
1484
1485         /*
1486                 Wait for server to accept connection
1487         */
1488
1489         try {
1490                 input->clear();
1491
1492                 FpsControl fps_control = { 0 };
1493                 f32 dtime;
1494                 f32 wait_time = 0; // in seconds
1495
1496                 fps_control.last_time = m_rendering_engine->get_timer_time();
1497
1498                 while (m_rendering_engine->run()) {
1499
1500                         limitFps(&fps_control, &dtime);
1501
1502                         // Update client and server
1503                         client->step(dtime);
1504
1505                         if (server != NULL)
1506                                 server->step(dtime);
1507
1508                         // End condition
1509                         if (client->getState() == LC_Init) {
1510                                 *connect_ok = true;
1511                                 break;
1512                         }
1513
1514                         // Break conditions
1515                         if (*connection_aborted)
1516                                 break;
1517
1518                         if (client->accessDenied()) {
1519                                 *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
1520                                 *reconnect_requested = client->reconnectRequested();
1521                                 errorstream << *error_message << std::endl;
1522                                 break;
1523                         }
1524
1525                         if (input->cancelPressed()) {
1526                                 *connection_aborted = true;
1527                                 infostream << "Connect aborted [Escape]" << std::endl;
1528                                 break;
1529                         }
1530
1531                         if (client->m_is_registration_confirmation_state) {
1532                                 if (registration_confirmation_shown) {
1533                                         // Keep drawing the GUI
1534                                         m_rendering_engine->draw_menu_scene(guienv, dtime, true);
1535                                 } else {
1536                                         registration_confirmation_shown = true;
1537                                         (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
1538                                                    &g_menumgr, client, start_data.name, start_data.password,
1539                                                    connection_aborted, texture_src))->drop();
1540                                 }
1541                         } else {
1542                                 wait_time += dtime;
1543                                 // Only time out if we aren't waiting for the server we started
1544                                 if (!start_data.address.empty() && wait_time > 10) {
1545                                         *error_message = gettext("Connection timed out.");
1546                                         errorstream << *error_message << std::endl;
1547                                         break;
1548                                 }
1549
1550                                 // Update status
1551                                 showOverlayMessage(N_("Connecting to server..."), dtime, 20);
1552                         }
1553                 }
1554         } catch (con::PeerNotFoundException &e) {
1555                 // TODO: Should something be done here? At least an info/error
1556                 // message?
1557                 return false;
1558         }
1559
1560         return true;
1561 }
1562
1563 bool Game::getServerContent(bool *aborted)
1564 {
1565         input->clear();
1566
1567         FpsControl fps_control = { 0 };
1568         f32 dtime; // in seconds
1569
1570         fps_control.last_time = m_rendering_engine->get_timer_time();
1571
1572         while (m_rendering_engine->run()) {
1573
1574                 limitFps(&fps_control, &dtime);
1575
1576                 // Update client and server
1577                 client->step(dtime);
1578
1579                 if (server != NULL)
1580                         server->step(dtime);
1581
1582                 // End condition
1583                 if (client->mediaReceived() && client->itemdefReceived() &&
1584                                 client->nodedefReceived()) {
1585                         break;
1586                 }
1587
1588                 // Error conditions
1589                 if (!checkConnection())
1590                         return false;
1591
1592                 if (client->getState() < LC_Init) {
1593                         *error_message = gettext("Client disconnected");
1594                         errorstream << *error_message << std::endl;
1595                         return false;
1596                 }
1597
1598                 if (input->cancelPressed()) {
1599                         *aborted = true;
1600                         infostream << "Connect aborted [Escape]" << std::endl;
1601                         return false;
1602                 }
1603
1604                 // Display status
1605                 int progress = 25;
1606
1607                 if (!client->itemdefReceived()) {
1608                         const wchar_t *text = wgettext("Item definitions...");
1609                         progress = 25;
1610                         m_rendering_engine->draw_load_screen(text, guienv, texture_src,
1611                                 dtime, progress);
1612                         delete[] text;
1613                 } else if (!client->nodedefReceived()) {
1614                         const wchar_t *text = wgettext("Node definitions...");
1615                         progress = 30;
1616                         m_rendering_engine->draw_load_screen(text, guienv, texture_src,
1617                                 dtime, progress);
1618                         delete[] text;
1619                 } else {
1620                         std::ostringstream message;
1621                         std::fixed(message);
1622                         message.precision(0);
1623                         float receive = client->mediaReceiveProgress() * 100;
1624                         message << gettext("Media...");
1625                         if (receive > 0)
1626                                 message << " " << receive << "%";
1627                         message.precision(2);
1628
1629                         if ((USE_CURL == 0) ||
1630                                         (!g_settings->getBool("enable_remote_media_server"))) {
1631                                 float cur = client->getCurRate();
1632                                 std::string cur_unit = gettext("KiB/s");
1633
1634                                 if (cur > 900) {
1635                                         cur /= 1024.0;
1636                                         cur_unit = gettext("MiB/s");
1637                                 }
1638
1639                                 message << " (" << cur << ' ' << cur_unit << ")";
1640                         }
1641
1642                         progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
1643                         m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv,
1644                                 texture_src, dtime, progress);
1645                 }
1646         }
1647
1648         return true;
1649 }
1650
1651
1652 /****************************************************************************/
1653 /****************************************************************************
1654  Run
1655  ****************************************************************************/
1656 /****************************************************************************/
1657
1658 inline void Game::updateInteractTimers(f32 dtime)
1659 {
1660         if (runData.nodig_delay_timer >= 0)
1661                 runData.nodig_delay_timer -= dtime;
1662
1663         if (runData.object_hit_delay_timer >= 0)
1664                 runData.object_hit_delay_timer -= dtime;
1665
1666         runData.time_from_last_punch += dtime;
1667 }
1668
1669
1670 /* returns false if game should exit, otherwise true
1671  */
1672 inline bool Game::checkConnection()
1673 {
1674         if (client->accessDenied()) {
1675                 *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
1676                 *reconnect_requested = client->reconnectRequested();
1677                 errorstream << *error_message << std::endl;
1678                 return false;
1679         }
1680
1681         return true;
1682 }
1683
1684
1685 /* returns false if game should exit, otherwise true
1686  */
1687 inline bool Game::handleCallbacks()
1688 {
1689         if (g_gamecallback->disconnect_requested) {
1690                 g_gamecallback->disconnect_requested = false;
1691                 return false;
1692         }
1693
1694         if (g_gamecallback->changepassword_requested) {
1695                 (new GUIPasswordChange(guienv, guiroot, -1,
1696                                        &g_menumgr, client, texture_src))->drop();
1697                 g_gamecallback->changepassword_requested = false;
1698         }
1699
1700         if (g_gamecallback->changevolume_requested) {
1701                 (new GUIVolumeChange(guienv, guiroot, -1,
1702                                      &g_menumgr, texture_src))->drop();
1703                 g_gamecallback->changevolume_requested = false;
1704         }
1705
1706         if (g_gamecallback->keyconfig_requested) {
1707                 (new GUIKeyChangeMenu(guienv, guiroot, -1,
1708                                       &g_menumgr, texture_src))->drop();
1709                 g_gamecallback->keyconfig_requested = false;
1710         }
1711
1712         if (g_gamecallback->keyconfig_changed) {
1713                 input->keycache.populate(); // update the cache with new settings
1714                 g_gamecallback->keyconfig_changed = false;
1715         }
1716
1717         return true;
1718 }
1719
1720
1721 void Game::processQueues()
1722 {
1723         texture_src->processQueue();
1724         itemdef_manager->processQueue(client);
1725         shader_src->processQueue();
1726 }
1727
1728 void Game::updateDebugState()
1729 {
1730         bool has_basic_debug = client->checkPrivilege("basic_debug");
1731         bool has_debug = client->checkPrivilege("debug");
1732
1733         if (m_game_ui->m_flags.show_basic_debug) {
1734                 if (!has_basic_debug) {
1735                         m_game_ui->m_flags.show_basic_debug = false;
1736                 }
1737         } else if (m_game_ui->m_flags.show_minimal_debug) {
1738                 if (has_basic_debug) {
1739                         m_game_ui->m_flags.show_basic_debug = true;
1740                 }
1741         }
1742         if (!has_basic_debug)
1743                 hud->disableBlockBounds();
1744         if (!has_debug)
1745                 draw_control->show_wireframe = false;
1746 }
1747
1748 void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
1749                 f32 dtime)
1750 {
1751         float profiler_print_interval =
1752                         g_settings->getFloat("profiler_print_interval");
1753         bool print_to_log = true;
1754
1755         if (profiler_print_interval == 0) {
1756                 print_to_log = false;
1757                 profiler_print_interval = 3;
1758         }
1759
1760         if (profiler_interval.step(dtime, profiler_print_interval)) {
1761                 if (print_to_log) {
1762                         infostream << "Profiler:" << std::endl;
1763                         g_profiler->print(infostream);
1764                 }
1765
1766                 m_game_ui->updateProfiler();
1767                 g_profiler->clear();
1768         }
1769
1770         // Update update graphs
1771         g_profiler->graphAdd("Time non-rendering [ms]",
1772                 draw_times.busy_time - stats.drawtime);
1773
1774         g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time);
1775         g_profiler->graphAdd("FPS", 1.0f / dtime);
1776 }
1777
1778 void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
1779                 f32 dtime)
1780 {
1781
1782         f32 jitter;
1783         Jitter *jp;
1784
1785         /* Time average and jitter calculation
1786          */
1787         jp = &stats->dtime_jitter;
1788         jp->avg = jp->avg * 0.96 + dtime * 0.04;
1789
1790         jitter = dtime - jp->avg;
1791
1792         if (jitter > jp->max)
1793                 jp->max = jitter;
1794
1795         jp->counter += dtime;
1796
1797         if (jp->counter > 0.0) {
1798                 jp->counter -= 3.0;
1799                 jp->max_sample = jp->max;
1800                 jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
1801                 jp->max = 0.0;
1802         }
1803
1804         /* Busytime average and jitter calculation
1805          */
1806         jp = &stats->busy_time_jitter;
1807         jp->avg = jp->avg + draw_times.busy_time * 0.02;
1808
1809         jitter = draw_times.busy_time - jp->avg;
1810
1811         if (jitter > jp->max)
1812                 jp->max = jitter;
1813         if (jitter < jp->min)
1814                 jp->min = jitter;
1815
1816         jp->counter += dtime;
1817
1818         if (jp->counter > 0.0) {
1819                 jp->counter -= 3.0;
1820                 jp->max_sample = jp->max;
1821                 jp->min_sample = jp->min;
1822                 jp->max = 0.0;
1823                 jp->min = 0.0;
1824         }
1825 }
1826
1827
1828
1829 /****************************************************************************
1830  Input handling
1831  ****************************************************************************/
1832
1833 void Game::processUserInput(f32 dtime)
1834 {
1835         // Reset input if window not active or some menu is active
1836         if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) {
1837                 input->clear();
1838 #ifdef HAVE_TOUCHSCREENGUI
1839                 g_touchscreengui->hide();
1840 #endif
1841         }
1842 #ifdef HAVE_TOUCHSCREENGUI
1843         else if (g_touchscreengui) {
1844                 /* on touchscreengui step may generate own input events which ain't
1845                  * what we want in case we just did clear them */
1846                 g_touchscreengui->show();
1847                 g_touchscreengui->step(dtime);
1848         }
1849 #endif
1850
1851         if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
1852                 gui_chat_console->closeConsoleAtOnce();
1853         }
1854
1855         // Input handler step() (used by the random input generator)
1856         input->step(dtime);
1857
1858 #ifdef __ANDROID__
1859         auto formspec = m_game_ui->getFormspecGUI();
1860         if (formspec)
1861                 formspec->getAndroidUIInput();
1862         else
1863                 handleAndroidChatInput();
1864 #endif
1865
1866         // Increase timer for double tap of "keymap_jump"
1867         if (m_cache_doubletap_jump && runData.jump_timer <= 0.2f)
1868                 runData.jump_timer += dtime;
1869
1870         processKeyInput();
1871         processItemSelection(&runData.new_playeritem);
1872 }
1873
1874
1875 void Game::processKeyInput()
1876 {
1877         if (wasKeyDown(KeyType::DROP)) {
1878                 dropSelectedItem(isKeyDown(KeyType::SNEAK));
1879         } else if (wasKeyDown(KeyType::AUTOFORWARD)) {
1880                 toggleAutoforward();
1881         } else if (wasKeyDown(KeyType::BACKWARD)) {
1882                 if (g_settings->getBool("continuous_forward"))
1883                         toggleAutoforward();
1884         } else if (wasKeyDown(KeyType::INVENTORY)) {
1885                 openInventory();
1886         } else if (input->cancelPressed()) {
1887 #ifdef __ANDROID__
1888                 m_android_chat_open = false;
1889 #endif
1890                 if (!gui_chat_console->isOpenInhibited()) {
1891                         showPauseMenu();
1892                 }
1893         } else if (wasKeyDown(KeyType::CHAT)) {
1894                 openConsole(0.2, L"");
1895         } else if (wasKeyDown(KeyType::CMD)) {
1896                 openConsole(0.2, L"/");
1897         } else if (wasKeyDown(KeyType::CMD_LOCAL)) {
1898                 if (client->modsLoaded())
1899                         openConsole(0.2, L".");
1900                 else
1901                         m_game_ui->showStatusText(wgettext("Client side scripting is disabled"));
1902         } else if (wasKeyDown(KeyType::CONSOLE)) {
1903                 openConsole(core::clamp(g_settings->getFloat("console_height"), 0.1f, 1.0f));
1904         } else if (wasKeyDown(KeyType::FREEMOVE)) {
1905                 toggleFreeMove();
1906         } else if (wasKeyDown(KeyType::JUMP)) {
1907                 toggleFreeMoveAlt();
1908         } else if (wasKeyDown(KeyType::PITCHMOVE)) {
1909                 togglePitchMove();
1910         } else if (wasKeyDown(KeyType::FASTMOVE)) {
1911                 toggleFast();
1912         } else if (wasKeyDown(KeyType::NOCLIP)) {
1913                 toggleNoClip();
1914 #if USE_SOUND
1915         } else if (wasKeyDown(KeyType::MUTE)) {
1916                 if (g_settings->getBool("enable_sound")) {
1917                         bool new_mute_sound = !g_settings->getBool("mute_sound");
1918                         g_settings->setBool("mute_sound", new_mute_sound);
1919                         if (new_mute_sound)
1920                                 m_game_ui->showTranslatedStatusText("Sound muted");
1921                         else
1922                                 m_game_ui->showTranslatedStatusText("Sound unmuted");
1923                 } else {
1924                         m_game_ui->showTranslatedStatusText("Sound system is disabled");
1925                 }
1926         } else if (wasKeyDown(KeyType::INC_VOLUME)) {
1927                 if (g_settings->getBool("enable_sound")) {
1928                         float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
1929                         g_settings->setFloat("sound_volume", new_volume);
1930                         std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
1931                         m_game_ui->showStatusText(msg);
1932                 } else {
1933                         m_game_ui->showTranslatedStatusText("Sound system is disabled");
1934                 }
1935         } else if (wasKeyDown(KeyType::DEC_VOLUME)) {
1936                 if (g_settings->getBool("enable_sound")) {
1937                         float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
1938                         g_settings->setFloat("sound_volume", new_volume);
1939                         std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
1940                         m_game_ui->showStatusText(msg);
1941                 } else {
1942                         m_game_ui->showTranslatedStatusText("Sound system is disabled");
1943                 }
1944 #else
1945         } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME)
1946                         || wasKeyDown(KeyType::DEC_VOLUME)) {
1947                 m_game_ui->showTranslatedStatusText("Sound system is not supported on this build");
1948 #endif
1949         } else if (wasKeyDown(KeyType::CINEMATIC)) {
1950                 toggleCinematic();
1951         } else if (wasKeyDown(KeyType::SCREENSHOT)) {
1952                 client->makeScreenshot();
1953         } else if (wasKeyDown(KeyType::TOGGLE_BLOCK_BOUNDS)) {
1954                 toggleBlockBounds();
1955         } else if (wasKeyDown(KeyType::TOGGLE_HUD)) {
1956                 m_game_ui->toggleHud();
1957         } else if (wasKeyDown(KeyType::MINIMAP)) {
1958                 toggleMinimap(isKeyDown(KeyType::SNEAK));
1959         } else if (wasKeyDown(KeyType::TOGGLE_CHAT)) {
1960                 m_game_ui->toggleChat();
1961         } else if (wasKeyDown(KeyType::TOGGLE_FOG)) {
1962                 toggleFog();
1963         } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
1964                 toggleUpdateCamera();
1965         } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) {
1966                 toggleDebug();
1967         } else if (wasKeyDown(KeyType::TOGGLE_PROFILER)) {
1968                 m_game_ui->toggleProfiler();
1969         } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) {
1970                 increaseViewRange();
1971         } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) {
1972                 decreaseViewRange();
1973         } else if (wasKeyDown(KeyType::RANGESELECT)) {
1974                 toggleFullViewRange();
1975         } else if (wasKeyDown(KeyType::ZOOM)) {
1976                 checkZoomEnabled();
1977         } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) {
1978                 quicktune->next();
1979         } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) {
1980                 quicktune->prev();
1981         } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) {
1982                 quicktune->inc();
1983         } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) {
1984                 quicktune->dec();
1985         }
1986
1987         if (!isKeyDown(KeyType::JUMP) && runData.reset_jump_timer) {
1988                 runData.reset_jump_timer = false;
1989                 runData.jump_timer = 0.0f;
1990         }
1991
1992         if (quicktune->hasMessage()) {
1993                 m_game_ui->showStatusText(utf8_to_wide(quicktune->getMessage()));
1994         }
1995 }
1996
1997 void Game::processItemSelection(u16 *new_playeritem)
1998 {
1999         LocalPlayer *player = client->getEnv().getLocalPlayer();
2000
2001         /* Item selection using mouse wheel
2002          */
2003         *new_playeritem = player->getWieldIndex();
2004
2005         s32 wheel = input->getMouseWheel();
2006         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
2007                     player->hud_hotbar_itemcount - 1);
2008
2009         s32 dir = wheel;
2010
2011         if (wasKeyDown(KeyType::HOTBAR_NEXT))
2012                 dir = -1;
2013
2014         if (wasKeyDown(KeyType::HOTBAR_PREV))
2015                 dir = 1;
2016
2017         if (dir < 0)
2018                 *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
2019         else if (dir > 0)
2020                 *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
2021         // else dir == 0
2022
2023         /* Item selection using hotbar slot keys
2024          */
2025         for (u16 i = 0; i <= max_item; i++) {
2026                 if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) {
2027                         *new_playeritem = i;
2028                         break;
2029                 }
2030         }
2031 }
2032
2033
2034 void Game::dropSelectedItem(bool single_item)
2035 {
2036         IDropAction *a = new IDropAction();
2037         a->count = single_item ? 1 : 0;
2038         a->from_inv.setCurrentPlayer();
2039         a->from_list = "main";
2040         a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex();
2041         client->inventoryAction(a);
2042 }
2043
2044
2045 void Game::openInventory()
2046 {
2047         /*
2048          * Don't permit to open inventory is CAO or player doesn't exists.
2049          * This prevent showing an empty inventory at player load
2050          */
2051
2052         LocalPlayer *player = client->getEnv().getLocalPlayer();
2053         if (!player || !player->getCAO())
2054                 return;
2055
2056         infostream << "Game: Launching inventory" << std::endl;
2057
2058         PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
2059
2060         InventoryLocation inventoryloc;
2061         inventoryloc.setCurrentPlayer();
2062
2063         if (!client->modsLoaded()
2064                         || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
2065                 TextDest *txt_dst = new TextDestPlayerInventory(client);
2066                 auto *&formspec = m_game_ui->updateFormspec("");
2067                 GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
2068                         &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
2069
2070                 formspec->setFormSpec(fs_src->getForm(), inventoryloc);
2071         }
2072 }
2073
2074
2075 void Game::openConsole(float scale, const wchar_t *line)
2076 {
2077         assert(scale > 0.0f && scale <= 1.0f);
2078
2079 #ifdef __ANDROID__
2080         porting::showInputDialog(gettext("ok"), "", "", 2);
2081         m_android_chat_open = true;
2082 #else
2083         if (gui_chat_console->isOpenInhibited())
2084                 return;
2085         gui_chat_console->openConsole(scale);
2086         if (line) {
2087                 gui_chat_console->setCloseOnEnter(true);
2088                 gui_chat_console->replaceAndAddToHistory(line);
2089         }
2090 #endif
2091 }
2092
2093 #ifdef __ANDROID__
2094 void Game::handleAndroidChatInput()
2095 {
2096         if (m_android_chat_open && porting::getInputDialogState() == 0) {
2097                 std::string text = porting::getInputDialogValue();
2098                 client->typeChatMessage(utf8_to_wide(text));
2099                 m_android_chat_open = false;
2100         }
2101 }
2102 #endif
2103
2104
2105 void Game::toggleFreeMove()
2106 {
2107         bool free_move = !g_settings->getBool("free_move");
2108         g_settings->set("free_move", bool_to_cstr(free_move));
2109
2110         if (free_move) {
2111                 if (client->checkPrivilege("fly")) {
2112                         m_game_ui->showTranslatedStatusText("Fly mode enabled");
2113                 } else {
2114                         m_game_ui->showTranslatedStatusText("Fly mode enabled (note: no 'fly' privilege)");
2115                 }
2116         } else {
2117                 m_game_ui->showTranslatedStatusText("Fly mode disabled");
2118         }
2119 }
2120
2121 void Game::toggleFreeMoveAlt()
2122 {
2123         if (m_cache_doubletap_jump && runData.jump_timer < 0.2f)
2124                 toggleFreeMove();
2125
2126         runData.reset_jump_timer = true;
2127 }
2128
2129
2130 void Game::togglePitchMove()
2131 {
2132         bool pitch_move = !g_settings->getBool("pitch_move");
2133         g_settings->set("pitch_move", bool_to_cstr(pitch_move));
2134
2135         if (pitch_move) {
2136                 m_game_ui->showTranslatedStatusText("Pitch move mode enabled");
2137         } else {
2138                 m_game_ui->showTranslatedStatusText("Pitch move mode disabled");
2139         }
2140 }
2141
2142
2143 void Game::toggleFast()
2144 {
2145         bool fast_move = !g_settings->getBool("fast_move");
2146         bool has_fast_privs = client->checkPrivilege("fast");
2147         g_settings->set("fast_move", bool_to_cstr(fast_move));
2148
2149         if (fast_move) {
2150                 if (has_fast_privs) {
2151                         m_game_ui->showTranslatedStatusText("Fast mode enabled");
2152                 } else {
2153                         m_game_ui->showTranslatedStatusText("Fast mode enabled (note: no 'fast' privilege)");
2154                 }
2155         } else {
2156                 m_game_ui->showTranslatedStatusText("Fast mode disabled");
2157         }
2158
2159 #ifdef HAVE_TOUCHSCREENGUI
2160         m_cache_hold_aux1 = fast_move && has_fast_privs;
2161 #endif
2162 }
2163
2164
2165 void Game::toggleNoClip()
2166 {
2167         bool noclip = !g_settings->getBool("noclip");
2168         g_settings->set("noclip", bool_to_cstr(noclip));
2169
2170         if (noclip) {
2171                 if (client->checkPrivilege("noclip")) {
2172                         m_game_ui->showTranslatedStatusText("Noclip mode enabled");
2173                 } else {
2174                         m_game_ui->showTranslatedStatusText("Noclip mode enabled (note: no 'noclip' privilege)");
2175                 }
2176         } else {
2177                 m_game_ui->showTranslatedStatusText("Noclip mode disabled");
2178         }
2179 }
2180
2181 void Game::toggleCinematic()
2182 {
2183         bool cinematic = !g_settings->getBool("cinematic");
2184         g_settings->set("cinematic", bool_to_cstr(cinematic));
2185
2186         if (cinematic)
2187                 m_game_ui->showTranslatedStatusText("Cinematic mode enabled");
2188         else
2189                 m_game_ui->showTranslatedStatusText("Cinematic mode disabled");
2190 }
2191
2192 void Game::toggleBlockBounds()
2193 {
2194         if (client->checkPrivilege("basic_debug")) {
2195                 enum Hud::BlockBoundsMode newmode = hud->toggleBlockBounds();
2196                 switch (newmode) {
2197                         case Hud::BLOCK_BOUNDS_OFF:
2198                                 m_game_ui->showTranslatedStatusText("Block bounds hidden");
2199                                 break;
2200                         case Hud::BLOCK_BOUNDS_CURRENT:
2201                                 m_game_ui->showTranslatedStatusText("Block bounds shown for current block");
2202                                 break;
2203                         case Hud::BLOCK_BOUNDS_NEAR:
2204                                 m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks");
2205                                 break;
2206                         case Hud::BLOCK_BOUNDS_MAX:
2207                                 m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks");
2208                                 break;
2209                         default:
2210                                 break;
2211                 }
2212
2213         } else {
2214                 m_game_ui->showTranslatedStatusText("Can't show block bounds (need 'basic_debug' privilege)");
2215         }
2216 }
2217
2218 // Autoforward by toggling continuous forward.
2219 void Game::toggleAutoforward()
2220 {
2221         bool autorun_enabled = !g_settings->getBool("continuous_forward");
2222         g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled));
2223
2224         if (autorun_enabled)
2225                 m_game_ui->showTranslatedStatusText("Automatic forward enabled");
2226         else
2227                 m_game_ui->showTranslatedStatusText("Automatic forward disabled");
2228 }
2229
2230 void Game::toggleMinimap(bool shift_pressed)
2231 {
2232         if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap"))
2233                 return;
2234
2235         if (shift_pressed)
2236                 mapper->toggleMinimapShape();
2237         else
2238                 mapper->nextMode();
2239
2240         // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here
2241
2242         // Not so satisying code to keep compatibility with old fixed mode system
2243         // -->
2244         u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
2245
2246         if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) {
2247                 m_game_ui->m_flags.show_minimap = false;
2248         } else {
2249
2250         // If radar is disabled, try to find a non radar mode or fall back to 0
2251                 if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
2252                         while (mapper->getModeIndex() &&
2253                                         mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
2254                                 mapper->nextMode();
2255
2256                 m_game_ui->m_flags.show_minimap = mapper->getModeDef().type !=
2257                                 MINIMAP_TYPE_OFF;
2258         }
2259         // <--
2260         // End of 'not so satifying code'
2261         if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ||
2262                         (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP)))
2263                 m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
2264         else
2265                 m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod");
2266 }
2267
2268 void Game::toggleFog()
2269 {
2270         bool fog_enabled = g_settings->getBool("enable_fog");
2271         g_settings->setBool("enable_fog", !fog_enabled);
2272         if (fog_enabled)
2273                 m_game_ui->showTranslatedStatusText("Fog disabled");
2274         else
2275                 m_game_ui->showTranslatedStatusText("Fog enabled");
2276 }
2277
2278
2279 void Game::toggleDebug()
2280 {
2281         // Initial: No debug info
2282         // 1x toggle: Debug text
2283         // 2x toggle: Debug text with profiler graph
2284         // 3x toggle: Debug text and wireframe (needs "debug" priv)
2285         // Next toggle: Back to initial
2286         //
2287         // The debug text can be in 2 modes: minimal and basic.
2288         // * Minimal: Only technical client info that not gameplay-relevant
2289         // * Basic: Info that might give gameplay advantage, e.g. pos, angle
2290         // Basic mode is used when player has "basic_debug" priv,
2291         // otherwise the Minimal mode is used.
2292         if (!m_game_ui->m_flags.show_minimal_debug) {
2293                 m_game_ui->m_flags.show_minimal_debug = true;
2294                 if (client->checkPrivilege("basic_debug")) {
2295                         m_game_ui->m_flags.show_basic_debug = true;
2296                 }
2297                 m_game_ui->m_flags.show_profiler_graph = false;
2298                 draw_control->show_wireframe = false;
2299                 m_game_ui->showTranslatedStatusText("Debug info shown");
2300         } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) {
2301                 if (client->checkPrivilege("basic_debug")) {
2302                         m_game_ui->m_flags.show_basic_debug = true;
2303                 }
2304                 m_game_ui->m_flags.show_profiler_graph = true;
2305                 m_game_ui->showTranslatedStatusText("Profiler graph shown");
2306         } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) {
2307                 if (client->checkPrivilege("basic_debug")) {
2308                         m_game_ui->m_flags.show_basic_debug = true;
2309                 }
2310                 m_game_ui->m_flags.show_profiler_graph = false;
2311                 draw_control->show_wireframe = true;
2312                 m_game_ui->showTranslatedStatusText("Wireframe shown");
2313         } else {
2314                 m_game_ui->m_flags.show_minimal_debug = false;
2315                 m_game_ui->m_flags.show_basic_debug = false;
2316                 m_game_ui->m_flags.show_profiler_graph = false;
2317                 draw_control->show_wireframe = false;
2318                 if (client->checkPrivilege("debug")) {
2319                         m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden");
2320                 } else {
2321                         m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden");
2322                 }
2323         }
2324 }
2325
2326
2327 void Game::toggleUpdateCamera()
2328 {
2329         m_flags.disable_camera_update = !m_flags.disable_camera_update;
2330         if (m_flags.disable_camera_update)
2331                 m_game_ui->showTranslatedStatusText("Camera update disabled");
2332         else
2333                 m_game_ui->showTranslatedStatusText("Camera update enabled");
2334 }
2335
2336
2337 void Game::increaseViewRange()
2338 {
2339         s16 range = g_settings->getS16("viewing_range");
2340         s16 range_new = range + 10;
2341
2342         if (range_new > 4000) {
2343                 range_new = 4000;
2344                 std::wstring msg = fwgettext("Viewing range is at maximum: %d", range_new);
2345                 m_game_ui->showStatusText(msg);
2346         } else {
2347                 std::wstring msg = fwgettext("Viewing range changed to %d", range_new);
2348                 m_game_ui->showStatusText(msg);
2349         }
2350         g_settings->set("viewing_range", itos(range_new));
2351 }
2352
2353
2354 void Game::decreaseViewRange()
2355 {
2356         s16 range = g_settings->getS16("viewing_range");
2357         s16 range_new = range - 10;
2358
2359         if (range_new < 20) {
2360                 range_new = 20;
2361                 std::wstring msg = fwgettext("Viewing range is at minimum: %d", range_new);
2362                 m_game_ui->showStatusText(msg);
2363         } else {
2364                 std::wstring msg = fwgettext("Viewing range changed to %d", range_new);
2365                 m_game_ui->showStatusText(msg);
2366         }
2367         g_settings->set("viewing_range", itos(range_new));
2368 }
2369
2370
2371 void Game::toggleFullViewRange()
2372 {
2373         draw_control->range_all = !draw_control->range_all;
2374         if (draw_control->range_all)
2375                 m_game_ui->showTranslatedStatusText("Enabled unlimited viewing range");
2376         else
2377                 m_game_ui->showTranslatedStatusText("Disabled unlimited viewing range");
2378 }
2379
2380
2381 void Game::checkZoomEnabled()
2382 {
2383         LocalPlayer *player = client->getEnv().getLocalPlayer();
2384         if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f)
2385                 m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod");
2386 }
2387
2388 void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
2389 {
2390         if ((device->isWindowActive() && device->isWindowFocused()
2391                         && !isMenuActive()) || input->isRandom()) {
2392
2393 #ifndef __ANDROID__
2394                 if (!input->isRandom()) {
2395                         // Mac OSX gets upset if this is set every frame
2396                         if (device->getCursorControl()->isVisible())
2397                                 device->getCursorControl()->setVisible(false);
2398                 }
2399 #endif
2400
2401                 if (m_first_loop_after_window_activation) {
2402                         m_first_loop_after_window_activation = false;
2403
2404                         input->setMousePos(driver->getScreenSize().Width / 2,
2405                                 driver->getScreenSize().Height / 2);
2406                 } else {
2407                         updateCameraOrientation(cam, dtime);
2408                 }
2409
2410         } else {
2411
2412 #ifndef ANDROID
2413                 // Mac OSX gets upset if this is set every frame
2414                 if (!device->getCursorControl()->isVisible())
2415                         device->getCursorControl()->setVisible(true);
2416 #endif
2417
2418                 m_first_loop_after_window_activation = true;
2419
2420         }
2421 }
2422
2423 // Get the factor to multiply with sensitivity to get the same mouse/joystick
2424 // responsiveness independently of FOV.
2425 f32 Game::getSensitivityScaleFactor() const
2426 {
2427         f32 fov_y = client->getCamera()->getFovY();
2428
2429         // Multiply by a constant such that it becomes 1.0 at 72 degree FOV and
2430         // 16:9 aspect ratio to minimize disruption of existing sensitivity
2431         // settings.
2432         return tan(fov_y / 2.0f) * 1.3763818698f;
2433 }
2434
2435 void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
2436 {
2437 #ifdef HAVE_TOUCHSCREENGUI
2438         if (g_touchscreengui) {
2439                 cam->camera_yaw   += g_touchscreengui->getYawChange();
2440                 cam->camera_pitch  = g_touchscreengui->getPitch();
2441         } else {
2442 #endif
2443                 v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
2444                 v2s32 dist = input->getMousePos() - center;
2445
2446                 if (m_invert_mouse || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
2447                         dist.Y = -dist.Y;
2448                 }
2449
2450                 f32 sens_scale = getSensitivityScaleFactor();
2451                 cam->camera_yaw   -= dist.X * m_cache_mouse_sensitivity * sens_scale;
2452                 cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity * sens_scale;
2453
2454                 if (dist.X != 0 || dist.Y != 0)
2455                         input->setMousePos(center.X, center.Y);
2456 #ifdef HAVE_TOUCHSCREENGUI
2457         }
2458 #endif
2459
2460         if (m_cache_enable_joysticks) {
2461                 f32 sens_scale = getSensitivityScaleFactor();
2462                 f32 c = m_cache_joystick_frustum_sensitivity * dtime * sens_scale;
2463                 cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c;
2464                 cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c;
2465         }
2466
2467         cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
2468 }
2469
2470
2471 void Game::updatePlayerControl(const CameraOrientation &cam)
2472 {
2473         LocalPlayer *player = client->getEnv().getLocalPlayer();
2474
2475         //TimeTaker tt("update player control", NULL, PRECISION_NANO);
2476
2477         PlayerControl control(
2478                 isKeyDown(KeyType::JUMP) || player->getAutojump(),
2479                 isKeyDown(KeyType::AUX1),
2480                 isKeyDown(KeyType::SNEAK),
2481                 isKeyDown(KeyType::ZOOM),
2482                 isKeyDown(KeyType::DIG),
2483                 isKeyDown(KeyType::PLACE),
2484                 cam.camera_pitch,
2485                 cam.camera_yaw,
2486                 input->getMovementSpeed(),
2487                 input->getMovementDirection()
2488         );
2489
2490         // autoforward if set: move towards pointed position at maximum speed
2491         if (player->getPlayerSettings().continuous_forward &&
2492                         client->activeObjectsReceived() && !player->isDead()) {
2493                 control.movement_speed = 1.0f;
2494                 control.movement_direction = 0.0f;
2495         }
2496
2497 #ifdef HAVE_TOUCHSCREENGUI
2498         /* For touch, simulate holding down AUX1 (fast move) if the user has
2499          * the fast_move setting toggled on. If there is an aux1 key defined for
2500          * touch then its meaning is inverted (i.e. holding aux1 means walk and
2501          * not fast)
2502          */
2503         if (m_cache_hold_aux1) {
2504                 control.aux1 = control.aux1 ^ true;
2505         }
2506 #endif
2507
2508         u32 keypress_bits = (
2509                         ( (u32)(control.jump  & 0x1) << 4) |
2510                         ( (u32)(control.aux1  & 0x1) << 5) |
2511                         ( (u32)(control.sneak & 0x1) << 6) |
2512                         ( (u32)(control.dig   & 0x1) << 7) |
2513                         ( (u32)(control.place & 0x1) << 8) |
2514                         ( (u32)(control.zoom  & 0x1) << 9)
2515                 );
2516
2517         // Set direction keys to ensure mod compatibility
2518         if (control.movement_speed > 0.001f) {
2519                 float absolute_direction;
2520
2521                 // Check in original orientation (absolute value indicates forward / backward)
2522                 absolute_direction = abs(control.movement_direction);
2523                 if (absolute_direction < (3.0f / 8.0f * M_PI))
2524                         keypress_bits |= (u32)(0x1 << 0); // Forward
2525                 if (absolute_direction > (5.0f / 8.0f * M_PI))
2526                         keypress_bits |= (u32)(0x1 << 1); // Backward
2527
2528                 // Rotate entire coordinate system by 90 degrees (absolute value indicates left / right)
2529                 absolute_direction = control.movement_direction + M_PI_2;
2530                 if (absolute_direction >= M_PI)
2531                         absolute_direction -= 2 * M_PI;
2532                 absolute_direction = abs(absolute_direction);
2533                 if (absolute_direction < (3.0f / 8.0f * M_PI))
2534                         keypress_bits |= (u32)(0x1 << 2); // Left
2535                 if (absolute_direction > (5.0f / 8.0f * M_PI))
2536                         keypress_bits |= (u32)(0x1 << 3); // Right
2537         }
2538
2539         client->setPlayerControl(control);
2540         player->keyPressed = keypress_bits;
2541
2542         //tt.stop();
2543 }
2544
2545
2546 inline void Game::step(f32 *dtime)
2547 {
2548         bool can_be_and_is_paused =
2549                         (simple_singleplayer_mode && g_menumgr.pausesGame());
2550
2551         if (can_be_and_is_paused) { // This is for a singleplayer server
2552                 *dtime = 0;             // No time passes
2553         } else {
2554                 if (simple_singleplayer_mode && !paused_animated_nodes.empty())
2555                         resumeAnimation();
2556
2557                 if (server)
2558                         server->step(*dtime);
2559
2560                 client->step(*dtime);
2561         }
2562 }
2563
2564 static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) {
2565         if (!node)
2566                 return;
2567         for (auto &&child: node->getChildren())
2568                 pauseNodeAnimation(paused, child);
2569         if (node->getType() != scene::ESNT_ANIMATED_MESH)
2570                 return;
2571         auto animated_node = static_cast<scene::IAnimatedMeshSceneNode *>(node);
2572         float speed = animated_node->getAnimationSpeed();
2573         if (!speed)
2574                 return;
2575         paused.push_back({grab(animated_node), speed});
2576         animated_node->setAnimationSpeed(0.0f);
2577 }
2578
2579 void Game::pauseAnimation()
2580 {
2581         pauseNodeAnimation(paused_animated_nodes, smgr->getRootSceneNode());
2582 }
2583
2584 void Game::resumeAnimation()
2585 {
2586         for (auto &&pair: paused_animated_nodes)
2587                 pair.first->setAnimationSpeed(pair.second);
2588         paused_animated_nodes.clear();
2589 }
2590
2591 const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
2592         {&Game::handleClientEvent_None},
2593         {&Game::handleClientEvent_PlayerDamage},
2594         {&Game::handleClientEvent_PlayerForceMove},
2595         {&Game::handleClientEvent_Deathscreen},
2596         {&Game::handleClientEvent_ShowFormSpec},
2597         {&Game::handleClientEvent_ShowLocalFormSpec},
2598         {&Game::handleClientEvent_HandleParticleEvent},
2599         {&Game::handleClientEvent_HandleParticleEvent},
2600         {&Game::handleClientEvent_HandleParticleEvent},
2601         {&Game::handleClientEvent_HudAdd},
2602         {&Game::handleClientEvent_HudRemove},
2603         {&Game::handleClientEvent_HudChange},
2604         {&Game::handleClientEvent_SetSky},
2605         {&Game::handleClientEvent_SetSun},
2606         {&Game::handleClientEvent_SetMoon},
2607         {&Game::handleClientEvent_SetStars},
2608         {&Game::handleClientEvent_OverrideDayNigthRatio},
2609         {&Game::handleClientEvent_CloudParams},
2610 };
2611
2612 void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam)
2613 {
2614         FATAL_ERROR("ClientEvent type None received");
2615 }
2616
2617 void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam)
2618 {
2619         if (client->modsLoaded())
2620                 client->getScript()->on_damage_taken(event->player_damage.amount);
2621
2622         // Damage flash and hurt tilt are not used at death
2623         if (client->getHP() > 0) {
2624                 LocalPlayer *player = client->getEnv().getLocalPlayer();
2625
2626                 f32 hp_max = player->getCAO() ?
2627                         player->getCAO()->getProperties().hp_max : PLAYER_MAX_HP_DEFAULT;
2628                 f32 damage_ratio = event->player_damage.amount / hp_max;
2629
2630                 runData.damage_flash += 95.0f + 64.f * damage_ratio;
2631                 runData.damage_flash = MYMIN(runData.damage_flash, 127.0f);
2632
2633                 player->hurt_tilt_timer = 1.5f;
2634                 player->hurt_tilt_strength =
2635                         rangelim(damage_ratio * 5.0f, 1.0f, 4.0f);
2636         }
2637
2638         // Play damage sound
2639         client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_DAMAGE));
2640 }
2641
2642 void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam)
2643 {
2644         cam->camera_yaw = event->player_force_move.yaw;
2645         cam->camera_pitch = event->player_force_move.pitch;
2646 }
2647
2648 void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam)
2649 {
2650         // If client scripting is enabled, deathscreen is handled by CSM code in
2651         // builtin/client/init.lua
2652         if (client->modsLoaded())
2653                 client->getScript()->on_death();
2654         else
2655                 showDeathFormspec();
2656
2657         /* Handle visualization */
2658         LocalPlayer *player = client->getEnv().getLocalPlayer();
2659         runData.damage_flash = 0;
2660         player->hurt_tilt_timer = 0;
2661         player->hurt_tilt_strength = 0;
2662 }
2663
2664 void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam)
2665 {
2666         if (event->show_formspec.formspec->empty()) {
2667                 auto formspec = m_game_ui->getFormspecGUI();
2668                 if (formspec && (event->show_formspec.formname->empty()
2669                                 || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) {
2670                         formspec->quitMenu();
2671                 }
2672         } else {
2673                 FormspecFormSource *fs_src =
2674                         new FormspecFormSource(*(event->show_formspec.formspec));
2675                 TextDestPlayerInventory *txt_dst =
2676                         new TextDestPlayerInventory(client, *(event->show_formspec.formname));
2677
2678                 auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname));
2679                 GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
2680                         &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
2681         }
2682
2683         delete event->show_formspec.formspec;
2684         delete event->show_formspec.formname;
2685 }
2686
2687 void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam)
2688 {
2689         FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
2690         LocalFormspecHandler *txt_dst =
2691                 new LocalFormspecHandler(*event->show_formspec.formname, client);
2692         GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(),
2693                         &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
2694
2695         delete event->show_formspec.formspec;
2696         delete event->show_formspec.formname;
2697 }
2698
2699 void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event,
2700                 CameraOrientation *cam)
2701 {
2702         LocalPlayer *player = client->getEnv().getLocalPlayer();
2703         client->getParticleManager()->handleParticleEvent(event, client, player);
2704 }
2705
2706 void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
2707 {
2708         LocalPlayer *player = client->getEnv().getLocalPlayer();
2709
2710         u32 server_id = event->hudadd->server_id;
2711         // ignore if we already have a HUD with that ID
2712         auto i = m_hud_server_to_client.find(server_id);
2713         if (i != m_hud_server_to_client.end()) {
2714                 delete event->hudadd;
2715                 return;
2716         }
2717
2718         HudElement *e = new HudElement;
2719         e->type   = static_cast<HudElementType>(event->hudadd->type);
2720         e->pos    = event->hudadd->pos;
2721         e->name   = event->hudadd->name;
2722         e->scale  = event->hudadd->scale;
2723         e->text   = event->hudadd->text;
2724         e->number = event->hudadd->number;
2725         e->item   = event->hudadd->item;
2726         e->dir    = event->hudadd->dir;
2727         e->align  = event->hudadd->align;
2728         e->offset = event->hudadd->offset;
2729         e->world_pos = event->hudadd->world_pos;
2730         e->size      = event->hudadd->size;
2731         e->z_index   = event->hudadd->z_index;
2732         e->text2     = event->hudadd->text2;
2733         e->style     = event->hudadd->style;
2734         m_hud_server_to_client[server_id] = player->addHud(e);
2735
2736         delete event->hudadd;
2737 }
2738
2739 void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam)
2740 {
2741         LocalPlayer *player = client->getEnv().getLocalPlayer();
2742
2743         auto i = m_hud_server_to_client.find(event->hudrm.id);
2744         if (i != m_hud_server_to_client.end()) {
2745                 HudElement *e = player->removeHud(i->second);
2746                 delete e;
2747                 m_hud_server_to_client.erase(i);
2748         }
2749
2750 }
2751
2752 void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam)
2753 {
2754         LocalPlayer *player = client->getEnv().getLocalPlayer();
2755
2756         HudElement *e = nullptr;
2757
2758         auto i = m_hud_server_to_client.find(event->hudchange->id);
2759         if (i != m_hud_server_to_client.end()) {
2760                 e = player->getHud(i->second);
2761         }
2762
2763         if (e == nullptr) {
2764                 delete event->hudchange;
2765                 return;
2766         }
2767
2768 #define CASE_SET(statval, prop, dataprop) \
2769         case statval: \
2770                 e->prop = event->hudchange->dataprop; \
2771                 break
2772
2773         switch (event->hudchange->stat) {
2774                 CASE_SET(HUD_STAT_POS, pos, v2fdata);
2775
2776                 CASE_SET(HUD_STAT_NAME, name, sdata);
2777
2778                 CASE_SET(HUD_STAT_SCALE, scale, v2fdata);
2779
2780                 CASE_SET(HUD_STAT_TEXT, text, sdata);
2781
2782                 CASE_SET(HUD_STAT_NUMBER, number, data);
2783
2784                 CASE_SET(HUD_STAT_ITEM, item, data);
2785
2786                 CASE_SET(HUD_STAT_DIR, dir, data);
2787
2788                 CASE_SET(HUD_STAT_ALIGN, align, v2fdata);
2789
2790                 CASE_SET(HUD_STAT_OFFSET, offset, v2fdata);
2791
2792                 CASE_SET(HUD_STAT_WORLD_POS, world_pos, v3fdata);
2793
2794                 CASE_SET(HUD_STAT_SIZE, size, v2s32data);
2795
2796                 CASE_SET(HUD_STAT_Z_INDEX, z_index, data);
2797
2798                 CASE_SET(HUD_STAT_TEXT2, text2, sdata);
2799
2800                 CASE_SET(HUD_STAT_STYLE, style, data);
2801         }
2802
2803 #undef CASE_SET
2804
2805         delete event->hudchange;
2806 }
2807
2808 void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
2809 {
2810         sky->setVisible(false);
2811         // Whether clouds are visible in front of a custom skybox.
2812         sky->setCloudsEnabled(event->set_sky->clouds);
2813
2814         if (skybox) {
2815                 skybox->remove();
2816                 skybox = NULL;
2817         }
2818         // Clear the old textures out in case we switch rendering type.
2819         sky->clearSkyboxTextures();
2820         // Handle according to type
2821         if (event->set_sky->type == "regular") {
2822                 // Shows the mesh skybox
2823                 sky->setVisible(true);
2824                 // Update mesh based skybox colours if applicable.
2825                 sky->setSkyColors(event->set_sky->sky_color);
2826                 sky->setHorizonTint(
2827                         event->set_sky->fog_sun_tint,
2828                         event->set_sky->fog_moon_tint,
2829                         event->set_sky->fog_tint_type
2830                 );
2831         } else if (event->set_sky->type == "skybox" &&
2832                         event->set_sky->textures.size() == 6) {
2833                 // Disable the dyanmic mesh skybox:
2834                 sky->setVisible(false);
2835                 // Set fog colors:
2836                 sky->setFallbackBgColor(event->set_sky->bgcolor);
2837                 // Set sunrise and sunset fog tinting:
2838                 sky->setHorizonTint(
2839                         event->set_sky->fog_sun_tint,
2840                         event->set_sky->fog_moon_tint,
2841                         event->set_sky->fog_tint_type
2842                 );
2843                 // Add textures to skybox.
2844                 for (int i = 0; i < 6; i++)
2845                         sky->addTextureToSkybox(event->set_sky->textures[i], i, texture_src);
2846         } else {
2847                 // Handle everything else as plain color.
2848                 if (event->set_sky->type != "plain")
2849                         infostream << "Unknown sky type: "
2850                                 << (event->set_sky->type) << std::endl;
2851                 sky->setVisible(false);
2852                 sky->setFallbackBgColor(event->set_sky->bgcolor);
2853                 // Disable directional sun/moon tinting on plain or invalid skyboxes.
2854                 sky->setHorizonTint(
2855                         event->set_sky->bgcolor,
2856                         event->set_sky->bgcolor,
2857                         "custom"
2858                 );
2859         }
2860
2861         delete event->set_sky;
2862 }
2863
2864 void Game::handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam)
2865 {
2866         sky->setSunVisible(event->sun_params->visible);
2867         sky->setSunTexture(event->sun_params->texture,
2868                 event->sun_params->tonemap, texture_src);
2869         sky->setSunScale(event->sun_params->scale);
2870         sky->setSunriseVisible(event->sun_params->sunrise_visible);
2871         sky->setSunriseTexture(event->sun_params->sunrise, texture_src);
2872         delete event->sun_params;
2873 }
2874
2875 void Game::handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam)
2876 {
2877         sky->setMoonVisible(event->moon_params->visible);
2878         sky->setMoonTexture(event->moon_params->texture,
2879                 event->moon_params->tonemap, texture_src);
2880         sky->setMoonScale(event->moon_params->scale);
2881         delete event->moon_params;
2882 }
2883
2884 void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam)
2885 {
2886         sky->setStarsVisible(event->star_params->visible);
2887         sky->setStarCount(event->star_params->count, false);
2888         sky->setStarColor(event->star_params->starcolor);
2889         sky->setStarScale(event->star_params->scale);
2890         delete event->star_params;
2891 }
2892
2893 void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
2894                 CameraOrientation *cam)
2895 {
2896         client->getEnv().setDayNightRatioOverride(
2897                 event->override_day_night_ratio.do_override,
2898                 event->override_day_night_ratio.ratio_f * 1000.0f);
2899 }
2900
2901 void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam)
2902 {
2903         if (!clouds)
2904                 return;
2905
2906         clouds->setDensity(event->cloud_params.density);
2907         clouds->setColorBright(video::SColor(event->cloud_params.color_bright));
2908         clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient));
2909         clouds->setHeight(event->cloud_params.height);
2910         clouds->setThickness(event->cloud_params.thickness);
2911         clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y));
2912 }
2913
2914 void Game::processClientEvents(CameraOrientation *cam)
2915 {
2916         while (client->hasClientEvents()) {
2917                 std::unique_ptr<ClientEvent> event(client->getClientEvent());
2918                 FATAL_ERROR_IF(event->type >= CLIENTEVENT_MAX, "Invalid clientevent type");
2919                 const ClientEventHandler& evHandler = clientEventHandler[event->type];
2920                 (this->*evHandler.handler)(event.get(), cam);
2921         }
2922 }
2923
2924 void Game::updateChat(f32 dtime)
2925 {
2926         // Get new messages from error log buffer
2927         while (!m_chat_log_buf.empty())
2928                 chat_backend->addMessage(L"", utf8_to_wide(m_chat_log_buf.get()));
2929
2930         // Get new messages from client
2931         std::wstring message;
2932         while (client->getChatMessage(message)) {
2933                 chat_backend->addUnparsedMessage(message);
2934         }
2935
2936         // Remove old messages
2937         chat_backend->step(dtime);
2938
2939         // Display all messages in a static text element
2940         auto &buf = chat_backend->getRecentBuffer();
2941         if (buf.getLinesModified()) {
2942                 buf.resetLinesModified();
2943                 m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount());
2944         }
2945
2946         // Make sure that the size is still correct
2947         m_game_ui->updateChatSize();
2948 }
2949
2950 void Game::updateCamera(u32 busy_time, f32 dtime)
2951 {
2952         LocalPlayer *player = client->getEnv().getLocalPlayer();
2953
2954         /*
2955                 For interaction purposes, get info about the held item
2956                 - What item is it?
2957                 - Is it a usable item?
2958                 - Can it point to liquids?
2959         */
2960         ItemStack playeritem;
2961         {
2962                 ItemStack selected, hand;
2963                 playeritem = player->getWieldedItem(&selected, &hand);
2964         }
2965
2966         ToolCapabilities playeritem_toolcap =
2967                 playeritem.getToolCapabilities(itemdef_manager);
2968
2969         v3s16 old_camera_offset = camera->getOffset();
2970
2971         if (wasKeyDown(KeyType::CAMERA_MODE)) {
2972                 GenericCAO *playercao = player->getCAO();
2973
2974                 // If playercao not loaded, don't change camera
2975                 if (!playercao)
2976                         return;
2977
2978                 camera->toggleCameraMode();
2979
2980                 // Make the player visible depending on camera mode.
2981                 playercao->updateMeshCulling();
2982                 playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
2983         }
2984
2985         float full_punch_interval = playeritem_toolcap.full_punch_interval;
2986         float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval;
2987
2988         tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
2989         camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio);
2990         camera->step(dtime);
2991
2992         v3f camera_position = camera->getPosition();
2993         v3f camera_direction = camera->getDirection();
2994         f32 camera_fov = camera->getFovMax();
2995         v3s16 camera_offset = camera->getOffset();
2996
2997         m_camera_offset_changed = (camera_offset != old_camera_offset);
2998
2999         if (!m_flags.disable_camera_update) {
3000                 client->getEnv().getClientMap().updateCamera(camera_position,
3001                                 camera_direction, camera_fov, camera_offset);
3002
3003                 if (m_camera_offset_changed) {
3004                         client->updateCameraOffset(camera_offset);
3005                         client->getEnv().updateCameraOffset(camera_offset);
3006
3007                         if (clouds)
3008                                 clouds->updateCameraOffset(camera_offset);
3009                 }
3010         }
3011 }
3012
3013
3014 void Game::updateSound(f32 dtime)
3015 {
3016         // Update sound listener
3017         v3s16 camera_offset = camera->getOffset();
3018         sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS),
3019                               v3f(0, 0, 0), // velocity
3020                               camera->getDirection(),
3021                               camera->getCameraNode()->getUpVector());
3022
3023         bool mute_sound = g_settings->getBool("mute_sound");
3024         if (mute_sound) {
3025                 sound->setListenerGain(0.0f);
3026         } else {
3027                 // Check if volume is in the proper range, else fix it.
3028                 float old_volume = g_settings->getFloat("sound_volume");
3029                 float new_volume = rangelim(old_volume, 0.0f, 1.0f);
3030                 sound->setListenerGain(new_volume);
3031
3032                 if (old_volume != new_volume) {
3033                         g_settings->setFloat("sound_volume", new_volume);
3034                 }
3035         }
3036
3037         LocalPlayer *player = client->getEnv().getLocalPlayer();
3038
3039         // Tell the sound maker whether to make footstep sounds
3040         soundmaker->makes_footstep_sound = player->makes_footstep_sound;
3041
3042         //      Update sound maker
3043         if (player->makes_footstep_sound)
3044                 soundmaker->step(dtime);
3045
3046         ClientMap &map = client->getEnv().getClientMap();
3047         MapNode n = map.getNode(player->getFootstepNodePos());
3048         soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
3049 }
3050
3051
3052 void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
3053 {
3054         LocalPlayer *player = client->getEnv().getLocalPlayer();
3055
3056         const v3f camera_direction = camera->getDirection();
3057         const v3s16 camera_offset  = camera->getOffset();
3058
3059         /*
3060                 Calculate what block is the crosshair pointing to
3061         */
3062
3063         ItemStack selected_item, hand_item;
3064         const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
3065
3066         const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
3067         f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager));
3068
3069         core::line3d<f32> shootline;
3070
3071         switch (camera->getCameraMode()) {
3072         case CAMERA_MODE_FIRST:
3073                 // Shoot from camera position, with bobbing
3074                 shootline.start = camera->getPosition();
3075                 break;
3076         case CAMERA_MODE_THIRD:
3077                 // Shoot from player head, no bobbing
3078                 shootline.start = camera->getHeadPosition();
3079                 break;
3080         case CAMERA_MODE_THIRD_FRONT:
3081                 shootline.start = camera->getHeadPosition();
3082                 // prevent player pointing anything in front-view
3083                 d = 0;
3084                 break;
3085         }
3086         shootline.end = shootline.start + camera_direction * BS * d;
3087
3088 #ifdef HAVE_TOUCHSCREENGUI
3089
3090         if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
3091                 shootline = g_touchscreengui->getShootline();
3092                 // Scale shootline to the acual distance the player can reach
3093                 shootline.end = shootline.start
3094                         + shootline.getVector().normalize() * BS * d;
3095                 shootline.start += intToFloat(camera_offset, BS);
3096                 shootline.end += intToFloat(camera_offset, BS);
3097         }
3098
3099 #endif
3100
3101         PointedThing pointed = updatePointedThing(shootline,
3102                         selected_def.liquids_pointable,
3103                         !runData.btn_down_for_dig,
3104                         camera_offset);
3105
3106         if (pointed != runData.pointed_old) {
3107                 infostream << "Pointing at " << pointed.dump() << std::endl;
3108                 hud->updateSelectionMesh(camera_offset);
3109         }
3110
3111         // Allow digging again if button is not pressed
3112         if (runData.digging_blocked && !isKeyDown(KeyType::DIG))
3113                 runData.digging_blocked = false;
3114
3115         /*
3116                 Stop digging when
3117                 - releasing dig button
3118                 - pointing away from node
3119         */
3120         if (runData.digging) {
3121                 if (wasKeyReleased(KeyType::DIG)) {
3122                         infostream << "Dig button released (stopped digging)" << std::endl;
3123                         runData.digging = false;
3124                 } else if (pointed != runData.pointed_old) {
3125                         if (pointed.type == POINTEDTHING_NODE
3126                                         && runData.pointed_old.type == POINTEDTHING_NODE
3127                                         && pointed.node_undersurface
3128                                                         == runData.pointed_old.node_undersurface) {
3129                                 // Still pointing to the same node, but a different face.
3130                                 // Don't reset.
3131                         } else {
3132                                 infostream << "Pointing away from node (stopped digging)" << std::endl;
3133                                 runData.digging = false;
3134                                 hud->updateSelectionMesh(camera_offset);
3135                         }
3136                 }
3137
3138                 if (!runData.digging) {
3139                         client->interact(INTERACT_STOP_DIGGING, runData.pointed_old);
3140                         client->setCrack(-1, v3s16(0, 0, 0));
3141                         runData.dig_time = 0.0;
3142                 }
3143         } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) {
3144                 // Remove e.g. torches faster when clicking instead of holding dig button
3145                 runData.nodig_delay_timer = 0;
3146                 runData.dig_instantly = false;
3147         }
3148
3149         if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG))
3150                 runData.btn_down_for_dig = false;
3151
3152         runData.punching = false;
3153
3154         soundmaker->m_player_leftpunch_sound.name = "";
3155
3156         // Prepare for repeating, unless we're not supposed to
3157         if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place"))
3158                 runData.repeat_place_timer += dtime;
3159         else
3160                 runData.repeat_place_timer = 0;
3161
3162         if (selected_def.usable && isKeyDown(KeyType::DIG)) {
3163                 if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() ||
3164                                 !client->getScript()->on_item_use(selected_item, pointed)))
3165                         client->interact(INTERACT_USE, pointed);
3166         } else if (pointed.type == POINTEDTHING_NODE) {
3167                 handlePointingAtNode(pointed, selected_item, hand_item, dtime);
3168         } else if (pointed.type == POINTEDTHING_OBJECT) {
3169                 v3f player_position  = player->getPosition();
3170                 handlePointingAtObject(pointed, tool_item, player_position, show_debug);
3171         } else if (isKeyDown(KeyType::DIG)) {
3172                 // When button is held down in air, show continuous animation
3173                 runData.punching = true;
3174                 // Run callback even though item is not usable
3175                 if (wasKeyPressed(KeyType::DIG) && client->modsLoaded())
3176                         client->getScript()->on_item_use(selected_item, pointed);
3177         } else if (wasKeyPressed(KeyType::PLACE)) {
3178                 handlePointingAtNothing(selected_item);
3179         }
3180
3181         runData.pointed_old = pointed;
3182
3183         if (runData.punching || wasKeyPressed(KeyType::DIG))
3184                 camera->setDigging(0); // dig animation
3185
3186         input->clearWasKeyPressed();
3187         input->clearWasKeyReleased();
3188         // Ensure DIG & PLACE are marked as handled
3189         wasKeyDown(KeyType::DIG);
3190         wasKeyDown(KeyType::PLACE);
3191
3192         input->joystick.clearWasKeyPressed(KeyType::DIG);
3193         input->joystick.clearWasKeyPressed(KeyType::PLACE);
3194
3195         input->joystick.clearWasKeyReleased(KeyType::DIG);
3196         input->joystick.clearWasKeyReleased(KeyType::PLACE);
3197 }
3198
3199
3200 PointedThing Game::updatePointedThing(
3201         const core::line3d<f32> &shootline,
3202         bool liquids_pointable,
3203         bool look_for_object,
3204         const v3s16 &camera_offset)
3205 {
3206         std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
3207         selectionboxes->clear();
3208         hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0));
3209         static thread_local const bool show_entity_selectionbox = g_settings->getBool(
3210                 "show_entity_selectionbox");
3211
3212         ClientEnvironment &env = client->getEnv();
3213         ClientMap &map = env.getClientMap();
3214         const NodeDefManager *nodedef = map.getNodeDefManager();
3215
3216         runData.selected_object = NULL;
3217         hud->pointing_at_object = false;
3218
3219         RaycastState s(shootline, look_for_object, liquids_pointable);
3220         PointedThing result;
3221         env.continueRaycast(&s, &result);
3222         if (result.type == POINTEDTHING_OBJECT) {
3223                 hud->pointing_at_object = true;
3224
3225                 runData.selected_object = client->getEnv().getActiveObject(result.object_id);
3226                 aabb3f selection_box;
3227                 if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
3228                                 runData.selected_object->getSelectionBox(&selection_box)) {
3229                         v3f pos = runData.selected_object->getPosition();
3230                         selectionboxes->push_back(aabb3f(selection_box));
3231                         hud->setSelectionPos(pos, camera_offset);
3232                 }
3233         } else if (result.type == POINTEDTHING_NODE) {
3234                 // Update selection boxes
3235                 MapNode n = map.getNode(result.node_undersurface);
3236                 std::vector<aabb3f> boxes;
3237                 n.getSelectionBoxes(nodedef, &boxes,
3238                         n.getNeighbors(result.node_undersurface, &map));
3239
3240                 f32 d = 0.002 * BS;
3241                 for (std::vector<aabb3f>::const_iterator i = boxes.begin();
3242                         i != boxes.end(); ++i) {
3243                         aabb3f box = *i;
3244                         box.MinEdge -= v3f(d, d, d);
3245                         box.MaxEdge += v3f(d, d, d);
3246                         selectionboxes->push_back(box);
3247                 }
3248                 hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
3249                         camera_offset);
3250                 hud->setSelectedFaceNormal(v3f(
3251                         result.intersection_normal.X,
3252                         result.intersection_normal.Y,
3253                         result.intersection_normal.Z));
3254         }
3255
3256         // Update selection mesh light level and vertex colors
3257         if (!selectionboxes->empty()) {
3258                 v3f pf = hud->getSelectionPos();
3259                 v3s16 p = floatToInt(pf, BS);
3260
3261                 // Get selection mesh light level
3262                 MapNode n = map.getNode(p);
3263                 u16 node_light = getInteriorLight(n, -1, nodedef);
3264                 u16 light_level = node_light;
3265
3266                 for (const v3s16 &dir : g_6dirs) {
3267                         n = map.getNode(p + dir);
3268                         node_light = getInteriorLight(n, -1, nodedef);
3269                         if (node_light > light_level)
3270                                 light_level = node_light;
3271                 }
3272
3273                 u32 daynight_ratio = client->getEnv().getDayNightRatio();
3274                 video::SColor c;
3275                 final_color_blend(&c, light_level, daynight_ratio);
3276
3277                 // Modify final color a bit with time
3278                 u32 timer = porting::getTimeMs() % 5000;
3279                 float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5));
3280                 float sin_r = 0.08f * std::sin(timerf);
3281                 float sin_g = 0.08f * std::sin(timerf + irr::core::PI * 0.5f);
3282                 float sin_b = 0.08f * std::sin(timerf + irr::core::PI);
3283                 c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
3284                 c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
3285                 c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
3286
3287                 // Set mesh final color
3288                 hud->setSelectionMeshColor(c);
3289         }
3290         return result;
3291 }
3292
3293
3294 void Game::handlePointingAtNothing(const ItemStack &playerItem)
3295 {
3296         infostream << "Attempted to place item while pointing at nothing" << std::endl;
3297         PointedThing fauxPointed;
3298         fauxPointed.type = POINTEDTHING_NOTHING;
3299         client->interact(INTERACT_ACTIVATE, fauxPointed);
3300 }
3301
3302
3303 void Game::handlePointingAtNode(const PointedThing &pointed,
3304         const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
3305 {
3306         v3s16 nodepos = pointed.node_undersurface;
3307         v3s16 neighbourpos = pointed.node_abovesurface;
3308
3309         /*
3310                 Check information text of node
3311         */
3312
3313         ClientMap &map = client->getEnv().getClientMap();
3314
3315         if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG)
3316                         && !runData.digging_blocked
3317                         && client->checkPrivilege("interact")) {
3318                 handleDigging(pointed, nodepos, selected_item, hand_item, dtime);
3319         }
3320
3321         // This should be done after digging handling
3322         NodeMetadata *meta = map.getNodeMetadata(nodepos);
3323
3324         if (meta) {
3325                 m_game_ui->setInfoText(unescape_translate(utf8_to_wide(
3326                         meta->getString("infotext"))));
3327         } else {
3328                 MapNode n = map.getNode(nodepos);
3329
3330                 if (nodedef_manager->get(n).name == "unknown") {
3331                         m_game_ui->setInfoText(L"Unknown node");
3332                 }
3333         }
3334
3335         if ((wasKeyPressed(KeyType::PLACE) ||
3336                         runData.repeat_place_timer >= m_repeat_place_time) &&
3337                         client->checkPrivilege("interact")) {
3338                 runData.repeat_place_timer = 0;
3339                 infostream << "Place button pressed while looking at ground" << std::endl;
3340
3341                 // Placing animation (always shown for feedback)
3342                 camera->setDigging(1);
3343
3344                 soundmaker->m_player_rightpunch_sound = SimpleSoundSpec();
3345
3346                 // If the wielded item has node placement prediction,
3347                 // make that happen
3348                 // And also set the sound and send the interact
3349                 // But first check for meta formspec and rightclickable
3350                 auto &def = selected_item.getDefinition(itemdef_manager);
3351                 bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos,
3352                         pointed, meta);
3353
3354                 if (placed && client->modsLoaded())
3355                         client->getScript()->on_placenode(pointed, def);
3356         }
3357 }
3358
3359 bool Game::nodePlacement(const ItemDefinition &selected_def,
3360         const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos,
3361         const PointedThing &pointed, const NodeMetadata *meta)
3362 {
3363         const auto &prediction = selected_def.node_placement_prediction;
3364
3365         const NodeDefManager *nodedef = client->ndef();
3366         ClientMap &map = client->getEnv().getClientMap();
3367         MapNode node;
3368         bool is_valid_position;
3369
3370         node = map.getNode(nodepos, &is_valid_position);
3371         if (!is_valid_position) {
3372                 soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
3373                 return false;
3374         }
3375
3376         // formspec in meta
3377         if (meta && !meta->getString("formspec").empty() && !input->isRandom()
3378                         && !isKeyDown(KeyType::SNEAK)) {
3379                 // on_rightclick callbacks are called anyway
3380                 if (nodedef_manager->get(map.getNode(nodepos)).rightclickable)
3381                         client->interact(INTERACT_PLACE, pointed);
3382
3383                 infostream << "Launching custom inventory view" << std::endl;
3384
3385                 InventoryLocation inventoryloc;
3386                 inventoryloc.setNodeMeta(nodepos);
3387
3388                 NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
3389                         &client->getEnv().getClientMap(), nodepos);
3390                 TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
3391
3392                 auto *&formspec = m_game_ui->updateFormspec("");
3393                 GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
3394                         &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
3395
3396                 formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
3397                 return false;
3398         }
3399
3400         // on_rightclick callback
3401         if (prediction.empty() || (nodedef->get(node).rightclickable &&
3402                         !isKeyDown(KeyType::SNEAK))) {
3403                 // Report to server
3404                 client->interact(INTERACT_PLACE, pointed);
3405                 return false;
3406         }
3407
3408         verbosestream << "Node placement prediction for "
3409                 << selected_def.name << " is " << prediction << std::endl;
3410         v3s16 p = neighbourpos;
3411
3412         // Place inside node itself if buildable_to
3413         MapNode n_under = map.getNode(nodepos, &is_valid_position);
3414         if (is_valid_position) {
3415                 if (nodedef->get(n_under).buildable_to) {
3416                         p = nodepos;
3417                 } else {
3418                         node = map.getNode(p, &is_valid_position);
3419                         if (is_valid_position && !nodedef->get(node).buildable_to) {
3420                                 soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
3421                                 // Report to server
3422                                 client->interact(INTERACT_PLACE, pointed);
3423                                 return false;
3424                         }
3425                 }
3426         }
3427
3428         // Find id of predicted node
3429         content_t id;
3430         bool found = nodedef->getId(prediction, id);
3431
3432         if (!found) {
3433                 errorstream << "Node placement prediction failed for "
3434                         << selected_def.name << " (places " << prediction
3435                         << ") - Name not known" << std::endl;
3436                 // Handle this as if prediction was empty
3437                 // Report to server
3438                 client->interact(INTERACT_PLACE, pointed);
3439                 return false;
3440         }
3441
3442         const ContentFeatures &predicted_f = nodedef->get(id);
3443
3444         // Predict param2 for facedir and wallmounted nodes
3445         // Compare core.item_place_node() for what the server does
3446         u8 param2 = 0;
3447
3448         const u8 place_param2 = selected_def.place_param2;
3449
3450         if (place_param2) {
3451                 param2 = place_param2;
3452         } else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
3453                         predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
3454                 v3s16 dir = nodepos - neighbourpos;
3455
3456                 if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
3457                         param2 = dir.Y < 0 ? 1 : 0;
3458                 } else if (abs(dir.X) > abs(dir.Z)) {
3459                         param2 = dir.X < 0 ? 3 : 2;
3460                 } else {
3461                         param2 = dir.Z < 0 ? 5 : 4;
3462                 }
3463         } else if (predicted_f.param_type_2 == CPT2_FACEDIR ||
3464                         predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
3465                 v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
3466
3467                 if (abs(dir.X) > abs(dir.Z)) {
3468                         param2 = dir.X < 0 ? 3 : 1;
3469                 } else {
3470                         param2 = dir.Z < 0 ? 2 : 0;
3471                 }
3472         }
3473
3474         // Check attachment if node is in group attached_node
3475         if (itemgroup_get(predicted_f.groups, "attached_node") != 0) {
3476                 const static v3s16 wallmounted_dirs[8] = {
3477                         v3s16(0, 1, 0),
3478                         v3s16(0, -1, 0),
3479                         v3s16(1, 0, 0),
3480                         v3s16(-1, 0, 0),
3481                         v3s16(0, 0, 1),
3482                         v3s16(0, 0, -1),
3483                 };
3484                 v3s16 pp;
3485
3486                 if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
3487                                 predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
3488                         pp = p + wallmounted_dirs[param2];
3489                 else
3490                         pp = p + v3s16(0, -1, 0);
3491
3492                 if (!nodedef->get(map.getNode(pp)).walkable) {
3493                         soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
3494                         // Report to server
3495                         client->interact(INTERACT_PLACE, pointed);
3496                         return false;
3497                 }
3498         }
3499
3500         // Apply color
3501         if (!place_param2 && (predicted_f.param_type_2 == CPT2_COLOR
3502                         || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
3503                         || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
3504                 const auto &indexstr = selected_item.metadata.
3505                         getString("palette_index", 0);
3506                 if (!indexstr.empty()) {
3507                         s32 index = mystoi(indexstr);
3508                         if (predicted_f.param_type_2 == CPT2_COLOR) {
3509                                 param2 = index;
3510                         } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
3511                                 // param2 = pure palette index + other
3512                                 param2 = (index & 0xf8) | (param2 & 0x07);
3513                         } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
3514                                 // param2 = pure palette index + other
3515                                 param2 = (index & 0xe0) | (param2 & 0x1f);
3516                         }
3517                 }
3518         }
3519
3520         // Add node to client map
3521         MapNode n(id, 0, param2);
3522
3523         try {
3524                 LocalPlayer *player = client->getEnv().getLocalPlayer();
3525
3526                 // Dont place node when player would be inside new node
3527                 // NOTE: This is to be eventually implemented by a mod as client-side Lua
3528                 if (!nodedef->get(n).walkable ||
3529                                 g_settings->getBool("enable_build_where_you_stand") ||
3530                                 (client->checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
3531                                 (nodedef->get(n).walkable &&
3532                                         neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
3533                                         neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
3534                         // This triggers the required mesh update too
3535                         client->addNode(p, n);
3536                         // Report to server
3537                         client->interact(INTERACT_PLACE, pointed);
3538                         // A node is predicted, also play a sound
3539                         soundmaker->m_player_rightpunch_sound = selected_def.sound_place;
3540                         return true;
3541                 } else {
3542                         soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
3543                         return false;
3544                 }
3545         } catch (const InvalidPositionException &e) {
3546                 errorstream << "Node placement prediction failed for "
3547                         << selected_def.name << " (places "
3548                         << prediction << ") - Position not loaded" << std::endl;
3549                 soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
3550                 return false;
3551         }
3552 }
3553
3554 void Game::handlePointingAtObject(const PointedThing &pointed,
3555                 const ItemStack &tool_item, const v3f &player_position, bool show_debug)
3556 {
3557         std::wstring infotext = unescape_translate(
3558                 utf8_to_wide(runData.selected_object->infoText()));
3559
3560         if (show_debug) {
3561                 if (!infotext.empty()) {
3562                         infotext += L"\n";
3563                 }
3564                 infotext += utf8_to_wide(runData.selected_object->debugInfoText());
3565         }
3566
3567         m_game_ui->setInfoText(infotext);
3568
3569         if (isKeyDown(KeyType::DIG)) {
3570                 bool do_punch = false;
3571                 bool do_punch_damage = false;
3572
3573                 if (runData.object_hit_delay_timer <= 0.0) {
3574                         do_punch = true;
3575                         do_punch_damage = true;
3576                         runData.object_hit_delay_timer = object_hit_delay;
3577                 }
3578
3579                 if (wasKeyPressed(KeyType::DIG))
3580                         do_punch = true;
3581
3582                 if (do_punch) {
3583                         infostream << "Punched object" << std::endl;
3584                         runData.punching = true;
3585                 }
3586
3587                 if (do_punch_damage) {
3588                         // Report direct punch
3589                         v3f objpos = runData.selected_object->getPosition();
3590                         v3f dir = (objpos - player_position).normalize();
3591
3592                         bool disable_send = runData.selected_object->directReportPunch(
3593                                         dir, &tool_item, runData.time_from_last_punch);
3594                         runData.time_from_last_punch = 0;
3595
3596                         if (!disable_send)
3597                                 client->interact(INTERACT_START_DIGGING, pointed);
3598                 }
3599         } else if (wasKeyDown(KeyType::PLACE)) {
3600                 infostream << "Pressed place button while pointing at object" << std::endl;
3601                 client->interact(INTERACT_PLACE, pointed);  // place
3602         }
3603 }
3604
3605
3606 void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
3607                 const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
3608 {
3609         // See also: serverpackethandle.cpp, action == 2
3610         LocalPlayer *player = client->getEnv().getLocalPlayer();
3611         ClientMap &map = client->getEnv().getClientMap();
3612         MapNode n = client->getEnv().getClientMap().getNode(nodepos);
3613
3614         // NOTE: Similar piece of code exists on the server side for
3615         // cheat detection.
3616         // Get digging parameters
3617         DigParams params = getDigParams(nodedef_manager->get(n).groups,
3618                         &selected_item.getToolCapabilities(itemdef_manager),
3619                         selected_item.wear);
3620
3621         // If can't dig, try hand
3622         if (!params.diggable) {
3623                 params = getDigParams(nodedef_manager->get(n).groups,
3624                                 &hand_item.getToolCapabilities(itemdef_manager));
3625         }
3626
3627         if (!params.diggable) {
3628                 // I guess nobody will wait for this long
3629                 runData.dig_time_complete = 10000000.0;
3630         } else {
3631                 runData.dig_time_complete = params.time;
3632
3633                 if (m_cache_enable_particles) {
3634                         const ContentFeatures &features = client->getNodeDefManager()->get(n);
3635                         client->getParticleManager()->addNodeParticle(client,
3636                                         player, nodepos, n, features);
3637                 }
3638         }
3639
3640         if (!runData.digging) {
3641                 infostream << "Started digging" << std::endl;
3642                 runData.dig_instantly = runData.dig_time_complete == 0;
3643                 if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n))
3644                         return;
3645                 client->interact(INTERACT_START_DIGGING, pointed);
3646                 runData.digging = true;
3647                 runData.btn_down_for_dig = true;
3648         }
3649
3650         if (!runData.dig_instantly) {
3651                 runData.dig_index = (float)crack_animation_length
3652                                 * runData.dig_time
3653                                 / runData.dig_time_complete;
3654         } else {
3655                 // This is for e.g. torches
3656                 runData.dig_index = crack_animation_length;
3657         }
3658
3659         SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
3660
3661         if (sound_dig.exists() && params.diggable) {
3662                 if (sound_dig.name == "__group") {
3663                         if (!params.main_group.empty()) {
3664                                 soundmaker->m_player_leftpunch_sound.gain = 0.5;
3665                                 soundmaker->m_player_leftpunch_sound.name =
3666                                                 std::string("default_dig_") +
3667                                                 params.main_group;
3668                         }
3669                 } else {
3670                         soundmaker->m_player_leftpunch_sound = sound_dig;
3671                 }
3672         }
3673
3674         // Don't show cracks if not diggable
3675         if (runData.dig_time_complete >= 100000.0) {
3676         } else if (runData.dig_index < crack_animation_length) {
3677                 //TimeTaker timer("client.setTempMod");
3678                 //infostream<<"dig_index="<<dig_index<<std::endl;
3679                 client->setCrack(runData.dig_index, nodepos);
3680         } else {
3681                 infostream << "Digging completed" << std::endl;
3682                 client->setCrack(-1, v3s16(0, 0, 0));
3683
3684                 runData.dig_time = 0;
3685                 runData.digging = false;
3686                 // we successfully dug, now block it from repeating if we want to be safe
3687                 if (g_settings->getBool("safe_dig_and_place"))
3688                         runData.digging_blocked = true;
3689
3690                 runData.nodig_delay_timer =
3691                                 runData.dig_time_complete / (float)crack_animation_length;
3692
3693                 // We don't want a corresponding delay to very time consuming nodes
3694                 // and nodes without digging time (e.g. torches) get a fixed delay.
3695                 if (runData.nodig_delay_timer > 0.3)
3696                         runData.nodig_delay_timer = 0.3;
3697                 else if (runData.dig_instantly)
3698                         runData.nodig_delay_timer = 0.15;
3699
3700                 bool is_valid_position;
3701                 MapNode wasnode = map.getNode(nodepos, &is_valid_position);
3702                 if (is_valid_position) {
3703                         if (client->modsLoaded() &&
3704                                         client->getScript()->on_dignode(nodepos, wasnode)) {
3705                                 return;
3706                         }
3707
3708                         const ContentFeatures &f = client->ndef()->get(wasnode);
3709                         if (f.node_dig_prediction == "air") {
3710                                 client->removeNode(nodepos);
3711                         } else if (!f.node_dig_prediction.empty()) {
3712                                 content_t id;
3713                                 bool found = client->ndef()->getId(f.node_dig_prediction, id);
3714                                 if (found)
3715                                         client->addNode(nodepos, id, true);
3716                         }
3717                         // implicit else: no prediction
3718                 }
3719
3720                 client->interact(INTERACT_DIGGING_COMPLETED, pointed);
3721
3722                 if (m_cache_enable_particles) {
3723                         const ContentFeatures &features =
3724                                 client->getNodeDefManager()->get(wasnode);
3725                         client->getParticleManager()->addDiggingParticles(client,
3726                                 player, nodepos, wasnode, features);
3727                 }
3728
3729
3730                 // Send event to trigger sound
3731                 client->getEventManager()->put(new NodeDugEvent(nodepos, wasnode));
3732         }
3733
3734         if (runData.dig_time_complete < 100000.0) {
3735                 runData.dig_time += dtime;
3736         } else {
3737                 runData.dig_time = 0;
3738                 client->setCrack(-1, nodepos);
3739         }
3740
3741         camera->setDigging(0);  // Dig animation
3742 }
3743
3744 void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
3745                 const CameraOrientation &cam)
3746 {
3747         TimeTaker tt_update("Game::updateFrame()");
3748         LocalPlayer *player = client->getEnv().getLocalPlayer();
3749
3750         /*
3751                 Fog range
3752         */
3753
3754         if (draw_control->range_all) {
3755                 runData.fog_range = 100000 * BS;
3756         } else {
3757                 runData.fog_range = draw_control->wanted_range * BS;
3758         }
3759
3760         /*
3761                 Calculate general brightness
3762         */
3763         u32 daynight_ratio = client->getEnv().getDayNightRatio();
3764         float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
3765         float direct_brightness;
3766         bool sunlight_seen;
3767
3768         if (m_cache_enable_noclip && m_cache_enable_free_move) {
3769                 direct_brightness = time_brightness;
3770                 sunlight_seen = true;
3771         } else {
3772                 float old_brightness = sky->getBrightness();
3773                 direct_brightness = client->getEnv().getClientMap()
3774                                 .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS),
3775                                         daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
3776                                     / 255.0;
3777         }
3778
3779         float time_of_day_smooth = runData.time_of_day_smooth;
3780         float time_of_day = client->getEnv().getTimeOfDayF();
3781
3782         static const float maxsm = 0.05f;
3783         static const float todsm = 0.05f;
3784
3785         if (std::fabs(time_of_day - time_of_day_smooth) > maxsm &&
3786                         std::fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
3787                         std::fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
3788                 time_of_day_smooth = time_of_day;
3789
3790         if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
3791                 time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
3792                                 + (time_of_day + 1.0) * todsm;
3793         else
3794                 time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
3795                                 + time_of_day * todsm;
3796
3797         runData.time_of_day_smooth = time_of_day_smooth;
3798
3799         sky->update(time_of_day_smooth, time_brightness, direct_brightness,
3800                         sunlight_seen, camera->getCameraMode(), player->getYaw(),
3801                         player->getPitch());
3802
3803         /*
3804                 Update clouds
3805         */
3806         if (clouds) {
3807                 if (sky->getCloudsVisible()) {
3808                         clouds->setVisible(true);
3809                         clouds->step(dtime);
3810                         // camera->getPosition is not enough for 3rd person views
3811                         v3f camera_node_position = camera->getCameraNode()->getPosition();
3812                         v3s16 camera_offset      = camera->getOffset();
3813                         camera_node_position.X   = camera_node_position.X + camera_offset.X * BS;
3814                         camera_node_position.Y   = camera_node_position.Y + camera_offset.Y * BS;
3815                         camera_node_position.Z   = camera_node_position.Z + camera_offset.Z * BS;
3816                         clouds->update(camera_node_position,
3817                                         sky->getCloudColor());
3818                         if (clouds->isCameraInsideCloud() && m_cache_enable_fog) {
3819                                 // if inside clouds, and fog enabled, use that as sky
3820                                 // color(s)
3821                                 video::SColor clouds_dark = clouds->getColor()
3822                                                 .getInterpolated(video::SColor(255, 0, 0, 0), 0.9);
3823                                 sky->overrideColors(clouds_dark, clouds->getColor());
3824                                 sky->setInClouds(true);
3825                                 runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS);
3826                                 // do not draw clouds after all
3827                                 clouds->setVisible(false);
3828                         }
3829                 } else {
3830                         clouds->setVisible(false);
3831                 }
3832         }
3833
3834         /*
3835                 Update particles
3836         */
3837         client->getParticleManager()->step(dtime);
3838
3839         /*
3840                 Fog
3841         */
3842
3843         if (m_cache_enable_fog) {
3844                 driver->setFog(
3845                                 sky->getBgColor(),
3846                                 video::EFT_FOG_LINEAR,
3847                                 runData.fog_range * m_cache_fog_start,
3848                                 runData.fog_range * 1.0,
3849                                 0.01,
3850                                 false, // pixel fog
3851                                 true // range fog
3852                 );
3853         } else {
3854                 driver->setFog(
3855                                 sky->getBgColor(),
3856                                 video::EFT_FOG_LINEAR,
3857                                 100000 * BS,
3858                                 110000 * BS,
3859                                 0.01f,
3860                                 false, // pixel fog
3861                                 false // range fog
3862                 );
3863         }
3864
3865         /*
3866                 Get chat messages from client
3867         */
3868
3869         updateChat(dtime);
3870
3871         /*
3872                 Inventory
3873         */
3874
3875         if (player->getWieldIndex() != runData.new_playeritem)
3876                 client->setPlayerItem(runData.new_playeritem);
3877
3878         if (client->updateWieldedItem()) {
3879                 // Update wielded tool
3880                 ItemStack selected_item, hand_item;
3881                 ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
3882                 camera->wield(tool_item);
3883         }
3884
3885         /*
3886                 Update block draw list every 200ms or when camera direction has
3887                 changed much
3888         */
3889         runData.update_draw_list_timer += dtime;
3890
3891         float update_draw_list_delta = 0.2f;
3892
3893         v3f camera_direction = camera->getDirection();
3894         if (runData.update_draw_list_timer >= update_draw_list_delta
3895                         || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
3896                         || m_camera_offset_changed
3897                         || client->getEnv().getClientMap().needsUpdateDrawList()) {
3898                 runData.update_draw_list_timer = 0;
3899                 client->getEnv().getClientMap().updateDrawList();
3900                 runData.update_draw_list_last_cam_dir = camera_direction;
3901         }
3902
3903         if (RenderingEngine::get_shadow_renderer()) {
3904                 updateShadows();
3905         }
3906
3907         m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
3908
3909         /*
3910            make sure menu is on top
3911            1. Delete formspec menu reference if menu was removed
3912            2. Else, make sure formspec menu is on top
3913         */
3914         auto formspec = m_game_ui->getFormspecGUI();
3915         do { // breakable. only runs for one iteration
3916                 if (!formspec)
3917                         break;
3918
3919                 if (formspec->getReferenceCount() == 1) {
3920                         m_game_ui->deleteFormspec();
3921                         break;
3922                 }
3923
3924                 auto &loc = formspec->getFormspecLocation();
3925                 if (loc.type == InventoryLocation::NODEMETA) {
3926                         NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p);
3927                         if (!meta || meta->getString("formspec").empty()) {
3928                                 formspec->quitMenu();
3929                                 break;
3930                         }
3931                 }
3932
3933                 if (isMenuActive())
3934                         guiroot->bringToFront(formspec);
3935         } while (false);
3936
3937         /*
3938                 Drawing begins
3939         */
3940         const video::SColor &skycolor = sky->getSkyColor();
3941
3942         TimeTaker tt_draw("Draw scene");
3943         driver->beginScene(true, true, skycolor);
3944
3945         bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
3946                         (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
3947                         (camera->getCameraMode() == CAMERA_MODE_FIRST));
3948         bool draw_crosshair = (
3949                         (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
3950                         (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
3951 #ifdef HAVE_TOUCHSCREENGUI
3952         try {
3953                 draw_crosshair = !g_settings->getBool("touchtarget");
3954         } catch (SettingNotFoundException) {
3955         }
3956 #endif
3957         m_rendering_engine->draw_scene(skycolor, m_game_ui->m_flags.show_hud,
3958                         m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
3959
3960         /*
3961                 Profiler graph
3962         */
3963         v2u32 screensize = driver->getScreenSize();
3964
3965         if (m_game_ui->m_flags.show_profiler_graph)
3966                 graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
3967
3968         /*
3969                 Damage flash
3970         */
3971         if (runData.damage_flash > 0.0f) {
3972                 video::SColor color(runData.damage_flash, 180, 0, 0);
3973                 driver->draw2DRectangle(color,
3974                                         core::rect<s32>(0, 0, screensize.X, screensize.Y),
3975                                         NULL);
3976
3977                 runData.damage_flash -= 384.0f * dtime;
3978         }
3979
3980         /*
3981                 Damage camera tilt
3982         */
3983         if (player->hurt_tilt_timer > 0.0f) {
3984                 player->hurt_tilt_timer -= dtime * 6.0f;
3985
3986                 if (player->hurt_tilt_timer < 0.0f)
3987                         player->hurt_tilt_strength = 0.0f;
3988         }
3989
3990         /*
3991                 Update minimap pos and rotation
3992         */
3993         if (mapper && m_game_ui->m_flags.show_hud) {
3994                 mapper->setPos(floatToInt(player->getPosition(), BS));
3995                 mapper->setAngle(player->getYaw());
3996         }
3997
3998         /*
3999                 End scene
4000         */
4001         if (++m_reset_HW_buffer_counter > 500) {
4002                 /*
4003                   Periodically remove all mesh HW buffers.
4004
4005                   Work around for a quirk in Irrlicht where a HW buffer is only
4006                   released after 20000 iterations (triggered from endScene()).
4007
4008                   Without this, all loaded but unused meshes will retain their HW
4009                   buffers for at least 5 minutes, at which point looking up the HW buffers
4010                   becomes a bottleneck and the framerate drops (as much as 30%).
4011
4012                   Tests showed that numbers between 50 and 1000 are good, so picked 500.
4013                   There are no other public Irrlicht APIs that allow interacting with the
4014                   HW buffers without tracking the status of every individual mesh.
4015
4016                   The HW buffers for _visible_ meshes will be reinitialized in the next frame.
4017                 */
4018                 infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl;
4019                 driver->removeAllHardwareBuffers();
4020                 m_reset_HW_buffer_counter = 0;
4021         }
4022         driver->endScene();
4023
4024         stats->drawtime = tt_draw.stop(true);
4025         g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime);
4026         g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true));
4027 }
4028
4029 /* Log times and stuff for visualization */
4030 inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
4031 {
4032         Profiler::GraphValues values;
4033         g_profiler->graphGet(values);
4034         graph->put(values);
4035 }
4036
4037 /****************************************************************************
4038  * Shadows
4039  *****************************************************************************/
4040 void Game::updateShadows()
4041 {
4042         ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer();
4043         if (!shadow)
4044                 return;
4045
4046         float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
4047
4048         float timeoftheday = fmod(getWickedTimeOfDay(in_timeofday) + 0.75f, 0.5f) + 0.25f;
4049         const float offset_constant = 10000.0f;
4050
4051         v3f light(0.0f, 0.0f, -1.0f);
4052         light.rotateXZBy(90);
4053         light.rotateXYBy(timeoftheday * 360 - 90);
4054         light.rotateYZBy(sky->getSkyBodyOrbitTilt());
4055
4056         v3f sun_pos = light * offset_constant;
4057
4058         if (shadow->getDirectionalLightCount() == 0)
4059                 shadow->addDirectionalLight();
4060         shadow->getDirectionalLight().setDirection(sun_pos);
4061         shadow->setTimeOfDay(in_timeofday);
4062
4063         shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
4064 }
4065
4066 /****************************************************************************
4067  Misc
4068  ****************************************************************************/
4069
4070 /* On some computers framerate doesn't seem to be automatically limited
4071  */
4072 inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
4073 {
4074         // not using getRealTime is necessary for wine
4075         device->getTimer()->tick(); // Maker sure device time is up-to-date
4076         u32 time = device->getTimer()->getTime();
4077         u32 last_time = fps_timings->last_time;
4078
4079         if (time > last_time)  // Make sure time hasn't overflowed
4080                 fps_timings->busy_time = time - last_time;
4081         else
4082                 fps_timings->busy_time = 0;
4083
4084         u32 frametime_min = 1000 / (
4085                 device->isWindowFocused() && !g_menumgr.pausesGame()
4086                         ? g_settings->getFloat("fps_max")
4087                         : g_settings->getFloat("fps_max_unfocused"));
4088
4089         if (fps_timings->busy_time < frametime_min) {
4090                 fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
4091                 device->sleep(fps_timings->sleep_time);
4092         } else {
4093                 fps_timings->sleep_time = 0;
4094         }
4095
4096         /* Get the new value of the device timer. Note that device->sleep() may
4097          * not sleep for the entire requested time as sleep may be interrupted and
4098          * therefore it is arguably more accurate to get the new time from the
4099          * device rather than calculating it by adding sleep_time to time.
4100          */
4101
4102         device->getTimer()->tick(); // Update device timer
4103         time = device->getTimer()->getTime();
4104
4105         if (time > last_time)  // Make sure last_time hasn't overflowed
4106                 *dtime = (time - last_time) / 1000.0;
4107         else
4108                 *dtime = 0;
4109
4110         fps_timings->last_time = time;
4111 }
4112
4113 void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds)
4114 {
4115         const wchar_t *wmsg = wgettext(msg);
4116         m_rendering_engine->draw_load_screen(wmsg, guienv, texture_src, dtime, percent,
4117                 draw_clouds);
4118         delete[] wmsg;
4119 }
4120
4121 void Game::settingChangedCallback(const std::string &setting_name, void *data)
4122 {
4123         ((Game *)data)->readSettings();
4124 }
4125
4126 void Game::readSettings()
4127 {
4128         m_cache_doubletap_jump               = g_settings->getBool("doubletap_jump");
4129         m_cache_enable_clouds                = g_settings->getBool("enable_clouds");
4130         m_cache_enable_joysticks             = g_settings->getBool("enable_joysticks");
4131         m_cache_enable_particles             = g_settings->getBool("enable_particles");
4132         m_cache_enable_fog                   = g_settings->getBool("enable_fog");
4133         m_cache_mouse_sensitivity            = g_settings->getFloat("mouse_sensitivity");
4134         m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity");
4135         m_repeat_place_time                  = g_settings->getFloat("repeat_place_time");
4136
4137         m_cache_enable_noclip                = g_settings->getBool("noclip");
4138         m_cache_enable_free_move             = g_settings->getBool("free_move");
4139
4140         m_cache_fog_start                    = g_settings->getFloat("fog_start");
4141
4142         m_cache_cam_smoothing = 0;
4143         if (g_settings->getBool("cinematic"))
4144                 m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
4145         else
4146                 m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
4147
4148         m_cache_fog_start = rangelim(m_cache_fog_start, 0.0f, 0.99f);
4149         m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f);
4150         m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
4151
4152         m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus");
4153 }
4154
4155 /****************************************************************************/
4156 /****************************************************************************
4157  Shutdown / cleanup
4158  ****************************************************************************/
4159 /****************************************************************************/
4160
4161 void Game::showDeathFormspec()
4162 {
4163         static std::string formspec_str =
4164                 std::string("formspec_version[1]") +
4165                 SIZE_TAG
4166                 "bgcolor[#320000b4;true]"
4167                 "label[4.85,1.35;" + gettext("You died") + "]"
4168                 "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
4169                 ;
4170
4171         /* Create menu */
4172         /* Note: FormspecFormSource and LocalFormspecHandler  *
4173          * are deleted by guiFormSpecMenu                     */
4174         FormspecFormSource *fs_src = new FormspecFormSource(formspec_str);
4175         LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
4176
4177         auto *&formspec = m_game_ui->getFormspecGUI();
4178         GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
4179                 &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
4180         formspec->setFocus("btn_respawn");
4181 }
4182
4183 #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
4184 void Game::showPauseMenu()
4185 {
4186 #ifdef HAVE_TOUCHSCREENGUI
4187         static const std::string control_text = strgettext("Default Controls:\n"
4188                 "No menu visible:\n"
4189                 "- single tap: button activate\n"
4190                 "- double tap: place/use\n"
4191                 "- slide finger: look around\n"
4192                 "Menu/Inventory visible:\n"
4193                 "- double tap (outside):\n"
4194                 " -->close\n"
4195                 "- touch stack, touch slot:\n"
4196                 " --> move stack\n"
4197                 "- touch&drag, tap 2nd finger\n"
4198                 " --> place single item to slot\n"
4199                 );
4200 #else
4201         static const std::string control_text_template = strgettext("Controls:\n"
4202                 "- %s: move forwards\n"
4203                 "- %s: move backwards\n"
4204                 "- %s: move left\n"
4205                 "- %s: move right\n"
4206                 "- %s: jump/climb up\n"
4207                 "- %s: dig/punch\n"
4208                 "- %s: place/use\n"
4209                 "- %s: sneak/climb down\n"
4210                 "- %s: drop item\n"
4211                 "- %s: inventory\n"
4212                 "- Mouse: turn/look\n"
4213                 "- Mouse wheel: select item\n"
4214                 "- %s: chat\n"
4215         );
4216
4217         char control_text_buf[600];
4218
4219         porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(),
4220                 GET_KEY_NAME(keymap_forward),
4221                 GET_KEY_NAME(keymap_backward),
4222                 GET_KEY_NAME(keymap_left),
4223                 GET_KEY_NAME(keymap_right),
4224                 GET_KEY_NAME(keymap_jump),
4225                 GET_KEY_NAME(keymap_dig),
4226                 GET_KEY_NAME(keymap_place),
4227                 GET_KEY_NAME(keymap_sneak),
4228                 GET_KEY_NAME(keymap_drop),
4229                 GET_KEY_NAME(keymap_inventory),
4230                 GET_KEY_NAME(keymap_chat)
4231         );
4232
4233         std::string control_text = std::string(control_text_buf);
4234         str_formspec_escape(control_text);
4235 #endif
4236
4237         float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
4238         std::ostringstream os;
4239
4240         os << "formspec_version[1]" << SIZE_TAG
4241                 << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
4242                 << strgettext("Continue") << "]";
4243
4244         if (!simple_singleplayer_mode) {
4245                 os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
4246                         << strgettext("Change Password") << "]";
4247         } else {
4248                 os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]";
4249         }
4250
4251 #ifndef __ANDROID__
4252 #if USE_SOUND
4253         if (g_settings->getBool("enable_sound")) {
4254                 os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
4255                         << strgettext("Sound Volume") << "]";
4256         }
4257 #endif
4258         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
4259                 << strgettext("Change Keys")  << "]";
4260 #endif
4261         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
4262                 << strgettext("Exit to Menu") << "]";
4263         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
4264                 << strgettext("Exit to OS")   << "]"
4265                 << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
4266                 << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
4267                 << "\n"
4268                 <<  strgettext("Game info:") << "\n";
4269         const std::string &address = client->getAddressName();
4270         static const std::string mode = strgettext("- Mode: ");
4271         if (!simple_singleplayer_mode) {
4272                 Address serverAddress = client->getServerAddress();
4273                 if (!address.empty()) {
4274                         os << mode << strgettext("Remote server") << "\n"
4275                                         << strgettext("- Address: ") << address;
4276                 } else {
4277                         os << mode << strgettext("Hosting server");
4278                 }
4279                 os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n";
4280         } else {
4281                 os << mode << strgettext("Singleplayer") << "\n";
4282         }
4283         if (simple_singleplayer_mode || address.empty()) {
4284                 static const std::string on = strgettext("On");
4285                 static const std::string off = strgettext("Off");
4286                 const std::string &damage = g_settings->getBool("enable_damage") ? on : off;
4287                 const std::string &creative = g_settings->getBool("creative_mode") ? on : off;
4288                 const std::string &announced = g_settings->getBool("server_announce") ? on : off;
4289                 os << strgettext("- Damage: ") << damage << "\n"
4290                                 << strgettext("- Creative Mode: ") << creative << "\n";
4291                 if (!simple_singleplayer_mode) {
4292                         const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
4293                         //~ PvP = Player versus Player
4294                         os << strgettext("- PvP: ") << pvp << "\n"
4295                                         << strgettext("- Public: ") << announced << "\n";
4296                         std::string server_name = g_settings->get("server_name");
4297                         str_formspec_escape(server_name);
4298                         if (announced == on && !server_name.empty())
4299                                 os << strgettext("- Server Name: ") << server_name;
4300
4301                 }
4302         }
4303         os << ";]";
4304
4305         /* Create menu */
4306         /* Note: FormspecFormSource and LocalFormspecHandler  *
4307          * are deleted by guiFormSpecMenu                     */
4308         FormspecFormSource *fs_src = new FormspecFormSource(os.str());
4309         LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
4310
4311         auto *&formspec = m_game_ui->getFormspecGUI();
4312         GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
4313                         &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
4314         formspec->setFocus("btn_continue");
4315         formspec->doPause = true;
4316
4317         if (simple_singleplayer_mode)
4318                 pauseAnimation();
4319 }
4320
4321 /****************************************************************************/
4322 /****************************************************************************
4323  extern function for launching the game
4324  ****************************************************************************/
4325 /****************************************************************************/
4326
4327 void the_game(bool *kill,
4328                 InputHandler *input,
4329                 RenderingEngine *rendering_engine,
4330                 const GameStartData &start_data,
4331                 std::string &error_message,
4332                 ChatBackend &chat_backend,
4333                 bool *reconnect_requested) // Used for local game
4334 {
4335         Game game;
4336
4337         /* Make a copy of the server address because if a local singleplayer server
4338          * is created then this is updated and we don't want to change the value
4339          * passed to us by the calling function
4340          */
4341
4342         try {
4343
4344                 if (game.startup(kill, input, rendering_engine, start_data,
4345                                 error_message, reconnect_requested, &chat_backend)) {
4346                         game.run();
4347                 }
4348
4349         } catch (SerializationError &e) {
4350                 const std::string ver_err = fmtgettext("The server is probably running a different version of %s.", PROJECT_NAME_C);
4351                 error_message = strgettext("A serialization error occurred:") +"\n"
4352                                 + e.what() + "\n\n" + ver_err;
4353                 errorstream << error_message << std::endl;
4354         } catch (ServerError &e) {
4355                 error_message = e.what();
4356                 errorstream << "ServerError: " << error_message << std::endl;
4357         } catch (ModError &e) {
4358                 // DO NOT TRANSLATE the `ModError`, it's used by ui.lua
4359                 error_message = std::string("ModError: ") + e.what() +
4360                                 strgettext("\nCheck debug.txt for details.");
4361                 errorstream << error_message << std::endl;
4362         }
4363         game.shutdown();
4364 }