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