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