]> git.lizzy.rs Git - dragonfireclient.git/blob - src/game.cpp
Move buttons upwards to accommodate for new configure keys button in the
[dragonfireclient.git] / src / 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 #include "irrlichttypes_extrabloated.h"
22 #include <IGUICheckBox.h>
23 #include <IGUIEditBox.h>
24 #include <IGUIButton.h>
25 #include <IGUIStaticText.h>
26 #include <IGUIFont.h>
27 #include <IMaterialRendererServices.h>
28 #include "IMeshCache.h"
29 #include "client.h"
30 #include "server.h"
31 #include "guiPasswordChange.h"
32 #include "guiVolumeChange.h"
33 #include "guiKeyChangeMenu.h"
34 #include "guiFormSpecMenu.h"
35 #include "tool.h"
36 #include "guiChatConsole.h"
37 #include "config.h"
38 #include "version.h"
39 #include "clouds.h"
40 #include "particles.h"
41 #include "camera.h"
42 #include "mapblock.h"
43 #include "settings.h"
44 #include "profiler.h"
45 #include "mainmenumanager.h"
46 #include "gettext.h"
47 #include "log.h"
48 #include "filesys.h"
49 // Needed for determining pointing to nodes
50 #include "nodedef.h"
51 #include "nodemetadata.h"
52 #include "main.h" // For g_settings
53 #include "itemdef.h"
54 #include "tile.h" // For TextureSource
55 #include "shader.h" // For ShaderSource
56 #include "logoutputbuffer.h"
57 #include "subgame.h"
58 #include "quicktune_shortcutter.h"
59 #include "clientmap.h"
60 #include "hud.h"
61 #include "sky.h"
62 #include "sound.h"
63 #if USE_SOUND
64         #include "sound_openal.h"
65 #endif
66 #include "event_manager.h"
67 #include <iomanip>
68 #include <list>
69 #include "util/directiontables.h"
70 #include "util/pointedthing.h"
71 #include "drawscene.h"
72 #include "content_cao.h"
73
74 #ifdef HAVE_TOUCHSCREENGUI
75 #include "touchscreengui.h"
76 #endif
77
78 /*
79         Text input system
80 */
81
82 struct TextDestNodeMetadata : public TextDest
83 {
84         TextDestNodeMetadata(v3s16 p, Client *client)
85         {
86                 m_p = p;
87                 m_client = client;
88         }
89         // This is deprecated I guess? -celeron55
90         void gotText(std::wstring text)
91         {
92                 std::string ntext = wide_to_narrow(text);
93                 infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
94                                 <<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
95                 std::map<std::string, std::string> fields;
96                 fields["text"] = ntext;
97                 m_client->sendNodemetaFields(m_p, "", fields);
98         }
99         void gotText(std::map<std::string, std::string> fields)
100         {
101                 m_client->sendNodemetaFields(m_p, "", fields);
102         }
103
104         v3s16 m_p;
105         Client *m_client;
106 };
107
108 struct TextDestPlayerInventory : public TextDest
109 {
110         TextDestPlayerInventory(Client *client)
111         {
112                 m_client = client;
113                 m_formname = "";
114         }
115         TextDestPlayerInventory(Client *client, std::string formname)
116         {
117                 m_client = client;
118                 m_formname = formname;
119         }
120         void gotText(std::map<std::string, std::string> fields)
121         {
122                 m_client->sendInventoryFields(m_formname, fields);
123         }
124
125         Client *m_client;
126 };
127
128 struct LocalFormspecHandler : public TextDest
129 {
130         LocalFormspecHandler();
131         LocalFormspecHandler(std::string formname) :
132                 m_client(0)
133         {
134                 m_formname = formname;
135         }
136
137         LocalFormspecHandler(std::string formname, Client *client) :
138                 m_client(client)
139         {
140                 m_formname = formname;
141         }
142
143         void gotText(std::wstring message) {
144                 errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
145         }
146
147         void gotText(std::map<std::string, std::string> fields)
148         {
149                 if (m_formname == "MT_PAUSE_MENU") {
150                         if (fields.find("btn_sound") != fields.end()) {
151                                 g_gamecallback->changeVolume();
152                                 return;
153                         }
154
155                         if (fields.find("btn_key_config") != fields.end()) {
156                                 g_gamecallback->keyConfig();
157                                 return;
158                         }
159
160                         if (fields.find("btn_exit_menu") != fields.end()) {
161                                 g_gamecallback->disconnect();
162                                 return;
163                         }
164
165                         if (fields.find("btn_exit_os") != fields.end()) {
166                                 g_gamecallback->exitToOS();
167                                 return;
168                         }
169
170                         if (fields.find("btn_change_password") != fields.end()) {
171                                 g_gamecallback->changePassword();
172                                 return;
173                         }
174
175                         if (fields.find("quit") != fields.end()) {
176                                 return;
177                         }
178
179                         if (fields.find("btn_continue") != fields.end()) {
180                                 return;
181                         }
182                 }
183                 if (m_formname == "MT_CHAT_MENU") {
184                         assert(m_client != 0);
185                         if ((fields.find("btn_send") != fields.end()) ||
186                                         (fields.find("quit") != fields.end())) {
187                                 if (fields.find("f_text") != fields.end()) {
188                                         m_client->typeChatMessage(narrow_to_wide(fields["f_text"]));
189                                 }
190                                 return;
191                         }
192                 }
193
194                 if (m_formname == "MT_DEATH_SCREEN") {
195                         assert(m_client != 0);
196                         if ((fields.find("btn_respawn") != fields.end())) {
197                                 m_client->sendRespawn();
198                                 return;
199                         }
200
201                         if (fields.find("quit") != fields.end()) {
202                                 m_client->sendRespawn();
203                                 return;
204                         }
205                 }
206
207                 // don't show error message for unhandled cursor keys
208                 if ( (fields.find("key_up") != fields.end()) ||
209                         (fields.find("key_down") != fields.end()) ||
210                         (fields.find("key_left") != fields.end()) ||
211                         (fields.find("key_right") != fields.end())) {
212                         return;
213                 }
214
215                 errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl;
216                 int i = 0;
217                 for (std::map<std::string,std::string>::iterator iter = fields.begin();
218                                 iter != fields.end(); iter++) {
219                         errorstream << "\t"<< i << ": " << iter->first << "=" << iter->second << std::endl;
220                         i++;
221                 }
222         }
223
224         Client *m_client;
225 };
226
227 /* Form update callback */
228
229 class NodeMetadataFormSource: public IFormSource
230 {
231 public:
232         NodeMetadataFormSource(ClientMap *map, v3s16 p):
233                 m_map(map),
234                 m_p(p)
235         {
236         }
237         std::string getForm()
238         {
239                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
240                 if(!meta)
241                         return "";
242                 return meta->getString("formspec");
243         }
244         std::string resolveText(std::string str)
245         {
246                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
247                 if(!meta)
248                         return str;
249                 return meta->resolveString(str);
250         }
251
252         ClientMap *m_map;
253         v3s16 m_p;
254 };
255
256 class PlayerInventoryFormSource: public IFormSource
257 {
258 public:
259         PlayerInventoryFormSource(Client *client):
260                 m_client(client)
261         {
262         }
263         std::string getForm()
264         {
265                 LocalPlayer* player = m_client->getEnv().getLocalPlayer();
266                 return player->inventory_formspec;
267         }
268
269         Client *m_client;
270 };
271
272 /*
273         Check if a node is pointable
274 */
275 inline bool isPointableNode(const MapNode& n,
276                 Client *client, bool liquids_pointable)
277 {
278         const ContentFeatures &features = client->getNodeDefManager()->get(n);
279         return features.pointable ||
280                 (liquids_pointable && features.isLiquid());
281 }
282
283 /*
284         Find what the player is pointing at
285 */
286 PointedThing getPointedThing(Client *client, v3f player_position,
287                 v3f camera_direction, v3f camera_position, core::line3d<f32> shootline,
288                 f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset,
289                 std::vector<aabb3f> &hilightboxes, ClientActiveObject *&selected_object)
290 {
291         PointedThing result;
292
293         hilightboxes.clear();
294         selected_object = NULL;
295
296         INodeDefManager *nodedef = client->getNodeDefManager();
297         ClientMap &map = client->getEnv().getClientMap();
298
299         f32 mindistance = BS * 1001;
300
301         // First try to find a pointed at active object
302         if(look_for_object)
303         {
304                 selected_object = client->getSelectedActiveObject(d*BS,
305                                 camera_position, shootline);
306
307                 if(selected_object != NULL)
308                 {
309                         if(selected_object->doShowSelectionBox())
310                         {
311                                 aabb3f *selection_box = selected_object->getSelectionBox();
312                                 // Box should exist because object was
313                                 // returned in the first place
314                                 assert(selection_box);
315
316                                 v3f pos = selected_object->getPosition();
317                                 hilightboxes.push_back(aabb3f(
318                                                 selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
319                                                 selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
320                         }
321
322                         mindistance = (selected_object->getPosition() - camera_position).getLength();
323
324                         result.type = POINTEDTHING_OBJECT;
325                         result.object_id = selected_object->getId();
326                 }
327         }
328
329         // That didn't work, try to find a pointed at node
330
331
332         v3s16 pos_i = floatToInt(player_position, BS);
333
334         /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
335                         <<std::endl;*/
336
337         s16 a = d;
338         s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
339         s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
340         s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
341         s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
342         s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
343         s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
344
345         // Prevent signed number overflow
346         if(yend==32767)
347                 yend=32766;
348         if(zend==32767)
349                 zend=32766;
350         if(xend==32767)
351                 xend=32766;
352
353         for(s16 y = ystart; y <= yend; y++)
354         for(s16 z = zstart; z <= zend; z++)
355         for(s16 x = xstart; x <= xend; x++)
356         {
357                 MapNode n;
358                 try
359                 {
360                         n = map.getNode(v3s16(x,y,z));
361                 }
362                 catch(InvalidPositionException &e)
363                 {
364                         continue;
365                 }
366                 if(!isPointableNode(n, client, liquids_pointable))
367                         continue;
368
369                 std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
370
371                 v3s16 np(x,y,z);
372                 v3f npf = intToFloat(np, BS);
373
374                 for(std::vector<aabb3f>::const_iterator
375                                 i = boxes.begin();
376                                 i != boxes.end(); i++)
377                 {
378                         aabb3f box = *i;
379                         box.MinEdge += npf;
380                         box.MaxEdge += npf;
381
382                         for(u16 j=0; j<6; j++)
383                         {
384                                 v3s16 facedir = g_6dirs[j];
385                                 aabb3f facebox = box;
386
387                                 f32 d = 0.001*BS;
388                                 if(facedir.X > 0)
389                                         facebox.MinEdge.X = facebox.MaxEdge.X-d;
390                                 else if(facedir.X < 0)
391                                         facebox.MaxEdge.X = facebox.MinEdge.X+d;
392                                 else if(facedir.Y > 0)
393                                         facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
394                                 else if(facedir.Y < 0)
395                                         facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
396                                 else if(facedir.Z > 0)
397                                         facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
398                                 else if(facedir.Z < 0)
399                                         facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
400
401                                 v3f centerpoint = facebox.getCenter();
402                                 f32 distance = (centerpoint - camera_position).getLength();
403                                 if(distance >= mindistance)
404                                         continue;
405                                 if(!facebox.intersectsWithLine(shootline))
406                                         continue;
407
408                                 v3s16 np_above = np + facedir;
409
410                                 result.type = POINTEDTHING_NODE;
411                                 result.node_undersurface = np;
412                                 result.node_abovesurface = np_above;
413                                 mindistance = distance;
414
415                                 hilightboxes.clear();
416                                 if (!g_settings->getBool("enable_node_highlighting")) {
417                                         for(std::vector<aabb3f>::const_iterator
418                                                         i2 = boxes.begin();
419                                                         i2 != boxes.end(); i2++)
420                                         {
421                                                 aabb3f box = *i2;
422                                                 box.MinEdge += npf + v3f(-d,-d,-d) - intToFloat(camera_offset, BS);
423                                                 box.MaxEdge += npf + v3f(d,d,d) - intToFloat(camera_offset, BS);
424                                                 hilightboxes.push_back(box);
425                                         }
426                                 }
427                         }
428                 }
429         } // for coords
430
431         return result;
432 }
433
434 /* Profiler display */
435
436 void update_profiler_gui(gui::IGUIStaticText *guitext_profiler,
437                 gui::IGUIFont *font, u32 text_height, u32 show_profiler,
438                 u32 show_profiler_max)
439 {
440         if(show_profiler == 0)
441         {
442                 guitext_profiler->setVisible(false);
443         }
444         else
445         {
446
447                 std::ostringstream os(std::ios_base::binary);
448                 g_profiler->printPage(os, show_profiler, show_profiler_max);
449                 std::wstring text = narrow_to_wide(os.str());
450                 guitext_profiler->setText(text.c_str());
451                 guitext_profiler->setVisible(true);
452
453                 s32 w = font->getDimension(text.c_str()).Width;
454                 if(w < 400)
455                         w = 400;
456                 core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
457                                 8+(text_height+5)*2 +
458                                 font->getDimension(text.c_str()).Height);
459                 guitext_profiler->setRelativePosition(rect);
460                 guitext_profiler->setVisible(true);
461         }
462 }
463
464 class ProfilerGraph
465 {
466 private:
467         struct Piece{
468                 Profiler::GraphValues values;
469         };
470         struct Meta{
471                 float min;
472                 float max;
473                 video::SColor color;
474                 Meta(float initial=0, video::SColor color=
475                                 video::SColor(255,255,255,255)):
476                         min(initial),
477                         max(initial),
478                         color(color)
479                 {}
480         };
481         std::list<Piece> m_log;
482 public:
483         u32 m_log_max_size;
484
485         ProfilerGraph():
486                 m_log_max_size(200)
487         {}
488
489         void put(const Profiler::GraphValues &values)
490         {
491                 Piece piece;
492                 piece.values = values;
493                 m_log.push_back(piece);
494                 while(m_log.size() > m_log_max_size)
495                         m_log.erase(m_log.begin());
496         }
497
498         void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
499                         gui::IGUIFont* font) const
500         {
501                 std::map<std::string, Meta> m_meta;
502                 for(std::list<Piece>::const_iterator k = m_log.begin();
503                                 k != m_log.end(); k++)
504                 {
505                         const Piece &piece = *k;
506                         for(Profiler::GraphValues::const_iterator i = piece.values.begin();
507                                         i != piece.values.end(); i++){
508                                 const std::string &id = i->first;
509                                 const float &value = i->second;
510                                 std::map<std::string, Meta>::iterator j =
511                                                 m_meta.find(id);
512                                 if(j == m_meta.end()){
513                                         m_meta[id] = Meta(value);
514                                         continue;
515                                 }
516                                 if(value < j->second.min)
517                                         j->second.min = value;
518                                 if(value > j->second.max)
519                                         j->second.max = value;
520                         }
521                 }
522
523                 // Assign colors
524                 static const video::SColor usable_colors[] = {
525                         video::SColor(255,255,100,100),
526                         video::SColor(255,90,225,90),
527                         video::SColor(255,100,100,255),
528                         video::SColor(255,255,150,50),
529                         video::SColor(255,220,220,100)
530                 };
531                 static const u32 usable_colors_count =
532                                 sizeof(usable_colors) / sizeof(*usable_colors);
533                 u32 next_color_i = 0;
534                 for(std::map<std::string, Meta>::iterator i = m_meta.begin();
535                                 i != m_meta.end(); i++){
536                         Meta &meta = i->second;
537                         video::SColor color(255,200,200,200);
538                         if(next_color_i < usable_colors_count)
539                                 color = usable_colors[next_color_i++];
540                         meta.color = color;
541                 }
542
543                 s32 graphh = 50;
544                 s32 textx = x_left + m_log_max_size + 15;
545                 s32 textx2 = textx + 200 - 15;
546
547                 // Draw background
548                 /*{
549                         u32 num_graphs = m_meta.size();
550                         core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
551                                         textx2, y_bottom);
552                         video::SColor bgcolor(120,0,0,0);
553                         driver->draw2DRectangle(bgcolor, rect, NULL);
554                 }*/
555
556                 s32 meta_i = 0;
557                 for(std::map<std::string, Meta>::const_iterator i = m_meta.begin();
558                                 i != m_meta.end(); i++){
559                         const std::string &id = i->first;
560                         const Meta &meta = i->second;
561                         s32 x = x_left;
562                         s32 y = y_bottom - meta_i * 50;
563                         float show_min = meta.min;
564                         float show_max = meta.max;
565                         if(show_min >= -0.0001 && show_max >= -0.0001){
566                                 if(show_min <= show_max * 0.5)
567                                         show_min = 0;
568                         }
569                         s32 texth = 15;
570                         char buf[10];
571                         snprintf(buf, 10, "%.3g", show_max);
572                         font->draw(narrow_to_wide(buf).c_str(),
573                                         core::rect<s32>(textx, y - graphh,
574                                         textx2, y - graphh + texth),
575                                         meta.color);
576                         snprintf(buf, 10, "%.3g", show_min);
577                         font->draw(narrow_to_wide(buf).c_str(),
578                                         core::rect<s32>(textx, y - texth,
579                                         textx2, y),
580                                         meta.color);
581                         font->draw(narrow_to_wide(id).c_str(),
582                                         core::rect<s32>(textx, y - graphh/2 - texth/2,
583                                         textx2, y - graphh/2 + texth/2),
584                                         meta.color);
585                         s32 graph1y = y;
586                         s32 graph1h = graphh;
587                         bool relativegraph = (show_min != 0 && show_min != show_max);
588                         float lastscaledvalue = 0.0;
589                         bool lastscaledvalue_exists = false;
590                         for(std::list<Piece>::const_iterator j = m_log.begin();
591                                         j != m_log.end(); j++)
592                         {
593                                 const Piece &piece = *j;
594                                 float value = 0;
595                                 bool value_exists = false;
596                                 Profiler::GraphValues::const_iterator k =
597                                                 piece.values.find(id);
598                                 if(k != piece.values.end()){
599                                         value = k->second;
600                                         value_exists = true;
601                                 }
602                                 if(!value_exists){
603                                         x++;
604                                         lastscaledvalue_exists = false;
605                                         continue;
606                                 }
607                                 float scaledvalue = 1.0;
608                                 if(show_max != show_min)
609                                         scaledvalue = (value - show_min) / (show_max - show_min);
610                                 if(scaledvalue == 1.0 && value == 0){
611                                         x++;
612                                         lastscaledvalue_exists = false;
613                                         continue;
614                                 }
615                                 if(relativegraph){
616                                         if(lastscaledvalue_exists){
617                                                 s32 ivalue1 = lastscaledvalue * graph1h;
618                                                 s32 ivalue2 = scaledvalue * graph1h;
619                                                 driver->draw2DLine(v2s32(x-1, graph1y - ivalue1),
620                                                                 v2s32(x, graph1y - ivalue2), meta.color);
621                                         }
622                                         lastscaledvalue = scaledvalue;
623                                         lastscaledvalue_exists = true;
624                                 } else{
625                                         s32 ivalue = scaledvalue * graph1h;
626                                         driver->draw2DLine(v2s32(x, graph1y),
627                                                         v2s32(x, graph1y - ivalue), meta.color);
628                                 }
629                                 x++;
630                         }
631                         meta_i++;
632                 }
633         }
634 };
635
636 class NodeDugEvent: public MtEvent
637 {
638 public:
639         v3s16 p;
640         MapNode n;
641
642         NodeDugEvent(v3s16 p, MapNode n):
643                 p(p),
644                 n(n)
645         {}
646         const char* getType() const
647         {return "NodeDug";}
648 };
649
650 class SoundMaker
651 {
652         ISoundManager *m_sound;
653         INodeDefManager *m_ndef;
654 public:
655         float m_player_step_timer;
656
657         SimpleSoundSpec m_player_step_sound;
658         SimpleSoundSpec m_player_leftpunch_sound;
659         SimpleSoundSpec m_player_rightpunch_sound;
660
661         SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
662                 m_sound(sound),
663                 m_ndef(ndef),
664                 m_player_step_timer(0)
665         {
666         }
667
668         void playPlayerStep()
669         {
670                 if(m_player_step_timer <= 0 && m_player_step_sound.exists()){
671                         m_player_step_timer = 0.03;
672                         m_sound->playSound(m_player_step_sound, false);
673                 }
674         }
675
676         static void viewBobbingStep(MtEvent *e, void *data)
677         {
678                 SoundMaker *sm = (SoundMaker*)data;
679                 sm->playPlayerStep();
680         }
681
682         static void playerRegainGround(MtEvent *e, void *data)
683         {
684                 SoundMaker *sm = (SoundMaker*)data;
685                 sm->playPlayerStep();
686         }
687
688         static void playerJump(MtEvent *e, void *data)
689         {
690                 //SoundMaker *sm = (SoundMaker*)data;
691         }
692
693         static void cameraPunchLeft(MtEvent *e, void *data)
694         {
695                 SoundMaker *sm = (SoundMaker*)data;
696                 sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
697         }
698
699         static void cameraPunchRight(MtEvent *e, void *data)
700         {
701                 SoundMaker *sm = (SoundMaker*)data;
702                 sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
703         }
704
705         static void nodeDug(MtEvent *e, void *data)
706         {
707                 SoundMaker *sm = (SoundMaker*)data;
708                 NodeDugEvent *nde = (NodeDugEvent*)e;
709                 sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
710         }
711
712         static void playerDamage(MtEvent *e, void *data)
713         {
714                 SoundMaker *sm = (SoundMaker*)data;
715                 sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
716         }
717
718         static void playerFallingDamage(MtEvent *e, void *data)
719         {
720                 SoundMaker *sm = (SoundMaker*)data;
721                 sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
722         }
723
724         void registerReceiver(MtEventManager *mgr)
725         {
726                 mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
727                 mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
728                 mgr->reg("PlayerJump", SoundMaker::playerJump, this);
729                 mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
730                 mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
731                 mgr->reg("NodeDug", SoundMaker::nodeDug, this);
732                 mgr->reg("PlayerDamage", SoundMaker::playerDamage, this);
733                 mgr->reg("PlayerFallingDamage", SoundMaker::playerFallingDamage, this);
734         }
735
736         void step(float dtime)
737         {
738                 m_player_step_timer -= dtime;
739         }
740 };
741
742 // Locally stored sounds don't need to be preloaded because of this
743 class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
744 {
745         std::set<std::string> m_fetched;
746 public:
747
748         void fetchSounds(const std::string &name,
749                         std::set<std::string> &dst_paths,
750                         std::set<std::string> &dst_datas)
751         {
752                 if(m_fetched.count(name))
753                         return;
754                 m_fetched.insert(name);
755                 std::string base = porting::path_share + DIR_DELIM + "testsounds";
756                 dst_paths.insert(base + DIR_DELIM + name + ".ogg");
757                 dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
758                 dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
759                 dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
760                 dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
761                 dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
762                 dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
763                 dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
764                 dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
765                 dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
766                 dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
767         }
768 };
769
770 class GameGlobalShaderConstantSetter : public IShaderConstantSetter
771 {
772         Sky *m_sky;
773         bool *m_force_fog_off;
774         f32 *m_fog_range;
775         Client *m_client;
776
777 public:
778         GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
779                         f32 *fog_range, Client *client):
780                 m_sky(sky),
781                 m_force_fog_off(force_fog_off),
782                 m_fog_range(fog_range),
783                 m_client(client)
784         {}
785         ~GameGlobalShaderConstantSetter() {}
786
787         virtual void onSetConstants(video::IMaterialRendererServices *services,
788                         bool is_highlevel)
789         {
790                 if(!is_highlevel)
791                         return;
792
793                 // Background color
794                 video::SColor bgcolor = m_sky->getBgColor();
795                 video::SColorf bgcolorf(bgcolor);
796                 float bgcolorfa[4] = {
797                         bgcolorf.r,
798                         bgcolorf.g,
799                         bgcolorf.b,
800                         bgcolorf.a,
801                 };
802                 services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
803
804                 // Fog distance
805                 float fog_distance = 10000*BS;
806                 if(g_settings->getBool("enable_fog") && !*m_force_fog_off)
807                         fog_distance = *m_fog_range;
808                 services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
809
810                 // Day-night ratio
811                 u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
812                 float daynight_ratio_f = (float)daynight_ratio / 1000.0;
813                 services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
814
815                 u32 animation_timer = porting::getTimeMs() % 100000;
816                 float animation_timer_f = (float)animation_timer / 100000.0;
817                 services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
818                 services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
819
820                 LocalPlayer* player = m_client->getEnv().getLocalPlayer();
821                 v3f eye_position = player->getEyePosition();
822                 services->setPixelShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
823                 services->setVertexShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
824
825                 // Uniform sampler layers
826                 int layer0 = 0;
827                 int layer1 = 1;
828                 int layer2 = 2;
829                 // before 1.8 there isn't a "integer interface", only float
830 #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
831                 services->setPixelShaderConstant("baseTexture" , (irr::f32*)&layer0, 1);
832                 services->setPixelShaderConstant("normalTexture" , (irr::f32*)&layer1, 1);
833                 services->setPixelShaderConstant("useNormalmap" , (irr::f32*)&layer2, 1);
834 #else
835                 services->setPixelShaderConstant("baseTexture" , (irr::s32*)&layer0, 1);
836                 services->setPixelShaderConstant("normalTexture" , (irr::s32*)&layer1, 1);
837                 services->setPixelShaderConstant("useNormalmap" , (irr::s32*)&layer2, 1);
838 #endif
839         }
840 };
841
842 bool nodePlacementPrediction(Client &client,
843                 const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
844 {
845         std::string prediction = playeritem_def.node_placement_prediction;
846         INodeDefManager *nodedef = client.ndef();
847         ClientMap &map = client.getEnv().getClientMap();
848
849         if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
850         {
851                 verbosestream<<"Node placement prediction for "
852                                 <<playeritem_def.name<<" is "
853                                 <<prediction<<std::endl;
854                 v3s16 p = neighbourpos;
855                 // Place inside node itself if buildable_to
856                 try{
857                         MapNode n_under = map.getNode(nodepos);
858                         if(nodedef->get(n_under).buildable_to)
859                                 p = nodepos;
860                         else if (!nodedef->get(map.getNode(p)).buildable_to)
861                                 return false;
862                 }catch(InvalidPositionException &e){}
863                 // Find id of predicted node
864                 content_t id;
865                 bool found = nodedef->getId(prediction, id);
866                 if(!found){
867                         errorstream<<"Node placement prediction failed for "
868                                         <<playeritem_def.name<<" (places "
869                                         <<prediction
870                                         <<") - Name not known"<<std::endl;
871                         return false;
872                 }
873                 // Predict param2 for facedir and wallmounted nodes
874                 u8 param2 = 0;
875                 if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
876                         v3s16 dir = nodepos - neighbourpos;
877                         if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
878                                 param2 = dir.Y < 0 ? 1 : 0;
879                         } else if(abs(dir.X) > abs(dir.Z)){
880                                 param2 = dir.X < 0 ? 3 : 2;
881                         } else {
882                                 param2 = dir.Z < 0 ? 5 : 4;
883                         }
884                 }
885                 if(nodedef->get(id).param_type_2 == CPT2_FACEDIR){
886                         v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
887                         if(abs(dir.X) > abs(dir.Z)){
888                                 param2 = dir.X < 0 ? 3 : 1;
889                         } else {
890                                 param2 = dir.Z < 0 ? 2 : 0;
891                         }
892                 }
893                 assert(param2 >= 0 && param2 <= 5);
894                 //Check attachment if node is in group attached_node
895                 if(((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0){
896                         static v3s16 wallmounted_dirs[8] = {
897                                 v3s16(0,1,0),
898                                 v3s16(0,-1,0),
899                                 v3s16(1,0,0),
900                                 v3s16(-1,0,0),
901                                 v3s16(0,0,1),
902                                 v3s16(0,0,-1),
903                         };
904                         v3s16 pp;
905                         if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
906                                 pp = p + wallmounted_dirs[param2];
907                         else
908                                 pp = p + v3s16(0,-1,0);
909                         if(!nodedef->get(map.getNode(pp)).walkable)
910                                 return false;
911                 }
912                 // Add node to client map
913                 MapNode n(id, 0, param2);
914                 try{
915                         LocalPlayer* player = client.getEnv().getLocalPlayer();
916
917                         // Dont place node when player would be inside new node
918                         // NOTE: This is to be eventually implemented by a mod as client-side Lua
919                         if (!nodedef->get(n).walkable ||
920                                 (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
921                                 (nodedef->get(n).walkable &&
922                                 neighbourpos != player->getStandingNodePos() + v3s16(0,1,0) &&
923                                 neighbourpos != player->getStandingNodePos() + v3s16(0,2,0))) {
924
925                                         // This triggers the required mesh update too
926                                         client.addNode(p, n);
927                                         return true;
928                                 }
929                 }catch(InvalidPositionException &e){
930                         errorstream<<"Node placement prediction failed for "
931                                         <<playeritem_def.name<<" (places "
932                                         <<prediction
933                                         <<") - Position not loaded"<<std::endl;
934                 }
935         }
936         return false;
937 }
938
939 static inline void create_formspec_menu(GUIFormSpecMenu** cur_formspec,
940                 InventoryManager *invmgr, IGameDef *gamedef,
941                 IWritableTextureSource* tsrc, IrrlichtDevice * device,
942                 IFormSource* fs_src, TextDest* txt_dest, Client* client
943                 ) {
944
945         if (*cur_formspec == 0) {
946                 *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
947                                 invmgr, gamedef, tsrc, fs_src, txt_dest, cur_formspec, client);
948                 (*cur_formspec)->doPause = false;
949                 (*cur_formspec)->drop();
950         }
951         else {
952                 (*cur_formspec)->setFormSource(fs_src);
953                 (*cur_formspec)->setTextDest(txt_dest);
954         }
955 }
956
957 #ifdef __ANDROID__
958 #define SIZE_TAG "size[11,5.5]"
959 #else
960 #define SIZE_TAG "size[11,5.5,true]"
961 #endif
962
963 static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
964                 InventoryManager *invmgr, IGameDef *gamedef,
965                 IWritableTextureSource* tsrc, IrrlichtDevice * device,
966                 Client* client, std::string text)
967 {
968         std::string formspec =
969                 FORMSPEC_VERSION_STRING
970                 SIZE_TAG
971                 "field[3,2.35;6,0.5;f_text;;" + text + "]"
972                 "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
973                 ;
974
975         /* Create menu */
976         /* Note: FormspecFormSource and LocalFormspecHandler
977          * are deleted by guiFormSpecMenu                     */
978         FormspecFormSource* fs_src = new FormspecFormSource(formspec);
979         LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
980
981         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
982 }
983
984 static void show_deathscreen(GUIFormSpecMenu** cur_formspec,
985                 InventoryManager *invmgr, IGameDef *gamedef,
986                 IWritableTextureSource* tsrc, IrrlichtDevice * device, Client* client)
987 {
988         std::string formspec =
989                 std::string(FORMSPEC_VERSION_STRING) +
990                 SIZE_TAG
991                 "bgcolor[#320000b4;true]"
992                 "label[4.85,1.35;You died.]"
993                 "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
994                 ;
995
996         /* Create menu */
997         /* Note: FormspecFormSource and LocalFormspecHandler
998          * are deleted by guiFormSpecMenu                     */
999         FormspecFormSource* fs_src = new FormspecFormSource(formspec);
1000         LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
1001
1002         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,  fs_src, txt_dst, NULL);
1003 }
1004
1005 /******************************************************************************/
1006 static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
1007                 InventoryManager *invmgr, IGameDef *gamedef,
1008                 IWritableTextureSource* tsrc, IrrlichtDevice * device,
1009                 bool singleplayermode)
1010 {
1011 #ifdef __ANDROID__
1012         std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
1013                         "No menu visible:\n"
1014                         "- single tap: button activate\n"
1015                         "- double tap: place/use\n"
1016                         "- slide finger: look around\n"
1017                         "Menu/Inventory visible:\n"
1018                         "- double tap (outside):\n"
1019                         " -->close\n"
1020                         "- touch stack, touch slot:\n"
1021                         " --> move stack\n"
1022                         "- touch&drag, tap 2nd finger\n"
1023                         " --> place single item to slot\n"
1024                         ));
1025 #else
1026         std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
1027                         "- WASD: move\n"
1028                         "- Space: jump/climb\n"
1029                         "- Shift: sneak/go down\n"
1030                         "- Q: drop item\n"
1031                         "- I: inventory\n"
1032                         "- Mouse: turn/look\n"
1033                         "- Mouse left: dig/punch\n"
1034                         "- Mouse right: place/use\n"
1035                         "- Mouse wheel: select item\n"
1036                         "- T: chat\n"
1037                         ));
1038 #endif
1039
1040         float ypos = singleplayermode ? 0.5 : 0.1;
1041         std::ostringstream os;
1042
1043         os << FORMSPEC_VERSION_STRING  << SIZE_TAG
1044                         << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
1045                                         << wide_to_narrow(wstrgettext("Continue"))     << "]";
1046
1047         if (!singleplayermode) {
1048                 os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
1049                                         << wide_to_narrow(wstrgettext("Change Password")) << "]";
1050                 }
1051
1052         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
1053                                         << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
1054         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
1055                                         << wide_to_narrow(wstrgettext("Change Keys"))  << "]";
1056         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
1057                                         << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
1058         os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
1059                                         << wide_to_narrow(wstrgettext("Exit to OS"))   << "]"
1060                         << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
1061                         << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
1062                         << minetest_build_info << "\n"
1063                         << "path_user = " << wrap_rows(porting::path_user, 20)
1064                         << "\n;]";
1065
1066         /* Create menu */
1067         /* Note: FormspecFormSource and LocalFormspecHandler  *
1068          * are deleted by guiFormSpecMenu                     */
1069         FormspecFormSource* fs_src = new FormspecFormSource(os.str());
1070         LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
1071
1072         create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,  fs_src, txt_dst, NULL);
1073
1074         (*cur_formspec)->doPause = true;
1075 }
1076
1077 /******************************************************************************/
1078 static void updateChat(Client& client, f32 dtime, bool show_debug,
1079                 const v2u32& screensize, bool show_chat, u32 show_profiler,
1080                 ChatBackend& chat_backend, gui::IGUIStaticText* guitext_chat,
1081                 gui::IGUIFont* font)
1082 {
1083         // Add chat log output for errors to be shown in chat
1084         static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
1085
1086         // Get new messages from error log buffer
1087         while(!chat_log_error_buf.empty()) {
1088                 chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
1089         }
1090
1091         // Get new messages from client
1092         std::wstring message;
1093         while (client.getChatMessage(message)) {
1094                 chat_backend.addUnparsedMessage(message);
1095         }
1096
1097         // Remove old messages
1098         chat_backend.step(dtime);
1099
1100         // Display all messages in a static text element
1101         unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
1102         std::wstring recent_chat       = chat_backend.getRecentChat();
1103
1104         // TODO replace by fontengine fcts
1105         unsigned int line_height       = font->getDimension(L"Ay").Height + font->getKerningHeight();
1106
1107         guitext_chat->setText(recent_chat.c_str());
1108
1109         // Update gui element size and position
1110         s32 chat_y = 5 + line_height;
1111         if (show_debug)
1112                 chat_y += line_height;
1113
1114         // first pass to calculate height of text to be set
1115         s32 width = std::min(font->getDimension(recent_chat.c_str()).Width + 10,
1116                         porting::getWindowSize().X - 20);
1117         core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
1118         guitext_chat->setRelativePosition(rect);
1119
1120         //now use real height of text and adjust rect according to this size    
1121         rect = core::rect<s32>(10, chat_y, width,
1122                         chat_y + guitext_chat->getTextHeight());
1123
1124
1125         guitext_chat->setRelativePosition(rect);
1126         // Don't show chat if disabled or empty or profiler is enabled
1127         guitext_chat->setVisible(
1128                         show_chat && recent_chat_count != 0 && !show_profiler);
1129 }
1130
1131 /******************************************************************************/
1132 void the_game(bool &kill, bool random_input, InputHandler *input,
1133         IrrlichtDevice *device, gui::IGUIFont* font, std::string map_dir,
1134         std::string playername, std::string password,
1135         std::string address /* If "", local server is used */,
1136         u16 port, std::wstring &error_message, ChatBackend &chat_backend,
1137         const SubgameSpec &gamespec /* Used for local game */,
1138         bool simple_singleplayer_mode)
1139 {
1140         GUIFormSpecMenu* current_formspec = 0;
1141         video::IVideoDriver* driver = device->getVideoDriver();
1142         scene::ISceneManager* smgr = device->getSceneManager();
1143
1144         // Calculate text height using the font
1145         u32 text_height = font->getDimension(L"Random test string").Height;
1146
1147         /*
1148                 Draw "Loading" screen
1149         */
1150
1151         {
1152                 wchar_t* text = wgettext("Loading...");
1153                 draw_load_screen(text, device, guienv, font, 0, 0);
1154                 delete[] text;
1155         }
1156
1157         // Create texture source
1158         IWritableTextureSource *tsrc = createTextureSource(device);
1159
1160         // Create shader source
1161         IWritableShaderSource *shsrc = createShaderSource(device);
1162
1163         // These will be filled by data received from the server
1164         // Create item definition manager
1165         IWritableItemDefManager *itemdef = createItemDefManager();
1166         // Create node definition manager
1167         IWritableNodeDefManager *nodedef = createNodeDefManager();
1168
1169         // Sound fetcher (useful when testing)
1170         GameOnDemandSoundFetcher soundfetcher;
1171
1172         // Sound manager
1173         ISoundManager *sound = NULL;
1174         bool sound_is_dummy = false;
1175 #if USE_SOUND
1176         if(g_settings->getBool("enable_sound")){
1177                 infostream<<"Attempting to use OpenAL audio"<<std::endl;
1178                 sound = createOpenALSoundManager(&soundfetcher);
1179                 if(!sound)
1180                         infostream<<"Failed to initialize OpenAL audio"<<std::endl;
1181         } else {
1182                 infostream<<"Sound disabled."<<std::endl;
1183         }
1184 #endif
1185         if(!sound){
1186                 infostream<<"Using dummy audio."<<std::endl;
1187                 sound = &dummySoundManager;
1188                 sound_is_dummy = true;
1189         }
1190
1191         Server *server = NULL;
1192
1193         try{
1194         // Event manager
1195         EventManager eventmgr;
1196
1197         // Sound maker
1198         SoundMaker soundmaker(sound, nodedef);
1199         soundmaker.registerReceiver(&eventmgr);
1200
1201         // Create UI for modifying quicktune values
1202         QuicktuneShortcutter quicktune;
1203
1204         /*
1205                 Create server.
1206         */
1207
1208         if(address == ""){
1209                 wchar_t* text = wgettext("Creating server....");
1210                 draw_load_screen(text, device, guienv, font, 0, 25);
1211                 delete[] text;
1212                 infostream<<"Creating server"<<std::endl;
1213
1214                 std::string bind_str = g_settings->get("bind_address");
1215                 Address bind_addr(0,0,0,0, port);
1216
1217                 if (g_settings->getBool("ipv6_server")) {
1218                         bind_addr.setAddress((IPv6AddressBytes*) NULL);
1219                 }
1220                 try {
1221                         bind_addr.Resolve(bind_str.c_str());
1222                         address = bind_str;
1223                 } catch (ResolveError &e) {
1224                         infostream << "Resolving bind address \"" << bind_str
1225                                            << "\" failed: " << e.what()
1226                                            << " -- Listening on all addresses." << std::endl;
1227                 }
1228
1229                 if(bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
1230                         error_message = L"Unable to listen on " +
1231                                 narrow_to_wide(bind_addr.serializeString()) +
1232                                 L" because IPv6 is disabled";
1233                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1234                         // Break out of client scope
1235                         return;
1236                 }
1237
1238                 server = new Server(map_dir, gamespec,
1239                                 simple_singleplayer_mode,
1240                                 bind_addr.isIPv6());
1241
1242                 server->start(bind_addr);
1243         }
1244
1245         do{ // Client scope (breakable do-while(0))
1246
1247         /*
1248                 Create client
1249         */
1250
1251         {
1252                 wchar_t* text = wgettext("Creating client...");
1253                 draw_load_screen(text, device, guienv, font, 0, 50);
1254                 delete[] text;
1255         }
1256         infostream<<"Creating client"<<std::endl;
1257
1258         MapDrawControl draw_control;
1259
1260         {
1261                 wchar_t* text = wgettext("Resolving address...");
1262                 draw_load_screen(text, device, guienv, font, 0, 75);
1263                 delete[] text;
1264         }
1265         Address connect_address(0,0,0,0, port);
1266         try {
1267                 connect_address.Resolve(address.c_str());
1268                 if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
1269                         //connect_address.Resolve("localhost");
1270                         if (connect_address.isIPv6()) {
1271                                 IPv6AddressBytes addr_bytes;
1272                                 addr_bytes.bytes[15] = 1;
1273                                 connect_address.setAddress(&addr_bytes);
1274                         } else {
1275                                 connect_address.setAddress(127,0,0,1);
1276                         }
1277                 }
1278         }
1279         catch(ResolveError &e) {
1280                 error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
1281                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1282                 // Break out of client scope
1283                 break;
1284         }
1285         if(connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
1286                 error_message = L"Unable to connect to " +
1287                         narrow_to_wide(connect_address.serializeString()) +
1288                         L" because IPv6 is disabled";
1289                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1290                 // Break out of client scope
1291                 break;
1292         }
1293
1294         /*
1295                 Create client
1296         */
1297         Client client(device, playername.c_str(), password, draw_control,
1298                 tsrc, shsrc, itemdef, nodedef, sound, &eventmgr,
1299                 connect_address.isIPv6());
1300
1301         // Client acts as our GameDef
1302         IGameDef *gamedef = &client;
1303
1304         /*
1305                 Attempt to connect to the server
1306         */
1307
1308         infostream<<"Connecting to server at ";
1309         connect_address.print(&infostream);
1310         infostream<<std::endl;
1311         client.connect(connect_address);
1312
1313         /*
1314                 Wait for server to accept connection
1315         */
1316         bool could_connect = false;
1317         bool connect_aborted = false;
1318         try{
1319                 float time_counter = 0.0;
1320                 input->clear();
1321                 float fps_max = g_settings->getFloat("fps_max");
1322                 bool cloud_menu_background = g_settings->getBool("menu_clouds");
1323                 u32 lasttime = device->getTimer()->getTime();
1324                 while(device->run())
1325                 {
1326                         f32 dtime = 0.033; // in seconds
1327                         if (cloud_menu_background) {
1328                                 u32 time = device->getTimer()->getTime();
1329                                 if(time > lasttime)
1330                                         dtime = (time - lasttime) / 1000.0;
1331                                 else
1332                                         dtime = 0;
1333                                 lasttime = time;
1334                         }
1335                         // Update client and server
1336                         client.step(dtime);
1337                         if(server != NULL)
1338                                 server->step(dtime);
1339
1340                         // End condition
1341                         if(client.getState() == LC_Init) {
1342                                 could_connect = true;
1343                                 break;
1344                         }
1345                         // Break conditions
1346                         if(client.accessDenied()) {
1347                                 error_message = L"Access denied. Reason: "
1348                                                 +client.accessDeniedReason();
1349                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1350                                 break;
1351                         }
1352                         if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
1353                                 connect_aborted = true;
1354                                 infostream<<"Connect aborted [Escape]"<<std::endl;
1355                                 break;
1356                         }
1357
1358                         // Display status
1359                         {
1360                                 wchar_t* text = wgettext("Connecting to server...");
1361                                 draw_load_screen(text, device, guienv, font, dtime, 100);
1362                                 delete[] text;
1363                         }
1364
1365                         // On some computers framerate doesn't seem to be
1366                         // automatically limited
1367                         if (cloud_menu_background) {
1368                                 // Time of frame without fps limit
1369                                 float busytime;
1370                                 u32 busytime_u32;
1371                                 // not using getRealTime is necessary for wine
1372                                 u32 time = device->getTimer()->getTime();
1373                                 if(time > lasttime)
1374                                         busytime_u32 = time - lasttime;
1375                                 else
1376                                         busytime_u32 = 0;
1377                                 busytime = busytime_u32 / 1000.0;
1378
1379                                 // FPS limiter
1380                                 u32 frametime_min = 1000./fps_max;
1381
1382                                 if(busytime_u32 < frametime_min) {
1383                                         u32 sleeptime = frametime_min - busytime_u32;
1384                                         device->sleep(sleeptime);
1385                                 }
1386                         } else {
1387                                 sleep_ms(25);
1388                         }
1389                         time_counter += dtime;
1390                 }
1391         }
1392         catch(con::PeerNotFoundException &e)
1393         {}
1394
1395         /*
1396                 Handle failure to connect
1397         */
1398         if(!could_connect) {
1399                 if(error_message == L"" && !connect_aborted) {
1400                         error_message = L"Connection failed";
1401                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1402                 }
1403                 // Break out of client scope
1404                 break;
1405         }
1406
1407         /*
1408                 Wait until content has been received
1409         */
1410         bool got_content = false;
1411         bool content_aborted = false;
1412         {
1413                 float time_counter = 0.0;
1414                 input->clear();
1415                 float fps_max = g_settings->getFloat("fps_max");
1416                 bool cloud_menu_background = g_settings->getBool("menu_clouds");
1417                 u32 lasttime = device->getTimer()->getTime();
1418                 while (device->run()) {
1419                         f32 dtime = 0.033; // in seconds
1420                         if (cloud_menu_background) {
1421                                 u32 time = device->getTimer()->getTime();
1422                                 if(time > lasttime)
1423                                         dtime = (time - lasttime) / 1000.0;
1424                                 else
1425                                         dtime = 0;
1426                                 lasttime = time;
1427                         }
1428                         // Update client and server
1429                         client.step(dtime);
1430                         if (server != NULL)
1431                                 server->step(dtime);
1432
1433                         // End condition
1434                         if (client.mediaReceived() &&
1435                                         client.itemdefReceived() &&
1436                                         client.nodedefReceived()) {
1437                                 got_content = true;
1438                                 break;
1439                         }
1440                         // Break conditions
1441                         if (client.accessDenied()) {
1442                                 error_message = L"Access denied. Reason: "
1443                                                 +client.accessDeniedReason();
1444                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1445                                 break;
1446                         }
1447                         if (client.getState() < LC_Init) {
1448                                 error_message = L"Client disconnected";
1449                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1450                                 break;
1451                         }
1452                         if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
1453                                 content_aborted = true;
1454                                 infostream<<"Connect aborted [Escape]"<<std::endl;
1455                                 break;
1456                         }
1457
1458                         // Display status
1459                         int progress=0;
1460                         if (!client.itemdefReceived())
1461                         {
1462                                 wchar_t* text = wgettext("Item definitions...");
1463                                 progress = 0;
1464                                 draw_load_screen(text, device, guienv, font, dtime, progress);
1465                                 delete[] text;
1466                         }
1467                         else if (!client.nodedefReceived())
1468                         {
1469                                 wchar_t* text = wgettext("Node definitions...");
1470                                 progress = 25;
1471                                 draw_load_screen(text, device, guienv, font, dtime, progress);
1472                                 delete[] text;
1473                         }
1474                         else
1475                         {
1476
1477                                 std::stringstream message;
1478                                 message.precision(3);
1479                                 message << gettext("Media...");
1480
1481                                 if ( ( USE_CURL == 0) ||
1482                                                 (!g_settings->getBool("enable_remote_media_server"))) {
1483                                         float cur = client.getCurRate();
1484                                         std::string cur_unit = gettext(" KB/s");
1485
1486                                         if (cur > 900) {
1487                                                 cur /= 1024.0;
1488                                                 cur_unit = gettext(" MB/s");
1489                                         }
1490                                         message << " ( " << cur << cur_unit << " )";
1491                                 }
1492                                 progress = 50+client.mediaReceiveProgress()*50+0.5;
1493                                 draw_load_screen(narrow_to_wide(message.str().c_str()), device,
1494                                                 guienv, font, dtime, progress);
1495                         }
1496
1497                         // On some computers framerate doesn't seem to be
1498                         // automatically limited
1499                         if (cloud_menu_background) {
1500                                 // Time of frame without fps limit
1501                                 float busytime;
1502                                 u32 busytime_u32;
1503                                 // not using getRealTime is necessary for wine
1504                                 u32 time = device->getTimer()->getTime();
1505                                 if(time > lasttime)
1506                                         busytime_u32 = time - lasttime;
1507                                 else
1508                                         busytime_u32 = 0;
1509                                 busytime = busytime_u32 / 1000.0;
1510
1511                                 // FPS limiter
1512                                 u32 frametime_min = 1000./fps_max;
1513
1514                                 if(busytime_u32 < frametime_min) {
1515                                         u32 sleeptime = frametime_min - busytime_u32;
1516                                         device->sleep(sleeptime);
1517                                 }
1518                         } else {
1519                                 sleep_ms(25);
1520                         }
1521                         time_counter += dtime;
1522                 }
1523         }
1524
1525         if(!got_content){
1526                 if(error_message == L"" && !content_aborted){
1527                         error_message = L"Something failed";
1528                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1529                 }
1530                 // Break out of client scope
1531                 break;
1532         }
1533
1534         /*
1535                 After all content has been received:
1536                 Update cached textures, meshes and materials
1537         */
1538         client.afterContentReceived(device,font);
1539
1540         /*
1541                 Create the camera node
1542         */
1543         Camera camera(smgr, draw_control, gamedef);
1544         if (!camera.successfullyCreated(error_message))
1545                 return;
1546
1547         f32 camera_yaw = 0; // "right/left"
1548         f32 camera_pitch = 0; // "up/down"
1549
1550         /*
1551                 Clouds
1552         */
1553
1554         Clouds *clouds = NULL;
1555         if(g_settings->getBool("enable_clouds"))
1556         {
1557                 clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
1558         }
1559
1560         /*
1561                 Skybox thingy
1562         */
1563
1564         Sky *sky = NULL;
1565         sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
1566
1567         scene::ISceneNode* skybox = NULL;
1568
1569         /*
1570                 A copy of the local inventory
1571         */
1572         Inventory local_inventory(itemdef);
1573
1574         /*
1575                 Find out size of crack animation
1576         */
1577         int crack_animation_length = 5;
1578         {
1579                 video::ITexture *t = tsrc->getTexture("crack_anylength.png");
1580                 v2u32 size = t->getOriginalSize();
1581                 crack_animation_length = size.Y / size.X;
1582         }
1583
1584         /*
1585                 Add some gui stuff
1586         */
1587
1588         // First line of debug text
1589         gui::IGUIStaticText *guitext = guienv->addStaticText(
1590                         L"Minetest",
1591                         core::rect<s32>(0, 0, 0, 0),
1592                         false, false, guiroot);
1593         // Second line of debug text
1594         gui::IGUIStaticText *guitext2 = guienv->addStaticText(
1595                         L"",
1596                         core::rect<s32>(0, 0, 0, 0),
1597                         false, false, guiroot);
1598         // At the middle of the screen
1599         // Object infos are shown in this
1600         gui::IGUIStaticText *guitext_info = guienv->addStaticText(
1601                         L"",
1602                         core::rect<s32>(0,0,400,text_height*5+5) + v2s32(100,200),
1603                         false, true, guiroot);
1604
1605         // Status text (displays info when showing and hiding GUI stuff, etc.)
1606         gui::IGUIStaticText *guitext_status = guienv->addStaticText(
1607                         L"<Status>",
1608                         core::rect<s32>(0,0,0,0),
1609                         false, false, guiroot);
1610         guitext_status->setVisible(false);
1611
1612         std::wstring statustext;
1613         float statustext_time = 0;
1614
1615         // Chat text
1616         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
1617                         L"",
1618                         core::rect<s32>(0,0,0,0),
1619                         //false, false); // Disable word wrap as of now
1620                         false, true, guiroot);
1621         // Remove stale "recent" chat messages from previous connections
1622         chat_backend.clearRecentChat();
1623         // Chat backend and console
1624         GUIChatConsole *gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, &chat_backend, &client);
1625
1626         // Profiler text (size is updated when text is updated)
1627         gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
1628                         L"<Profiler>",
1629                         core::rect<s32>(0,0,0,0),
1630                         false, false, guiroot);
1631         guitext_profiler->setBackgroundColor(video::SColor(120,0,0,0));
1632         guitext_profiler->setVisible(false);
1633         guitext_profiler->setWordWrap(true);
1634
1635 #ifdef HAVE_TOUCHSCREENGUI
1636         if (g_touchscreengui)
1637                 g_touchscreengui->init(tsrc,porting::getDisplayDensity());
1638 #endif
1639
1640         /*
1641                 Some statistics are collected in these
1642         */
1643         u32 drawtime = 0;
1644         u32 beginscenetime = 0;
1645         u32 endscenetime = 0;
1646
1647         float recent_turn_speed = 0.0;
1648
1649         ProfilerGraph graph;
1650         // Initially clear the profiler
1651         Profiler::GraphValues dummyvalues;
1652         g_profiler->graphGet(dummyvalues);
1653
1654         float nodig_delay_timer = 0.0;
1655         float dig_time = 0.0;
1656         u16 dig_index = 0;
1657         PointedThing pointed_old;
1658         bool digging = false;
1659         bool ldown_for_dig = false;
1660
1661         float damage_flash = 0;
1662
1663         float jump_timer = 0;
1664         bool reset_jump_timer = false;
1665
1666         const float object_hit_delay = 0.2;
1667         float object_hit_delay_timer = 0.0;
1668         float time_from_last_punch = 10;
1669
1670         float update_draw_list_timer = 0.0;
1671         v3f update_draw_list_last_cam_dir;
1672
1673         bool invert_mouse = g_settings->getBool("invert_mouse");
1674
1675         bool update_wielded_item_trigger = true;
1676
1677         bool show_hud = true;
1678         bool show_chat = true;
1679         bool force_fog_off = false;
1680         f32 fog_range = 100*BS;
1681         bool disable_camera_update = false;
1682         bool show_debug = g_settings->getBool("show_debug");
1683         bool show_profiler_graph = false;
1684         u32 show_profiler = 0;
1685         u32 show_profiler_max = 3;  // Number of pages
1686
1687         float time_of_day = 0;
1688         float time_of_day_smooth = 0;
1689
1690         float repeat_rightclick_timer = 0;
1691
1692         /*
1693                 Shader constants
1694         */
1695         shsrc->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
1696                         sky, &force_fog_off, &fog_range, &client));
1697
1698         /*
1699                 Main loop
1700         */
1701
1702         bool first_loop_after_window_activation = true;
1703
1704         // TODO: Convert the static interval timers to these
1705         // Interval limiter for profiler
1706         IntervalLimiter m_profiler_interval;
1707
1708         // Time is in milliseconds
1709         // NOTE: getRealTime() causes strange problems in wine (imprecision?)
1710         // NOTE: So we have to use getTime() and call run()s between them
1711         u32 lasttime = device->getTimer()->getTime();
1712
1713         LocalPlayer* player = client.getEnv().getLocalPlayer();
1714         player->hurt_tilt_timer = 0;
1715         player->hurt_tilt_strength = 0;
1716
1717         /*
1718                 HUD object
1719         */
1720         Hud hud(driver, smgr, guienv, font, text_height,
1721                         gamedef, player, &local_inventory);
1722
1723         core::stringw str = L"Minetest [";
1724         str += driver->getName();
1725         str += "]";
1726         device->setWindowCaption(str.c_str());
1727
1728         // Info text
1729         std::wstring infotext;
1730
1731         for(;;)
1732         {
1733                 if(device->run() == false || kill == true ||
1734                                 g_gamecallback->shutdown_requested)
1735                         break;
1736
1737                 v2u32 screensize = driver->getScreenSize();
1738
1739                 // Time of frame without fps limit
1740                 float busytime;
1741                 u32 busytime_u32;
1742                 {
1743                         // not using getRealTime is necessary for wine
1744                         u32 time = device->getTimer()->getTime();
1745                         if(time > lasttime)
1746                                 busytime_u32 = time - lasttime;
1747                         else
1748                                 busytime_u32 = 0;
1749                         busytime = busytime_u32 / 1000.0;
1750                 }
1751
1752                 g_profiler->graphAdd("mainloop_other", busytime - (float)drawtime/1000.0f);
1753
1754                 // Necessary for device->getTimer()->getTime()
1755                 device->run();
1756
1757                 /*
1758                         FPS limiter
1759                 */
1760
1761                 {
1762                         float fps_max = g_menumgr.pausesGame() ?
1763                                         g_settings->getFloat("pause_fps_max") :
1764                                         g_settings->getFloat("fps_max");
1765                         u32 frametime_min = 1000./fps_max;
1766
1767                         if(busytime_u32 < frametime_min)
1768                         {
1769                                 u32 sleeptime = frametime_min - busytime_u32;
1770                                 device->sleep(sleeptime);
1771                                 g_profiler->graphAdd("mainloop_sleep", (float)sleeptime/1000.0f);
1772                         }
1773                 }
1774
1775                 // Necessary for device->getTimer()->getTime()
1776                 device->run();
1777
1778                 /*
1779                         Time difference calculation
1780                 */
1781                 f32 dtime; // in seconds
1782
1783                 u32 time = device->getTimer()->getTime();
1784                 if(time > lasttime)
1785                         dtime = (time - lasttime) / 1000.0;
1786                 else
1787                         dtime = 0;
1788                 lasttime = time;
1789
1790                 g_profiler->graphAdd("mainloop_dtime", dtime);
1791
1792                 /* Run timers */
1793
1794                 if(nodig_delay_timer >= 0)
1795                         nodig_delay_timer -= dtime;
1796                 if(object_hit_delay_timer >= 0)
1797                         object_hit_delay_timer -= dtime;
1798                 time_from_last_punch += dtime;
1799
1800                 g_profiler->add("Elapsed time", dtime);
1801                 g_profiler->avg("FPS", 1./dtime);
1802
1803                 /*
1804                         Time average and jitter calculation
1805                 */
1806
1807                 static f32 dtime_avg1 = 0.0;
1808                 dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
1809                 f32 dtime_jitter1 = dtime - dtime_avg1;
1810
1811                 static f32 dtime_jitter1_max_sample = 0.0;
1812                 static f32 dtime_jitter1_max_fraction = 0.0;
1813                 {
1814                         static f32 jitter1_max = 0.0;
1815                         static f32 counter = 0.0;
1816                         if(dtime_jitter1 > jitter1_max)
1817                                 jitter1_max = dtime_jitter1;
1818                         counter += dtime;
1819                         if(counter > 0.0)
1820                         {
1821                                 counter -= 3.0;
1822                                 dtime_jitter1_max_sample = jitter1_max;
1823                                 dtime_jitter1_max_fraction
1824                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
1825                                 jitter1_max = 0.0;
1826                         }
1827                 }
1828
1829                 /*
1830                         Busytime average and jitter calculation
1831                 */
1832
1833                 static f32 busytime_avg1 = 0.0;
1834                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
1835                 f32 busytime_jitter1 = busytime - busytime_avg1;
1836
1837                 static f32 busytime_jitter1_max_sample = 0.0;
1838                 static f32 busytime_jitter1_min_sample = 0.0;
1839                 {
1840                         static f32 jitter1_max = 0.0;
1841                         static f32 jitter1_min = 0.0;
1842                         static f32 counter = 0.0;
1843                         if(busytime_jitter1 > jitter1_max)
1844                                 jitter1_max = busytime_jitter1;
1845                         if(busytime_jitter1 < jitter1_min)
1846                                 jitter1_min = busytime_jitter1;
1847                         counter += dtime;
1848                         if(counter > 0.0){
1849                                 counter -= 3.0;
1850                                 busytime_jitter1_max_sample = jitter1_max;
1851                                 busytime_jitter1_min_sample = jitter1_min;
1852                                 jitter1_max = 0.0;
1853                                 jitter1_min = 0.0;
1854                         }
1855                 }
1856
1857                 /*
1858                         Handle miscellaneous stuff
1859                 */
1860
1861                 if(client.accessDenied())
1862                 {
1863                         error_message = L"Access denied. Reason: "
1864                                         +client.accessDeniedReason();
1865                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1866                         break;
1867                 }
1868
1869                 if(g_gamecallback->disconnect_requested)
1870                 {
1871                         g_gamecallback->disconnect_requested = false;
1872                         break;
1873                 }
1874
1875                 if(g_gamecallback->changepassword_requested)
1876                 {
1877                         (new GUIPasswordChange(guienv, guiroot, -1,
1878                                 &g_menumgr, &client))->drop();
1879                         g_gamecallback->changepassword_requested = false;
1880                 }
1881
1882                 if(g_gamecallback->changevolume_requested)
1883                 {
1884                         (new GUIVolumeChange(guienv, guiroot, -1,
1885                                 &g_menumgr, &client))->drop();
1886                         g_gamecallback->changevolume_requested = false;
1887                 }
1888
1889                 if(g_gamecallback->keyconfig_requested)
1890                 {
1891                         (new GUIKeyChangeMenu(guienv, guiroot, -1,
1892                                 &g_menumgr))->drop();
1893                         g_gamecallback->keyconfig_requested = false;
1894                 }
1895
1896
1897                 /* Process TextureSource's queue */
1898                 tsrc->processQueue();
1899
1900                 /* Process ItemDefManager's queue */
1901                 itemdef->processQueue(gamedef);
1902
1903                 /*
1904                         Process ShaderSource's queue
1905                 */
1906                 shsrc->processQueue();
1907
1908                 /*
1909                         Random calculations
1910                 */
1911                 hud.resizeHotbar();
1912
1913                 // Hilight boxes collected during the loop and displayed
1914                 std::vector<aabb3f> hilightboxes;
1915
1916                 /* reset infotext */
1917                 infotext = L"";
1918                 /*
1919                         Profiler
1920                 */
1921                 float profiler_print_interval =
1922                                 g_settings->getFloat("profiler_print_interval");
1923                 bool print_to_log = true;
1924                 if(profiler_print_interval == 0){
1925                         print_to_log = false;
1926                         profiler_print_interval = 5;
1927                 }
1928                 if(m_profiler_interval.step(dtime, profiler_print_interval))
1929                 {
1930                         if(print_to_log){
1931                                 infostream<<"Profiler:"<<std::endl;
1932                                 g_profiler->print(infostream);
1933                         }
1934
1935                         update_profiler_gui(guitext_profiler, font, text_height,
1936                                         show_profiler, show_profiler_max);
1937
1938                         g_profiler->clear();
1939                 }
1940
1941                 /*
1942                         Direct handling of user input
1943                 */
1944
1945                 // Reset input if window not active or some menu is active
1946                 if(device->isWindowActive() == false
1947                                 || noMenuActive() == false
1948                                 || guienv->hasFocus(gui_chat_console))
1949                 {
1950                         input->clear();
1951                 }
1952                 if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen())
1953                 {
1954                         gui_chat_console->closeConsoleAtOnce();
1955                 }
1956
1957                 // Input handler step() (used by the random input generator)
1958                 input->step(dtime);
1959 #ifdef HAVE_TOUCHSCREENGUI
1960                 if (g_touchscreengui) {
1961                         g_touchscreengui->step(dtime);
1962                 }
1963 #endif
1964 #ifdef __ANDROID__
1965                 if (current_formspec != 0)
1966                         current_formspec->getAndroidUIInput();
1967 #endif
1968
1969                 // Increase timer for doubleclick of "jump"
1970                 if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2)
1971                         jump_timer += dtime;
1972
1973                 /*
1974                         Launch menus and trigger stuff according to keys
1975                 */
1976                 if(input->wasKeyDown(getKeySetting("keymap_drop")))
1977                 {
1978                         // drop selected item
1979                         IDropAction *a = new IDropAction();
1980                         a->count = 0;
1981                         a->from_inv.setCurrentPlayer();
1982                         a->from_list = "main";
1983                         a->from_i = client.getPlayerItem();
1984                         client.inventoryAction(a);
1985                 }
1986                 else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
1987                 {
1988                         infostream<<"the_game: "
1989                                         <<"Launching inventory"<<std::endl;
1990
1991                         PlayerInventoryFormSource* fs_src = new PlayerInventoryFormSource(&client);
1992                         TextDest* txt_dst = new TextDestPlayerInventory(&client);
1993
1994                         create_formspec_menu(&current_formspec, &client, gamedef, tsrc, device, fs_src, txt_dst, &client);
1995
1996                         InventoryLocation inventoryloc;
1997                         inventoryloc.setCurrentPlayer();
1998                         current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
1999                 }
2000                 else if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey))
2001                 {
2002                         show_pause_menu(&current_formspec, &client, gamedef, tsrc, device,
2003                                         simple_singleplayer_mode);
2004                 }
2005                 else if(input->wasKeyDown(getKeySetting("keymap_chat")))
2006                 {
2007                         show_chat_menu(&current_formspec, &client, gamedef, tsrc, device,
2008                                         &client,"");
2009                 }
2010                 else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
2011                 {
2012                         show_chat_menu(&current_formspec, &client, gamedef, tsrc, device,
2013                                         &client,"/");
2014                 }
2015                 else if(input->wasKeyDown(getKeySetting("keymap_console")))
2016                 {
2017                         if (!gui_chat_console->isOpenInhibited())
2018                         {
2019                                 // Open up to over half of the screen
2020                                 gui_chat_console->openConsole(0.6);
2021                                 guienv->setFocus(gui_chat_console);
2022                         }
2023                 }
2024                 else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
2025                 {
2026                         if(g_settings->getBool("free_move"))
2027                         {
2028                                 g_settings->set("free_move","false");
2029                                 statustext = L"free_move disabled";
2030                                 statustext_time = 0;
2031                         }
2032                         else
2033                         {
2034                                 g_settings->set("free_move","true");
2035                                 statustext = L"free_move enabled";
2036                                 statustext_time = 0;
2037                                 if(!client.checkPrivilege("fly"))
2038                                         statustext += L" (note: no 'fly' privilege)";
2039                         }
2040                 }
2041                 else if(input->wasKeyDown(getKeySetting("keymap_jump")))
2042                 {
2043                         if(g_settings->getBool("doubletap_jump") && jump_timer < 0.2)
2044                         {
2045                                 if(g_settings->getBool("free_move"))
2046                                 {
2047                                         g_settings->set("free_move","false");
2048                                         statustext = L"free_move disabled";
2049                                         statustext_time = 0;
2050                                 }
2051                                 else
2052                                 {
2053                                         g_settings->set("free_move","true");
2054                                         statustext = L"free_move enabled";
2055                                         statustext_time = 0;
2056                                         if(!client.checkPrivilege("fly"))
2057                                                 statustext += L" (note: no 'fly' privilege)";
2058                                 }
2059                         }
2060                         reset_jump_timer = true;
2061                 }
2062                 else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
2063                 {
2064                         if(g_settings->getBool("fast_move"))
2065                         {
2066                                 g_settings->set("fast_move","false");
2067                                 statustext = L"fast_move disabled";
2068                                 statustext_time = 0;
2069                         }
2070                         else
2071                         {
2072                                 g_settings->set("fast_move","true");
2073                                 statustext = L"fast_move enabled";
2074                                 statustext_time = 0;
2075                                 if(!client.checkPrivilege("fast"))
2076                                         statustext += L" (note: no 'fast' privilege)";
2077                         }
2078                 }
2079                 else if(input->wasKeyDown(getKeySetting("keymap_noclip")))
2080                 {
2081                         if(g_settings->getBool("noclip"))
2082                         {
2083                                 g_settings->set("noclip","false");
2084                                 statustext = L"noclip disabled";
2085                                 statustext_time = 0;
2086                         }
2087                         else
2088                         {
2089                                 g_settings->set("noclip","true");
2090                                 statustext = L"noclip enabled";
2091                                 statustext_time = 0;
2092                                 if(!client.checkPrivilege("noclip"))
2093                                         statustext += L" (note: no 'noclip' privilege)";
2094                         }
2095                 }
2096                 else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
2097                 {
2098                         client.makeScreenshot(device);
2099                 }
2100                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_hud")))
2101                 {
2102                         show_hud = !show_hud;
2103                         if(show_hud) {
2104                                 statustext = L"HUD shown";
2105                                 client.setHighlighted(client.getHighlighted(), true);
2106                         } else {
2107                                 statustext = L"HUD hidden";
2108                                 client.setHighlighted(client.getHighlighted(), false);
2109                         }
2110                         statustext_time = 0;
2111                 }
2112                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_chat")))
2113                 {
2114                         show_chat = !show_chat;
2115                         if(show_chat)
2116                                 statustext = L"Chat shown";
2117                         else
2118                                 statustext = L"Chat hidden";
2119                         statustext_time = 0;
2120                 }
2121                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
2122                 {
2123                         force_fog_off = !force_fog_off;
2124                         if(force_fog_off)
2125                                 statustext = L"Fog disabled";
2126                         else
2127                                 statustext = L"Fog enabled";
2128                         statustext_time = 0;
2129                 }
2130                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
2131                 {
2132                         disable_camera_update = !disable_camera_update;
2133                         if(disable_camera_update)
2134                                 statustext = L"Camera update disabled";
2135                         else
2136                                 statustext = L"Camera update enabled";
2137                         statustext_time = 0;
2138                 }
2139                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_debug")))
2140                 {
2141                         // Initial / 3x toggle: Chat only
2142                         // 1x toggle: Debug text with chat
2143                         // 2x toggle: Debug text with profiler graph
2144                         if(!show_debug)
2145                         {
2146                                 show_debug = true;
2147                                 show_profiler_graph = false;
2148                                 statustext = L"Debug info shown";
2149                                 statustext_time = 0;
2150                         }
2151                         else if(show_profiler_graph)
2152                         {
2153                                 show_debug = false;
2154                                 show_profiler_graph = false;
2155                                 statustext = L"Debug info and profiler graph hidden";
2156                                 statustext_time = 0;
2157                         }
2158                         else
2159                         {
2160                                 show_profiler_graph = true;
2161                                 statustext = L"Profiler graph shown";
2162                                 statustext_time = 0;
2163                         }
2164                 }
2165                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
2166                 {
2167                         show_profiler = (show_profiler + 1) % (show_profiler_max + 1);
2168
2169                         // FIXME: This updates the profiler with incomplete values
2170                         update_profiler_gui(guitext_profiler, font, text_height,
2171                                         show_profiler, show_profiler_max);
2172
2173                         if(show_profiler != 0)
2174                         {
2175                                 std::wstringstream sstr;
2176                                 sstr<<"Profiler shown (page "<<show_profiler
2177                                         <<" of "<<show_profiler_max<<")";
2178                                 statustext = sstr.str();
2179                                 statustext_time = 0;
2180                         }
2181                         else
2182                         {
2183                                 statustext = L"Profiler hidden";
2184                                 statustext_time = 0;
2185                         }
2186                 }
2187                 else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min")))
2188                 {
2189                         s16 range = g_settings->getS16("viewing_range_nodes_min");
2190                         s16 range_new = range + 10;
2191                         g_settings->set("viewing_range_nodes_min", itos(range_new));
2192                         statustext = narrow_to_wide(
2193                                         "Minimum viewing range changed to "
2194                                         + itos(range_new));
2195                         statustext_time = 0;
2196                 }
2197                 else if(input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min")))
2198                 {
2199                         s16 range = g_settings->getS16("viewing_range_nodes_min");
2200                         s16 range_new = range - 10;
2201                         if(range_new < 0)
2202                                 range_new = range;
2203                         g_settings->set("viewing_range_nodes_min",
2204                                         itos(range_new));
2205                         statustext = narrow_to_wide(
2206                                         "Minimum viewing range changed to "
2207                                         + itos(range_new));
2208                         statustext_time = 0;
2209                 }
2210
2211                 // Reset jump_timer
2212                 if(!input->isKeyDown(getKeySetting("keymap_jump")) && reset_jump_timer)
2213                 {
2214                         reset_jump_timer = false;
2215                         jump_timer = 0.0;
2216                 }
2217
2218                 // Handle QuicktuneShortcutter
2219                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
2220                         quicktune.next();
2221                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_prev")))
2222                         quicktune.prev();
2223                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_inc")))
2224                         quicktune.inc();
2225                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_dec")))
2226                         quicktune.dec();
2227                 {
2228                         std::string msg = quicktune.getMessage();
2229                         if(msg != ""){
2230                                 statustext = narrow_to_wide(msg);
2231                                 statustext_time = 0;
2232                         }
2233                 }
2234
2235                 // Item selection with mouse wheel
2236                 u16 new_playeritem = client.getPlayerItem();
2237                 {
2238                         s32 wheel = input->getMouseWheel();
2239                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
2240                                         player->hud_hotbar_itemcount-1);
2241
2242                         if(wheel < 0)
2243                         {
2244                                 if(new_playeritem < max_item)
2245                                         new_playeritem++;
2246                                 else
2247                                         new_playeritem = 0;
2248                         }
2249                         else if(wheel > 0)
2250                         {
2251                                 if(new_playeritem > 0)
2252                                         new_playeritem--;
2253                                 else
2254                                         new_playeritem = max_item;
2255                         }
2256                 }
2257
2258                 // Item selection
2259                 for(u16 i=0; i<10; i++)
2260                 {
2261                         const KeyPress *kp = NumberKey + (i + 1) % 10;
2262                         if(input->wasKeyDown(*kp))
2263                         {
2264                                 if(i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount)
2265                                 {
2266                                         new_playeritem = i;
2267
2268                                         infostream<<"Selected item: "
2269                                                         <<new_playeritem<<std::endl;
2270                                 }
2271                         }
2272                 }
2273
2274                 // Viewing range selection
2275                 if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
2276                 {
2277                         draw_control.range_all = !draw_control.range_all;
2278                         if(draw_control.range_all)
2279                         {
2280                                 infostream<<"Enabled full viewing range"<<std::endl;
2281                                 statustext = L"Enabled full viewing range";
2282                                 statustext_time = 0;
2283                         }
2284                         else
2285                         {
2286                                 infostream<<"Disabled full viewing range"<<std::endl;
2287                                 statustext = L"Disabled full viewing range";
2288                                 statustext_time = 0;
2289                         }
2290                 }
2291
2292                 // Print debug stacks
2293                 if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
2294                 {
2295                         dstream<<"-----------------------------------------"
2296                                         <<std::endl;
2297                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
2298                         dstream<<"-----------------------------------------"
2299                                         <<std::endl;
2300                         debug_stacks_print();
2301                 }
2302
2303                 /*
2304                         Mouse and camera control
2305                         NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
2306                 */
2307
2308                 float turn_amount = 0;
2309                 if((device->isWindowActive() && noMenuActive()) || random_input)
2310                 {
2311 #ifndef __ANDROID__
2312                         if(!random_input)
2313                         {
2314                                 // Mac OSX gets upset if this is set every frame
2315                                 if(device->getCursorControl()->isVisible())
2316                                         device->getCursorControl()->setVisible(false);
2317                         }
2318 #endif
2319
2320                         if(first_loop_after_window_activation){
2321                                 //infostream<<"window active, first loop"<<std::endl;
2322                                 first_loop_after_window_activation = false;
2323                         } else {
2324 #ifdef HAVE_TOUCHSCREENGUI
2325                                 if (g_touchscreengui) {
2326                                         camera_yaw   = g_touchscreengui->getYaw();
2327                                         camera_pitch = g_touchscreengui->getPitch();
2328                                 } else {
2329 #endif
2330                                         s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2);
2331                                         s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2);
2332                                 if ((invert_mouse)
2333                                                 || (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
2334                                         dy = -dy;
2335                                 }
2336                                 //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
2337
2338                                 /*const float keyspeed = 500;
2339                                 if(input->isKeyDown(irr::KEY_UP))
2340                                         dy -= dtime * keyspeed;
2341                                 if(input->isKeyDown(irr::KEY_DOWN))
2342                                         dy += dtime * keyspeed;
2343                                 if(input->isKeyDown(irr::KEY_LEFT))
2344                                         dx -= dtime * keyspeed;
2345                                 if(input->isKeyDown(irr::KEY_RIGHT))
2346                                         dx += dtime * keyspeed;*/
2347
2348                                 float d = g_settings->getFloat("mouse_sensitivity");
2349                                 d = rangelim(d, 0.01, 100.0);
2350                                 camera_yaw -= dx*d;
2351                                 camera_pitch += dy*d;
2352                                 turn_amount = v2f(dx, dy).getLength() * d;
2353
2354 #ifdef HAVE_TOUCHSCREENGUI
2355                                 }
2356 #endif
2357                                 if(camera_pitch < -89.5) camera_pitch = -89.5;
2358                                 if(camera_pitch > 89.5) camera_pitch = 89.5;
2359                         }
2360                         input->setMousePos((driver->getScreenSize().Width/2),
2361                                         (driver->getScreenSize().Height/2));
2362                 }
2363                 else{
2364 #ifndef ANDROID
2365                         // Mac OSX gets upset if this is set every frame
2366                         if(device->getCursorControl()->isVisible() == false)
2367                                 device->getCursorControl()->setVisible(true);
2368 #endif
2369
2370                         //infostream<<"window inactive"<<std::endl;
2371                         first_loop_after_window_activation = true;
2372                 }
2373                 recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
2374                 //std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
2375
2376                 /*
2377                         Player speed control
2378                 */
2379                 {
2380                         /*bool a_up,
2381                         bool a_down,
2382                         bool a_left,
2383                         bool a_right,
2384                         bool a_jump,
2385                         bool a_superspeed,
2386                         bool a_sneak,
2387                         bool a_LMB,
2388                         bool a_RMB,
2389                         float a_pitch,
2390                         float a_yaw*/
2391                         PlayerControl control(
2392                                 input->isKeyDown(getKeySetting("keymap_forward")),
2393                                 input->isKeyDown(getKeySetting("keymap_backward")),
2394                                 input->isKeyDown(getKeySetting("keymap_left")),
2395                                 input->isKeyDown(getKeySetting("keymap_right")),
2396                                 input->isKeyDown(getKeySetting("keymap_jump")),
2397                                 input->isKeyDown(getKeySetting("keymap_special1")),
2398                                 input->isKeyDown(getKeySetting("keymap_sneak")),
2399                                 input->getLeftState(),
2400                                 input->getRightState(),
2401                                 camera_pitch,
2402                                 camera_yaw
2403                         );
2404                         client.setPlayerControl(control);
2405                         LocalPlayer* player = client.getEnv().getLocalPlayer();
2406                         player->keyPressed=
2407                         (((int)input->isKeyDown(getKeySetting("keymap_forward"))  & 0x1) << 0) |
2408                         (((int)input->isKeyDown(getKeySetting("keymap_backward")) & 0x1) << 1) |
2409                         (((int)input->isKeyDown(getKeySetting("keymap_left"))     & 0x1) << 2) |
2410                         (((int)input->isKeyDown(getKeySetting("keymap_right"))    & 0x1) << 3) |
2411                         (((int)input->isKeyDown(getKeySetting("keymap_jump"))     & 0x1) << 4) |
2412                         (((int)input->isKeyDown(getKeySetting("keymap_special1")) & 0x1) << 5) |
2413                         (((int)input->isKeyDown(getKeySetting("keymap_sneak"))    & 0x1) << 6) |
2414                         (((int)input->getLeftState()  & 0x1) << 7) |
2415                         (((int)input->getRightState() & 0x1) << 8);
2416                 }
2417
2418                 /*
2419                         Run server, client (and process environments)
2420                 */
2421                 bool can_be_and_is_paused =
2422                                 (simple_singleplayer_mode && g_menumgr.pausesGame());
2423                 if(can_be_and_is_paused)
2424                 {
2425                         // No time passes
2426                         dtime = 0;
2427                 }
2428                 else
2429                 {
2430                         if(server != NULL)
2431                         {
2432                                 //TimeTaker timer("server->step(dtime)");
2433                                 server->step(dtime);
2434                         }
2435                         {
2436                                 //TimeTaker timer("client.step(dtime)");
2437                                 client.step(dtime);
2438                         }
2439                 }
2440
2441                 {
2442                         // Read client events
2443                         for(;;) {
2444                                 ClientEvent event = client.getClientEvent();
2445                                 if(event.type == CE_NONE) {
2446                                         break;
2447                                 }
2448                                 else if(event.type == CE_PLAYER_DAMAGE &&
2449                                                 client.getHP() != 0) {
2450                                         //u16 damage = event.player_damage.amount;
2451                                         //infostream<<"Player damage: "<<damage<<std::endl;
2452
2453                                         damage_flash += 100.0;
2454                                         damage_flash += 8.0 * event.player_damage.amount;
2455
2456                                         player->hurt_tilt_timer = 1.5;
2457                                         player->hurt_tilt_strength = event.player_damage.amount/4;
2458                                         player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
2459
2460                                         MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
2461                                         gamedef->event()->put(e);
2462                                 }
2463                                 else if(event.type == CE_PLAYER_FORCE_MOVE) {
2464                                         camera_yaw = event.player_force_move.yaw;
2465                                         camera_pitch = event.player_force_move.pitch;
2466                                 }
2467                                 else if(event.type == CE_DEATHSCREEN) {
2468                                         show_deathscreen(&current_formspec, &client, gamedef, tsrc,
2469                                                         device, &client);
2470
2471                                         chat_backend.addMessage(L"", L"You died.");
2472
2473                                         /* Handle visualization */
2474                                         damage_flash = 0;
2475
2476                                         LocalPlayer* player = client.getEnv().getLocalPlayer();
2477                                         player->hurt_tilt_timer = 0;
2478                                         player->hurt_tilt_strength = 0;
2479
2480                                 }
2481                                 else if (event.type == CE_SHOW_FORMSPEC) {
2482                                         FormspecFormSource* fs_src =
2483                                                         new FormspecFormSource(*(event.show_formspec.formspec));
2484                                         TextDestPlayerInventory* txt_dst =
2485                                                         new TextDestPlayerInventory(&client,*(event.show_formspec.formname));
2486
2487                                         create_formspec_menu(&current_formspec, &client, gamedef,
2488                                                         tsrc, device, fs_src, txt_dst, &client);
2489
2490                                         delete(event.show_formspec.formspec);
2491                                         delete(event.show_formspec.formname);
2492                                 }
2493                                 else if(event.type == CE_SPAWN_PARTICLE) {
2494                                         LocalPlayer* player = client.getEnv().getLocalPlayer();
2495                                         video::ITexture *texture =
2496                                                 gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
2497
2498                                         new Particle(gamedef, smgr, player, client.getEnv(),
2499                                                 *event.spawn_particle.pos,
2500                                                 *event.spawn_particle.vel,
2501                                                 *event.spawn_particle.acc,
2502                                                  event.spawn_particle.expirationtime,
2503                                                  event.spawn_particle.size,
2504                                                  event.spawn_particle.collisiondetection,
2505                                                  event.spawn_particle.vertical,
2506                                                  texture,
2507                                                  v2f(0.0, 0.0),
2508                                                  v2f(1.0, 1.0));
2509                                 }
2510                                 else if(event.type == CE_ADD_PARTICLESPAWNER) {
2511                                         LocalPlayer* player = client.getEnv().getLocalPlayer();
2512                                         video::ITexture *texture =
2513                                                 gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
2514
2515                                         new ParticleSpawner(gamedef, smgr, player,
2516                                                  event.add_particlespawner.amount,
2517                                                  event.add_particlespawner.spawntime,
2518                                                 *event.add_particlespawner.minpos,
2519                                                 *event.add_particlespawner.maxpos,
2520                                                 *event.add_particlespawner.minvel,
2521                                                 *event.add_particlespawner.maxvel,
2522                                                 *event.add_particlespawner.minacc,
2523                                                 *event.add_particlespawner.maxacc,
2524                                                  event.add_particlespawner.minexptime,
2525                                                  event.add_particlespawner.maxexptime,
2526                                                  event.add_particlespawner.minsize,
2527                                                  event.add_particlespawner.maxsize,
2528                                                  event.add_particlespawner.collisiondetection,
2529                                                  event.add_particlespawner.vertical,
2530                                                  texture,
2531                                                  event.add_particlespawner.id);
2532                                 }
2533                                 else if(event.type == CE_DELETE_PARTICLESPAWNER) {
2534                                         delete_particlespawner (event.delete_particlespawner.id);
2535                                 }
2536                                 else if (event.type == CE_HUDADD) {
2537                                         u32 id = event.hudadd.id;
2538
2539                                         HudElement *e = player->getHud(id);
2540
2541                                         if (e != NULL) {
2542                                                 delete event.hudadd.pos;
2543                                                 delete event.hudadd.name;
2544                                                 delete event.hudadd.scale;
2545                                                 delete event.hudadd.text;
2546                                                 delete event.hudadd.align;
2547                                                 delete event.hudadd.offset;
2548                                                 delete event.hudadd.world_pos;
2549                                                 delete event.hudadd.size;
2550                                                 continue;
2551                                         }
2552
2553                                         e = new HudElement;
2554                                         e->type   = (HudElementType)event.hudadd.type;
2555                                         e->pos    = *event.hudadd.pos;
2556                                         e->name   = *event.hudadd.name;
2557                                         e->scale  = *event.hudadd.scale;
2558                                         e->text   = *event.hudadd.text;
2559                                         e->number = event.hudadd.number;
2560                                         e->item   = event.hudadd.item;
2561                                         e->dir    = event.hudadd.dir;
2562                                         e->align  = *event.hudadd.align;
2563                                         e->offset = *event.hudadd.offset;
2564                                         e->world_pos = *event.hudadd.world_pos;
2565                                         e->size = *event.hudadd.size;
2566
2567                                         u32 new_id = player->addHud(e);
2568                                         //if this isn't true our huds aren't consistent
2569                                         assert(new_id == id);
2570
2571                                         delete event.hudadd.pos;
2572                                         delete event.hudadd.name;
2573                                         delete event.hudadd.scale;
2574                                         delete event.hudadd.text;
2575                                         delete event.hudadd.align;
2576                                         delete event.hudadd.offset;
2577                                         delete event.hudadd.world_pos;
2578                                         delete event.hudadd.size;
2579                                 }
2580                                 else if (event.type == CE_HUDRM) {
2581                                         HudElement* e = player->removeHud(event.hudrm.id);
2582
2583                                         if (e != NULL)
2584                                                 delete (e);
2585                                 }
2586                                 else if (event.type == CE_HUDCHANGE) {
2587                                         u32 id = event.hudchange.id;
2588                                         HudElement* e = player->getHud(id);
2589                                         if (e == NULL)
2590                                         {
2591                                                 delete event.hudchange.v3fdata;
2592                                                 delete event.hudchange.v2fdata;
2593                                                 delete event.hudchange.sdata;
2594                                                 delete event.hudchange.v2s32data;
2595                                                 continue;
2596                                         }
2597
2598                                         switch (event.hudchange.stat) {
2599                                                 case HUD_STAT_POS:
2600                                                         e->pos = *event.hudchange.v2fdata;
2601                                                         break;
2602                                                 case HUD_STAT_NAME:
2603                                                         e->name = *event.hudchange.sdata;
2604                                                         break;
2605                                                 case HUD_STAT_SCALE:
2606                                                         e->scale = *event.hudchange.v2fdata;
2607                                                         break;
2608                                                 case HUD_STAT_TEXT:
2609                                                         e->text = *event.hudchange.sdata;
2610                                                         break;
2611                                                 case HUD_STAT_NUMBER:
2612                                                         e->number = event.hudchange.data;
2613                                                         break;
2614                                                 case HUD_STAT_ITEM:
2615                                                         e->item = event.hudchange.data;
2616                                                         break;
2617                                                 case HUD_STAT_DIR:
2618                                                         e->dir = event.hudchange.data;
2619                                                         break;
2620                                                 case HUD_STAT_ALIGN:
2621                                                         e->align = *event.hudchange.v2fdata;
2622                                                         break;
2623                                                 case HUD_STAT_OFFSET:
2624                                                         e->offset = *event.hudchange.v2fdata;
2625                                                         break;
2626                                                 case HUD_STAT_WORLD_POS:
2627                                                         e->world_pos = *event.hudchange.v3fdata;
2628                                                         break;
2629                                                 case HUD_STAT_SIZE:
2630                                                         e->size = *event.hudchange.v2s32data;
2631                                                         break;
2632                                         }
2633
2634                                         delete event.hudchange.v3fdata;
2635                                         delete event.hudchange.v2fdata;
2636                                         delete event.hudchange.sdata;
2637                                         delete event.hudchange.v2s32data;
2638                                 }
2639                                 else if (event.type == CE_SET_SKY) {
2640                                         sky->setVisible(false);
2641                                         if(skybox){
2642                                                 skybox->remove();
2643                                                 skybox = NULL;
2644                                         }
2645                                         // Handle according to type
2646                                         if(*event.set_sky.type == "regular") {
2647                                                 sky->setVisible(true);
2648                                         }
2649                                         else if(*event.set_sky.type == "skybox" &&
2650                                                         event.set_sky.params->size() == 6) {
2651                                                 sky->setFallbackBgColor(*event.set_sky.bgcolor);
2652                                                 skybox = smgr->addSkyBoxSceneNode(
2653                                                                 tsrc->getTexture((*event.set_sky.params)[0]),
2654                                                                 tsrc->getTexture((*event.set_sky.params)[1]),
2655                                                                 tsrc->getTexture((*event.set_sky.params)[2]),
2656                                                                 tsrc->getTexture((*event.set_sky.params)[3]),
2657                                                                 tsrc->getTexture((*event.set_sky.params)[4]),
2658                                                                 tsrc->getTexture((*event.set_sky.params)[5]));
2659                                         }
2660                                         // Handle everything else as plain color
2661                                         else {
2662                                                 if(*event.set_sky.type != "plain")
2663                                                         infostream<<"Unknown sky type: "
2664                                                                         <<(*event.set_sky.type)<<std::endl;
2665                                                 sky->setFallbackBgColor(*event.set_sky.bgcolor);
2666                                         }
2667
2668                                         delete event.set_sky.bgcolor;
2669                                         delete event.set_sky.type;
2670                                         delete event.set_sky.params;
2671                                 }
2672                                 else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
2673                                         bool enable = event.override_day_night_ratio.do_override;
2674                                         u32 value = event.override_day_night_ratio.ratio_f * 1000;
2675                                         client.getEnv().setDayNightRatioOverride(enable, value);
2676                                 }
2677                         }
2678                 }
2679
2680                 //TimeTaker //timer2("//timer2");
2681
2682                 /*
2683                         For interaction purposes, get info about the held item
2684                         - What item is it?
2685                         - Is it a usable item?
2686                         - Can it point to liquids?
2687                 */
2688                 ItemStack playeritem;
2689                 {
2690                         InventoryList *mlist = local_inventory.getList("main");
2691                         if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
2692                                 playeritem = mlist->getItem(client.getPlayerItem());
2693                 }
2694                 const ItemDefinition &playeritem_def =
2695                                 playeritem.getDefinition(itemdef);
2696                 ToolCapabilities playeritem_toolcap =
2697                                 playeritem.getToolCapabilities(itemdef);
2698
2699                 /*
2700                         Update camera
2701                 */
2702
2703                 v3s16 old_camera_offset = camera.getOffset();
2704
2705                 LocalPlayer* player = client.getEnv().getLocalPlayer();
2706                 float full_punch_interval = playeritem_toolcap.full_punch_interval;
2707                 float tool_reload_ratio = time_from_last_punch / full_punch_interval;
2708
2709                 if(input->wasKeyDown(getKeySetting("keymap_camera_mode"))) {
2710                         camera.toggleCameraMode();
2711                         GenericCAO* playercao = player->getCAO();
2712
2713                         assert( playercao != NULL );
2714                         if (camera.getCameraMode() > CAMERA_MODE_FIRST) {
2715                                 playercao->setVisible(true);
2716                         }
2717                         else {
2718                                 playercao->setVisible(false);
2719                         }
2720                 }
2721                 tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
2722                 camera.update(player, dtime, busytime, tool_reload_ratio,
2723                                 client.getEnv());
2724                 camera.step(dtime);
2725
2726                 v3f player_position = player->getPosition();
2727                 v3f camera_position = camera.getPosition();
2728                 v3f camera_direction = camera.getDirection();
2729                 f32 camera_fov = camera.getFovMax();
2730                 v3s16 camera_offset = camera.getOffset();
2731
2732                 bool camera_offset_changed = (camera_offset != old_camera_offset);
2733
2734                 if(!disable_camera_update){
2735                         client.getEnv().getClientMap().updateCamera(camera_position,
2736                                 camera_direction, camera_fov, camera_offset);
2737                         if (camera_offset_changed){
2738                                 client.updateCameraOffset(camera_offset);
2739                                 client.getEnv().updateCameraOffset(camera_offset);
2740                                 if (clouds)
2741                                         clouds->updateCameraOffset(camera_offset);
2742                         }
2743                 }
2744
2745                 // Update sound listener
2746                 sound->updateListener(camera.getCameraNode()->getPosition()+intToFloat(camera_offset, BS),
2747                                 v3f(0,0,0), // velocity
2748                                 camera.getDirection(),
2749                                 camera.getCameraNode()->getUpVector());
2750                 sound->setListenerGain(g_settings->getFloat("sound_volume"));
2751
2752                 /*
2753                         Update sound maker
2754                 */
2755                 {
2756                         soundmaker.step(dtime);
2757
2758                         ClientMap &map = client.getEnv().getClientMap();
2759                         MapNode n = map.getNodeNoEx(player->getStandingNodePos());
2760                         soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep;
2761                 }
2762
2763                 /*
2764                         Calculate what block is the crosshair pointing to
2765                 */
2766
2767                 //u32 t1 = device->getTimer()->getRealTime();
2768
2769                 f32 d = playeritem_def.range; // max. distance
2770                 f32 d_hand = itemdef->get("").range;
2771                 if(d < 0 && d_hand >= 0)
2772                         d = d_hand;
2773                 else if(d < 0)
2774                         d = 4.0;
2775                 core::line3d<f32> shootline(camera_position,
2776                                 camera_position + camera_direction * BS * (d+1));
2777
2778
2779                 // prevent player pointing anything in front-view
2780                 if (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)
2781                         shootline = core::line3d<f32>(0,0,0,0,0,0);
2782
2783 #ifdef HAVE_TOUCHSCREENGUI
2784                 if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
2785                         shootline = g_touchscreengui->getShootline();
2786                         shootline.start += intToFloat(camera_offset,BS);
2787                         shootline.end += intToFloat(camera_offset,BS);
2788                 }
2789 #endif
2790
2791                 ClientActiveObject *selected_object = NULL;
2792
2793                 PointedThing pointed = getPointedThing(
2794                                 // input
2795                                 &client, player_position, camera_direction,
2796                                 camera_position, shootline, d,
2797                                 playeritem_def.liquids_pointable, !ldown_for_dig,
2798                                 camera_offset,
2799                                 // output
2800                                 hilightboxes,
2801                                 selected_object);
2802
2803                 if(pointed != pointed_old)
2804                 {
2805                         infostream<<"Pointing at "<<pointed.dump()<<std::endl;
2806                         if (g_settings->getBool("enable_node_highlighting")) {
2807                                 if (pointed.type == POINTEDTHING_NODE) {
2808                                         client.setHighlighted(pointed.node_undersurface, show_hud);
2809                                 } else {
2810                                         client.setHighlighted(pointed.node_undersurface, false);
2811                                 }
2812                         }
2813                 }
2814
2815                 /*
2816                         Stop digging when
2817                         - releasing left mouse button
2818                         - pointing away from node
2819                 */
2820                 if(digging)
2821                 {
2822                         if(input->getLeftReleased())
2823                         {
2824                                 infostream<<"Left button released"
2825                                         <<" (stopped digging)"<<std::endl;
2826                                 digging = false;
2827                         }
2828                         else if(pointed != pointed_old)
2829                         {
2830                                 if (pointed.type == POINTEDTHING_NODE
2831                                         && pointed_old.type == POINTEDTHING_NODE
2832                                         && pointed.node_undersurface == pointed_old.node_undersurface)
2833                                 {
2834                                         // Still pointing to the same node,
2835                                         // but a different face. Don't reset.
2836                                 }
2837                                 else
2838                                 {
2839                                         infostream<<"Pointing away from node"
2840                                                 <<" (stopped digging)"<<std::endl;
2841                                         digging = false;
2842                                 }
2843                         }
2844                         if(!digging)
2845                         {
2846                                 client.interact(1, pointed_old);
2847                                 client.setCrack(-1, v3s16(0,0,0));
2848                                 dig_time = 0.0;
2849                         }
2850                 }
2851                 if(!digging && ldown_for_dig && !input->getLeftState())
2852                 {
2853                         ldown_for_dig = false;
2854                 }
2855
2856                 bool left_punch = false;
2857                 soundmaker.m_player_leftpunch_sound.name = "";
2858
2859                 if(input->getRightState())
2860                         repeat_rightclick_timer += dtime;
2861                 else
2862                         repeat_rightclick_timer = 0;
2863
2864                 if(playeritem_def.usable && input->getLeftState())
2865                 {
2866                         if(input->getLeftClicked())
2867                                 client.interact(4, pointed);
2868                 }
2869                 else if(pointed.type == POINTEDTHING_NODE)
2870                 {
2871                         v3s16 nodepos = pointed.node_undersurface;
2872                         v3s16 neighbourpos = pointed.node_abovesurface;
2873
2874                         /*
2875                                 Check information text of node
2876                         */
2877
2878                         ClientMap &map = client.getEnv().getClientMap();
2879                         NodeMetadata *meta = map.getNodeMetadata(nodepos);
2880                         if(meta){
2881                                 infotext = narrow_to_wide(meta->getString("infotext"));
2882                         } else {
2883                                 MapNode n = map.getNode(nodepos);
2884                                 if(nodedef->get(n).tiledef[0].name == "unknown_node.png"){
2885                                         infotext = L"Unknown node: ";
2886                                         infotext += narrow_to_wide(nodedef->get(n).name);
2887                                 }
2888                         }
2889
2890                         /*
2891                                 Handle digging
2892                         */
2893
2894                         if(nodig_delay_timer <= 0.0 && input->getLeftState()
2895                                         && client.checkPrivilege("interact"))
2896                         {
2897                                 if(!digging)
2898                                 {
2899                                         infostream<<"Started digging"<<std::endl;
2900                                         client.interact(0, pointed);
2901                                         digging = true;
2902                                         ldown_for_dig = true;
2903                                 }
2904                                 MapNode n = client.getEnv().getClientMap().getNode(nodepos);
2905
2906                                 // NOTE: Similar piece of code exists on the server side for
2907                                 // cheat detection.
2908                                 // Get digging parameters
2909                                 DigParams params = getDigParams(nodedef->get(n).groups,
2910                                                 &playeritem_toolcap);
2911                                 // If can't dig, try hand
2912                                 if(!params.diggable){
2913                                         const ItemDefinition &hand = itemdef->get("");
2914                                         const ToolCapabilities *tp = hand.tool_capabilities;
2915                                         if(tp)
2916                                                 params = getDigParams(nodedef->get(n).groups, tp);
2917                                 }
2918
2919                                 float dig_time_complete = 0.0;
2920
2921                                 if(params.diggable == false)
2922                                 {
2923                                         // I guess nobody will wait for this long
2924                                         dig_time_complete = 10000000.0;
2925                                 }
2926                                 else
2927                                 {
2928                                         dig_time_complete = params.time;
2929                                         if (g_settings->getBool("enable_particles"))
2930                                         {
2931                                                 const ContentFeatures &features =
2932                                                         client.getNodeDefManager()->get(n);
2933                                                 addPunchingParticles
2934                                                         (gamedef, smgr, player, client.getEnv(),
2935                                                          nodepos, features.tiles);
2936                                         }
2937                                 }
2938
2939                                 if(dig_time_complete >= 0.001)
2940                                 {
2941                                         dig_index = (u16)((float)crack_animation_length
2942                                                         * dig_time/dig_time_complete);
2943                                 }
2944                                 // This is for torches
2945                                 else
2946                                 {
2947                                         dig_index = crack_animation_length;
2948                                 }
2949
2950                                 SimpleSoundSpec sound_dig = nodedef->get(n).sound_dig;
2951                                 if(sound_dig.exists() && params.diggable){
2952                                         if(sound_dig.name == "__group"){
2953                                                 if(params.main_group != ""){
2954                                                         soundmaker.m_player_leftpunch_sound.gain = 0.5;
2955                                                         soundmaker.m_player_leftpunch_sound.name =
2956                                                                         std::string("default_dig_") +
2957                                                                                         params.main_group;
2958                                                 }
2959                                         } else{
2960                                                 soundmaker.m_player_leftpunch_sound = sound_dig;
2961                                         }
2962                                 }
2963
2964                                 // Don't show cracks if not diggable
2965                                 if(dig_time_complete >= 100000.0)
2966                                 {
2967                                 }
2968                                 else if(dig_index < crack_animation_length)
2969                                 {
2970                                         //TimeTaker timer("client.setTempMod");
2971                                         //infostream<<"dig_index="<<dig_index<<std::endl;
2972                                         client.setCrack(dig_index, nodepos);
2973                                 }
2974                                 else
2975                                 {
2976                                         infostream<<"Digging completed"<<std::endl;
2977                                         client.interact(2, pointed);
2978                                         client.setCrack(-1, v3s16(0,0,0));
2979                                         MapNode wasnode = map.getNode(nodepos);
2980                                         client.removeNode(nodepos);
2981
2982                                         if (g_settings->getBool("enable_particles"))
2983                                         {
2984                                                 const ContentFeatures &features =
2985                                                         client.getNodeDefManager()->get(wasnode);
2986                                                 addDiggingParticles
2987                                                         (gamedef, smgr, player, client.getEnv(),
2988                                                          nodepos, features.tiles);
2989                                         }
2990
2991                                         dig_time = 0;
2992                                         digging = false;
2993
2994                                         nodig_delay_timer = dig_time_complete
2995                                                         / (float)crack_animation_length;
2996
2997                                         // We don't want a corresponding delay to
2998                                         // very time consuming nodes
2999                                         if(nodig_delay_timer > 0.3)
3000                                                 nodig_delay_timer = 0.3;
3001                                         // We want a slight delay to very little
3002                                         // time consuming nodes
3003                                         float mindelay = 0.15;
3004                                         if(nodig_delay_timer < mindelay)
3005                                                 nodig_delay_timer = mindelay;
3006
3007                                         // Send event to trigger sound
3008                                         MtEvent *e = new NodeDugEvent(nodepos, wasnode);
3009                                         gamedef->event()->put(e);
3010                                 }
3011
3012                                 if(dig_time_complete < 100000.0)
3013                                         dig_time += dtime;
3014                                 else {
3015                                         dig_time = 0;
3016                                         client.setCrack(-1, nodepos);
3017                                 }
3018
3019                                 camera.setDigging(0);  // left click animation
3020                         }
3021
3022                         if((input->getRightClicked() ||
3023                                         repeat_rightclick_timer >=
3024                                                 g_settings->getFloat("repeat_rightclick_time")) &&
3025                                         client.checkPrivilege("interact"))
3026                         {
3027                                 repeat_rightclick_timer = 0;
3028                                 infostream<<"Ground right-clicked"<<std::endl;
3029
3030                                 if(meta && meta->getString("formspec") != "" && !random_input
3031                                                 && !input->isKeyDown(getKeySetting("keymap_sneak")))
3032                                 {
3033                                         infostream<<"Launching custom inventory view"<<std::endl;
3034
3035                                         InventoryLocation inventoryloc;
3036                                         inventoryloc.setNodeMeta(nodepos);
3037
3038                                         NodeMetadataFormSource* fs_src = new NodeMetadataFormSource(
3039                                                         &client.getEnv().getClientMap(), nodepos);
3040                                         TextDest* txt_dst = new TextDestNodeMetadata(nodepos, &client);
3041
3042                                         create_formspec_menu(&current_formspec, &client, gamedef,
3043                                                         tsrc, device, fs_src, txt_dst, &client);
3044
3045                                         current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
3046                                 }
3047                                 // Otherwise report right click to server
3048                                 else
3049                                 {
3050                                         camera.setDigging(1);  // right click animation (always shown for feedback)
3051
3052                                         // If the wielded item has node placement prediction,
3053                                         // make that happen
3054                                         bool placed = nodePlacementPrediction(client,
3055                                                 playeritem_def,
3056                                                 nodepos, neighbourpos);
3057
3058                                         if(placed) {
3059                                                 // Report to server
3060                                                 client.interact(3, pointed);
3061                                                 // Read the sound
3062                                                 soundmaker.m_player_rightpunch_sound =
3063                                                         playeritem_def.sound_place;
3064                                         } else {
3065                                                 soundmaker.m_player_rightpunch_sound =
3066                                                         SimpleSoundSpec();
3067                                         }
3068
3069                                         if (playeritem_def.node_placement_prediction == "" ||
3070                                                 nodedef->get(map.getNode(nodepos)).rightclickable)
3071                                                 client.interact(3, pointed); // Report to server
3072                                 }
3073                         }
3074                 }
3075                 else if(pointed.type == POINTEDTHING_OBJECT)
3076                 {
3077                         infotext = narrow_to_wide(selected_object->infoText());
3078
3079                         if(infotext == L"" && show_debug){
3080                                 infotext = narrow_to_wide(selected_object->debugInfoText());
3081                         }
3082
3083                         //if(input->getLeftClicked())
3084                         if(input->getLeftState())
3085                         {
3086                                 bool do_punch = false;
3087                                 bool do_punch_damage = false;
3088                                 if(object_hit_delay_timer <= 0.0){
3089                                         do_punch = true;
3090                                         do_punch_damage = true;
3091                                         object_hit_delay_timer = object_hit_delay;
3092                                 }
3093                                 if(input->getLeftClicked()){
3094                                         do_punch = true;
3095                                 }
3096                                 if(do_punch){
3097                                         infostream<<"Left-clicked object"<<std::endl;
3098                                         left_punch = true;
3099                                 }
3100                                 if(do_punch_damage){
3101                                         // Report direct punch
3102                                         v3f objpos = selected_object->getPosition();
3103                                         v3f dir = (objpos - player_position).normalize();
3104
3105                                         bool disable_send = selected_object->directReportPunch(
3106                                                         dir, &playeritem, time_from_last_punch);
3107                                         time_from_last_punch = 0;
3108                                         if(!disable_send)
3109                                                 client.interact(0, pointed);
3110                                 }
3111                         }
3112                         else if(input->getRightClicked())
3113                         {
3114                                 infostream<<"Right-clicked object"<<std::endl;
3115                                 client.interact(3, pointed);  // place
3116                         }
3117                 }
3118                 else if(input->getLeftState())
3119                 {
3120                         // When button is held down in air, show continuous animation
3121                         left_punch = true;
3122                 }
3123
3124                 pointed_old = pointed;
3125
3126                 if(left_punch || input->getLeftClicked())
3127                 {
3128                         camera.setDigging(0); // left click animation
3129                 }
3130
3131                 input->resetLeftClicked();
3132                 input->resetRightClicked();
3133
3134                 input->resetLeftReleased();
3135                 input->resetRightReleased();
3136
3137                 /*
3138                         Calculate stuff for drawing
3139                 */
3140
3141                 /*
3142                         Fog range
3143                 */
3144
3145                 if(draw_control.range_all)
3146                         fog_range = 100000*BS;
3147                 else {
3148                         fog_range = draw_control.wanted_range*BS + 0.0*MAP_BLOCKSIZE*BS;
3149                         fog_range = MYMIN(fog_range, (draw_control.farthest_drawn+20)*BS);
3150                         fog_range *= 0.9;
3151                 }
3152
3153                 /*
3154                         Calculate general brightness
3155                 */
3156                 u32 daynight_ratio = client.getEnv().getDayNightRatio();
3157                 float time_brightness = decode_light_f((float)daynight_ratio/1000.0);
3158                 float direct_brightness = 0;
3159                 bool sunlight_seen = false;
3160                 if(g_settings->getBool("free_move")){
3161                         direct_brightness = time_brightness;
3162                         sunlight_seen = true;
3163                 } else {
3164                         ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
3165                         float old_brightness = sky->getBrightness();
3166                         direct_brightness = (float)client.getEnv().getClientMap()
3167                                         .getBackgroundBrightness(MYMIN(fog_range*1.2, 60*BS),
3168                                         daynight_ratio, (int)(old_brightness*255.5), &sunlight_seen)
3169                                         / 255.0;
3170                 }
3171
3172                 time_of_day = client.getEnv().getTimeOfDayF();
3173                 float maxsm = 0.05;
3174                 if(fabs(time_of_day - time_of_day_smooth) > maxsm &&
3175                                 fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
3176                                 fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
3177                         time_of_day_smooth = time_of_day;
3178                 float todsm = 0.05;
3179                 if(time_of_day_smooth > 0.8 && time_of_day < 0.2)
3180                         time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
3181                                         + (time_of_day+1.0) * todsm;
3182                 else
3183                         time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
3184                                         + time_of_day * todsm;
3185
3186                 sky->update(time_of_day_smooth, time_brightness, direct_brightness,
3187                                 sunlight_seen,camera.getCameraMode(), player->getYaw(),
3188                                 player->getPitch());
3189
3190                 video::SColor bgcolor = sky->getBgColor();
3191                 video::SColor skycolor = sky->getSkyColor();
3192
3193                 /*
3194                         Update clouds
3195                 */
3196                 if(clouds){
3197                         if(sky->getCloudsVisible()){
3198                                 clouds->setVisible(true);
3199                                 clouds->step(dtime);
3200                                 clouds->update(v2f(player_position.X, player_position.Z),
3201                                                 sky->getCloudColor());
3202                         } else{
3203                                 clouds->setVisible(false);
3204                         }
3205                 }
3206
3207                 /*
3208                         Update particles
3209                 */
3210
3211                 allparticles_step(dtime);
3212                 allparticlespawners_step(dtime, client.getEnv());
3213
3214                 /*
3215                         Fog
3216                 */
3217
3218                 if(g_settings->getBool("enable_fog") && !force_fog_off)
3219                 {
3220                         driver->setFog(
3221                                 bgcolor,
3222                                 video::EFT_FOG_LINEAR,
3223                                 fog_range*0.4,
3224                                 fog_range*1.0,
3225                                 0.01,
3226                                 false, // pixel fog
3227                                 false // range fog
3228                         );
3229                 }
3230                 else
3231                 {
3232                         driver->setFog(
3233                                 bgcolor,
3234                                 video::EFT_FOG_LINEAR,
3235                                 100000*BS,
3236                                 110000*BS,
3237                                 0.01,
3238                                 false, // pixel fog
3239                                 false // range fog
3240                         );
3241                 }
3242
3243                 /*
3244                         Update gui stuff (0ms)
3245                 */
3246
3247                 //TimeTaker guiupdatetimer("Gui updating");
3248
3249                 if(show_debug)
3250                 {
3251                         static float drawtime_avg = 0;
3252                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
3253                         /*static float beginscenetime_avg = 0;
3254                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
3255                         static float scenetime_avg = 0;
3256                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
3257                         static float endscenetime_avg = 0;
3258                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/
3259
3260                         u16 fps = (1.0/dtime_avg1);
3261
3262                         std::ostringstream os(std::ios_base::binary);
3263                         os<<std::fixed
3264                                 <<"Minetest "<<minetest_version_hash
3265                                 <<" FPS = "<<fps
3266                                 <<" (R: range_all="<<draw_control.range_all<<")"
3267                                 <<std::setprecision(0)
3268                                 <<" drawtime = "<<drawtime_avg
3269                                 <<std::setprecision(1)
3270                                 <<", dtime_jitter = "
3271                                 <<(dtime_jitter1_max_fraction * 100.0)<<" %"
3272                                 <<std::setprecision(1)
3273                                 <<", v_range = "<<draw_control.wanted_range
3274                                 <<std::setprecision(3)
3275                                 <<", RTT = "<<client.getRTT();
3276                         guitext->setText(narrow_to_wide(os.str()).c_str());
3277                         guitext->setVisible(true);
3278                 }
3279                 else if(show_hud || show_chat)
3280                 {
3281                         std::ostringstream os(std::ios_base::binary);
3282                         os<<"Minetest "<<minetest_version_hash;
3283                         guitext->setText(narrow_to_wide(os.str()).c_str());
3284                         guitext->setVisible(true);
3285                 }
3286                 else
3287                 {
3288                         guitext->setVisible(false);
3289                 }
3290
3291                 if (guitext->isVisible())
3292                 {
3293                         core::rect<s32> rect(
3294                                 5,
3295                                 5,
3296                                 screensize.X,
3297                                 5 + text_height
3298                         );
3299                         guitext->setRelativePosition(rect);
3300                 }
3301
3302                 if(show_debug)
3303                 {
3304                         std::ostringstream os(std::ios_base::binary);
3305                         os<<std::setprecision(1)<<std::fixed
3306                                 <<"(" <<(player_position.X/BS)
3307                                 <<", "<<(player_position.Y/BS)
3308                                 <<", "<<(player_position.Z/BS)
3309                                 <<") (yaw="<<(wrapDegrees_0_360(camera_yaw))
3310                                 <<") (seed = "<<((u64)client.getMapSeed())
3311                                 <<")";
3312                         guitext2->setText(narrow_to_wide(os.str()).c_str());
3313                         guitext2->setVisible(true);
3314
3315                         core::rect<s32> rect(
3316                                 5,
3317                                 5 + text_height,
3318                                 screensize.X,
3319                                 5 + (text_height * 2)
3320                         );
3321                         guitext2->setRelativePosition(rect);
3322                 }
3323                 else
3324                 {
3325                         guitext2->setVisible(false);
3326                 }
3327
3328                 {
3329                         guitext_info->setText(infotext.c_str());
3330                         guitext_info->setVisible(show_hud && g_menumgr.menuCount() == 0);
3331                 }
3332
3333                 {
3334                         float statustext_time_max = 1.5;
3335                         if(!statustext.empty())
3336                         {
3337                                 statustext_time += dtime;
3338                                 if(statustext_time >= statustext_time_max)
3339                                 {
3340                                         statustext = L"";
3341                                         statustext_time = 0;
3342                                 }
3343                         }
3344                         guitext_status->setText(statustext.c_str());
3345                         guitext_status->setVisible(!statustext.empty());
3346
3347                         if(!statustext.empty())
3348                         {
3349                                 s32 status_y = screensize.Y - 130;
3350                                 core::rect<s32> rect(
3351                                                 10,
3352                                                 status_y - guitext_status->getTextHeight(),
3353                                                 10 + guitext_status->getTextWidth(),
3354                                                 status_y
3355                                 );
3356                                 guitext_status->setRelativePosition(rect);
3357
3358                                 // Fade out
3359                                 video::SColor initial_color(255,0,0,0);
3360                                 if(guienv->getSkin())
3361                                         initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
3362                                 video::SColor final_color = initial_color;
3363                                 final_color.setAlpha(0);
3364                                 video::SColor fade_color =
3365                                         initial_color.getInterpolated_quadratic(
3366                                                 initial_color,
3367                                                 final_color,
3368                                                 pow(statustext_time / (float)statustext_time_max, 2.0f));
3369                                 guitext_status->setOverrideColor(fade_color);
3370                                 guitext_status->enableOverrideColor(true);
3371                         }
3372                 }
3373
3374                 /*
3375                         Get chat messages from client
3376                 */
3377                 updateChat(client, dtime, show_debug, screensize, show_chat,
3378                                 show_profiler, chat_backend, guitext_chat, font);
3379
3380                 /*
3381                         Inventory
3382                 */
3383
3384                 if(client.getPlayerItem() != new_playeritem)
3385                 {
3386                         client.selectPlayerItem(new_playeritem);
3387                 }
3388                 if(client.getLocalInventoryUpdated())
3389                 {
3390                         //infostream<<"Updating local inventory"<<std::endl;
3391                         client.getLocalInventory(local_inventory);
3392
3393                         update_wielded_item_trigger = true;
3394                 }
3395                 if(update_wielded_item_trigger)
3396                 {
3397                         update_wielded_item_trigger = false;
3398                         // Update wielded tool
3399                         InventoryList *mlist = local_inventory.getList("main");
3400                         ItemStack item;
3401                         if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
3402                                 item = mlist->getItem(client.getPlayerItem());
3403                         camera.wield(item, client.getPlayerItem());
3404                 }
3405
3406                 /*
3407                         Update block draw list every 200ms or when camera direction has
3408                         changed much
3409                 */
3410                 update_draw_list_timer += dtime;
3411                 if(update_draw_list_timer >= 0.2 ||
3412                                 update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 ||
3413                                 camera_offset_changed){
3414                         update_draw_list_timer = 0;
3415                         client.getEnv().getClientMap().updateDrawList(driver);
3416                         update_draw_list_last_cam_dir = camera_direction;
3417                 }
3418
3419                 /*
3420                         make sure menu is on top
3421                 */
3422                 if ((!noMenuActive()) && (current_formspec)) {
3423                                 guiroot->bringToFront(current_formspec);
3424                 }
3425
3426                 /*
3427                         Drawing begins
3428                 */
3429                 TimeTaker tt_draw("mainloop: draw");
3430                 {
3431                         TimeTaker timer("beginScene");
3432                         driver->beginScene(true, true, skycolor);
3433                         beginscenetime = timer.stop(true);
3434                 }
3435
3436
3437                 draw_scene(driver, smgr, camera, client, player, hud, guienv,
3438                                 hilightboxes, screensize, skycolor, show_hud);
3439
3440                 /*
3441                         Profiler graph
3442                 */
3443                 if(show_profiler_graph)
3444                 {
3445                         graph.draw(10, screensize.Y - 10, driver, font);
3446                 }
3447
3448                 /*
3449                         Damage flash
3450                 */
3451                 if(damage_flash > 0.0)
3452                 {
3453                         video::SColor color(std::min(damage_flash, 180.0f),180,0,0);
3454                         driver->draw2DRectangle(color,
3455                                         core::rect<s32>(0,0,screensize.X,screensize.Y),
3456                                         NULL);
3457
3458                         damage_flash -= 100.0*dtime;
3459                 }
3460
3461                 /*
3462                         Damage camera tilt
3463                 */
3464                 if(player->hurt_tilt_timer > 0.0)
3465                 {
3466                         player->hurt_tilt_timer -= dtime*5;
3467                         if(player->hurt_tilt_timer < 0)
3468                                 player->hurt_tilt_strength = 0;
3469                 }
3470
3471                 /*
3472                         End scene
3473                 */
3474                 {
3475                         TimeTaker timer("endScene");
3476                         driver->endScene();
3477                         endscenetime = timer.stop(true);
3478                 }
3479
3480                 drawtime = tt_draw.stop(true);
3481                 g_profiler->graphAdd("mainloop_draw", (float)drawtime/1000.0f);
3482
3483                 /*
3484                         End of drawing
3485                 */
3486
3487                 /*
3488                         Log times and stuff for visualization
3489                 */
3490                 Profiler::GraphValues values;
3491                 g_profiler->graphGet(values);
3492                 graph.put(values);
3493         }
3494
3495         /*
3496                 Drop stuff
3497         */
3498         if (clouds)
3499                 clouds->drop();
3500         if (gui_chat_console)
3501                 gui_chat_console->drop();
3502         if (sky)
3503                 sky->drop();
3504         clear_particles();
3505
3506         /* cleanup menus */
3507         while (g_menumgr.menuCount() > 0)
3508         {
3509                 g_menumgr.m_stack.front()->setVisible(false);
3510                 g_menumgr.deletingMenu(g_menumgr.m_stack.front());
3511         }
3512         /*
3513                 Draw a "shutting down" screen, which will be shown while the map
3514                 generator and other stuff quits
3515         */
3516         {
3517                 wchar_t* text = wgettext("Shutting down stuff...");
3518                 draw_load_screen(text, device, guienv, font, 0, -1, false);
3519                 delete[] text;
3520         }
3521
3522         chat_backend.addMessage(L"", L"# Disconnected.");
3523         chat_backend.addMessage(L"", L"");
3524
3525         client.Stop();
3526
3527         //force answer all texture and shader jobs (TODO return empty values)
3528
3529         while(!client.isShutdown()) {
3530                 tsrc->processQueue();
3531                 shsrc->processQueue();
3532                 sleep_ms(100);
3533         }
3534
3535         // Client scope (client is destructed before destructing *def and tsrc)
3536         }while(0);
3537         } // try-catch
3538         catch(SerializationError &e)
3539         {
3540                 error_message = L"A serialization error occurred:\n"
3541                                 + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
3542                                 L" running a different version of Minetest.";
3543                 errorstream<<wide_to_narrow(error_message)<<std::endl;
3544         }
3545         catch(ServerError &e) {
3546                 error_message = narrow_to_wide(e.what());
3547                 errorstream << "ServerError: " << e.what() << std::endl;
3548         }
3549         catch(ModError &e) {
3550                 errorstream << "ModError: " << e.what() << std::endl;
3551                 error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
3552         }
3553
3554
3555
3556         if(!sound_is_dummy)
3557                 delete sound;
3558
3559         //has to be deleted first to stop all server threads
3560         delete server;
3561
3562         delete tsrc;
3563         delete shsrc;
3564         delete nodedef;
3565         delete itemdef;
3566
3567         //extended resource accounting
3568         infostream << "Irrlicht resources after cleanup:" << std::endl;
3569         infostream << "\tRemaining meshes   : "
3570                 << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
3571         infostream << "\tRemaining textures : "
3572                 << driver->getTextureCount() << std::endl;
3573         for (unsigned int i = 0; i < driver->getTextureCount(); i++ ) {
3574                 irr::video::ITexture* texture = driver->getTextureByIndex(i);
3575                 infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
3576                                 << std::endl;
3577         }
3578         clearTextureNameCache();
3579         infostream << "\tRemaining materials: "
3580                 << driver-> getMaterialRendererCount ()
3581                 << " (note: irrlicht doesn't support removing renderers)"<< std::endl;
3582 }