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