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