Move ContentFeatures to mapnode_contentfeatures.{h,cpp} and clean stuff
[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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "materials.h"
35 #include "config.h"
36 #include "clouds.h"
37 #include "camera.h"
38 #include "farmesh.h"
39 #include "mapblock.h"
40 #include "settings.h"
41 #include "profiler.h"
42 #include "mainmenumanager.h"
43 #include "gettext.h"
44 #include "log.h"
45 #include "filesys.h"
46 // Needed for some special cases for CONTENT_TORCH and CONTENT_SIGN_WALL
47 // TODO: A generic way for handling such should be created
48 #include "content_mapnode.h"
49 // Needed for sign text input
50 // TODO: A generic way for handling such should be created
51 #include "content_nodemeta.h"
52 // Needed for determining pointing to nodes
53 #include "mapnode_contentfeatures.h"
54
55 /*
56         Setting this to 1 enables a special camera mode that forces
57         the renderers to think that the camera statically points from
58         the starting place to a static direction.
59
60         This allows one to move around with the player and see what
61         is actually drawn behind solid things and behind the player.
62 */
63 #define FIELD_OF_VIEW_TEST 0
64
65
66 // Chat data
67 struct ChatLine
68 {
69         ChatLine():
70                 age(0.0)
71         {
72         }
73         ChatLine(const std::wstring &a_text):
74                 age(0.0),
75                 text(a_text)
76         {
77         }
78         float age;
79         std::wstring text;
80 };
81
82 /*
83         Inventory stuff
84 */
85
86 // Inventory actions from the menu are buffered here before sending
87 Queue<InventoryAction*> inventory_action_queue;
88 // This is a copy of the inventory that the client's environment has
89 Inventory local_inventory;
90
91 u16 g_selected_item = 0;
92
93 /*
94         Text input system
95 */
96
97 struct TextDestChat : public TextDest
98 {
99         TextDestChat(Client *client)
100         {
101                 m_client = client;
102         }
103         void gotText(std::wstring text)
104         {
105                 // Discard empty line
106                 if(text == L"")
107                         return;
108
109                 // Send to others
110                 m_client->sendChatMessage(text);
111                 // Show locally
112                 m_client->addChatMessage(text);
113         }
114
115         Client *m_client;
116 };
117
118 struct TextDestSignNode : public TextDest
119 {
120         TextDestSignNode(v3s16 p, Client *client)
121         {
122                 m_p = p;
123                 m_client = client;
124         }
125         void gotText(std::wstring text)
126         {
127                 std::string ntext = wide_to_narrow(text);
128                 infostream<<"Changing text of a sign node: "
129                                 <<ntext<<std::endl;
130                 m_client->sendSignNodeText(m_p, ntext);
131         }
132
133         v3s16 m_p;
134         Client *m_client;
135 };
136
137 /* Respawn menu callback */
138
139 class MainRespawnInitiator: public IRespawnInitiator
140 {
141 public:
142         MainRespawnInitiator(bool *active, Client *client):
143                 m_active(active), m_client(client)
144         {
145                 *m_active = true;
146         }
147         void respawn()
148         {
149                 *m_active = false;
150                 m_client->sendRespawn();
151         }
152 private:
153         bool *m_active;
154         Client *m_client;
155 };
156
157 /*
158         Hotbar draw routine
159 */
160 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
161                 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
162                 Inventory *inventory, s32 halfheartcount)
163 {
164         InventoryList *mainlist = inventory->getList("main");
165         if(mainlist == NULL)
166         {
167                 errorstream<<"draw_hotbar(): mainlist == NULL"<<std::endl;
168                 return;
169         }
170         
171         s32 padding = imgsize/12;
172         //s32 height = imgsize + padding*2;
173         s32 width = itemcount*(imgsize+padding*2);
174         
175         // Position of upper left corner of bar
176         v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
177         
178         // Draw background color
179         /*core::rect<s32> barrect(0,0,width,height);
180         barrect += pos;
181         video::SColor bgcolor(255,128,128,128);
182         driver->draw2DRectangle(bgcolor, barrect, NULL);*/
183
184         core::rect<s32> imgrect(0,0,imgsize,imgsize);
185
186         for(s32 i=0; i<itemcount; i++)
187         {
188                 InventoryItem *item = mainlist->getItem(i);
189                 
190                 core::rect<s32> rect = imgrect + pos
191                                 + v2s32(padding+i*(imgsize+padding*2), padding);
192                 
193                 if(g_selected_item == i)
194                 {
195                         video::SColor c_outside(255,255,0,0);
196                         //video::SColor c_outside(255,0,0,0);
197                         //video::SColor c_inside(255,192,192,192);
198                         s32 x1 = rect.UpperLeftCorner.X;
199                         s32 y1 = rect.UpperLeftCorner.Y;
200                         s32 x2 = rect.LowerRightCorner.X;
201                         s32 y2 = rect.LowerRightCorner.Y;
202                         // Black base borders
203                         driver->draw2DRectangle(c_outside,
204                                         core::rect<s32>(
205                                                 v2s32(x1 - padding, y1 - padding),
206                                                 v2s32(x2 + padding, y1)
207                                         ), NULL);
208                         driver->draw2DRectangle(c_outside,
209                                         core::rect<s32>(
210                                                 v2s32(x1 - padding, y2),
211                                                 v2s32(x2 + padding, y2 + padding)
212                                         ), NULL);
213                         driver->draw2DRectangle(c_outside,
214                                         core::rect<s32>(
215                                                 v2s32(x1 - padding, y1),
216                                                 v2s32(x1, y2)
217                                         ), NULL);
218                         driver->draw2DRectangle(c_outside,
219                                         core::rect<s32>(
220                                                 v2s32(x2, y1),
221                                                 v2s32(x2 + padding, y2)
222                                         ), NULL);
223                         /*// Light inside borders
224                         driver->draw2DRectangle(c_inside,
225                                         core::rect<s32>(
226                                                 v2s32(x1 - padding/2, y1 - padding/2),
227                                                 v2s32(x2 + padding/2, y1)
228                                         ), NULL);
229                         driver->draw2DRectangle(c_inside,
230                                         core::rect<s32>(
231                                                 v2s32(x1 - padding/2, y2),
232                                                 v2s32(x2 + padding/2, y2 + padding/2)
233                                         ), NULL);
234                         driver->draw2DRectangle(c_inside,
235                                         core::rect<s32>(
236                                                 v2s32(x1 - padding/2, y1),
237                                                 v2s32(x1, y2)
238                                         ), NULL);
239                         driver->draw2DRectangle(c_inside,
240                                         core::rect<s32>(
241                                                 v2s32(x2, y1),
242                                                 v2s32(x2 + padding/2, y2)
243                                         ), NULL);
244                         */
245                 }
246
247                 video::SColor bgcolor2(128,0,0,0);
248                 driver->draw2DRectangle(bgcolor2, rect, NULL);
249
250                 if(item != NULL)
251                 {
252                         drawInventoryItem(driver, font, item, rect, NULL);
253                 }
254         }
255         
256         /*
257                 Draw hearts
258         */
259         {
260                 video::ITexture *heart_texture =
261                                 driver->getTexture(getTexturePath("heart.png").c_str());
262                 v2s32 p = pos + v2s32(0, -20);
263                 for(s32 i=0; i<halfheartcount/2; i++)
264                 {
265                         const video::SColor color(255,255,255,255);
266                         const video::SColor colors[] = {color,color,color,color};
267                         core::rect<s32> rect(0,0,16,16);
268                         rect += p;
269                         driver->draw2DImage(heart_texture, rect,
270                                 core::rect<s32>(core::position2d<s32>(0,0),
271                                 core::dimension2di(heart_texture->getOriginalSize())),
272                                 NULL, colors, true);
273                         p += v2s32(16,0);
274                 }
275                 if(halfheartcount % 2 == 1)
276                 {
277                         const video::SColor color(255,255,255,255);
278                         const video::SColor colors[] = {color,color,color,color};
279                         core::rect<s32> rect(0,0,16/2,16);
280                         rect += p;
281                         core::dimension2di srcd(heart_texture->getOriginalSize());
282                         srcd.Width /= 2;
283                         driver->draw2DImage(heart_texture, rect,
284                                 core::rect<s32>(core::position2d<s32>(0,0), srcd),
285                                 NULL, colors, true);
286                         p += v2s32(16,0);
287                 }
288         }
289 }
290
291 /*
292         Find what the player is pointing at
293 */
294 void getPointedNode(Client *client, v3f player_position,
295                 v3f camera_direction, v3f camera_position,
296                 bool &nodefound, core::line3d<f32> shootline,
297                 v3s16 &nodepos, v3s16 &neighbourpos,
298                 core::aabbox3d<f32> &nodehilightbox,
299                 f32 d)
300 {
301         f32 mindistance = BS * 1001;
302         
303         v3s16 pos_i = floatToInt(player_position, BS);
304
305         /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
306                         <<std::endl;*/
307
308         s16 a = d;
309         s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
310         s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
311         s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
312         s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
313         s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
314         s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
315         
316         for(s16 y = ystart; y <= yend; y++)
317         for(s16 z = zstart; z <= zend; z++)
318         for(s16 x = xstart; x <= xend; x++)
319         {
320                 MapNode n;
321                 try
322                 {
323                         n = client->getNode(v3s16(x,y,z));
324                         if(content_pointable(n.getContent()) == false)
325                                 continue;
326                 }
327                 catch(InvalidPositionException &e)
328                 {
329                         continue;
330                 }
331
332                 v3s16 np(x,y,z);
333                 v3f npf = intToFloat(np, BS);
334                 
335                 f32 d = 0.01;
336                 
337                 v3s16 dirs[6] = {
338                         v3s16(0,0,1), // back
339                         v3s16(0,1,0), // top
340                         v3s16(1,0,0), // right
341                         v3s16(0,0,-1), // front
342                         v3s16(0,-1,0), // bottom
343                         v3s16(-1,0,0), // left
344                 };
345                 
346                 /*
347                         Meta-objects
348                 */
349                 if(n.getContent() == CONTENT_TORCH)
350                 {
351                         v3s16 dir = unpackDir(n.param2);
352                         v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
353                         dir_f *= BS/2 - BS/6 - BS/20;
354                         v3f cpf = npf + dir_f;
355                         f32 distance = (cpf - camera_position).getLength();
356
357                         core::aabbox3d<f32> box;
358                         
359                         // bottom
360                         if(dir == v3s16(0,-1,0))
361                         {
362                                 box = core::aabbox3d<f32>(
363                                         npf - v3f(BS/6, BS/2, BS/6),
364                                         npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
365                                 );
366                         }
367                         // top
368                         else if(dir == v3s16(0,1,0))
369                         {
370                                 box = core::aabbox3d<f32>(
371                                         npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
372                                         npf + v3f(BS/6, BS/2, BS/6)
373                                 );
374                         }
375                         // side
376                         else
377                         {
378                                 box = core::aabbox3d<f32>(
379                                         cpf - v3f(BS/6, BS/3, BS/6),
380                                         cpf + v3f(BS/6, BS/3, BS/6)
381                                 );
382                         }
383
384                         if(distance < mindistance)
385                         {
386                                 if(box.intersectsWithLine(shootline))
387                                 {
388                                         nodefound = true;
389                                         nodepos = np;
390                                         neighbourpos = np;
391                                         mindistance = distance;
392                                         nodehilightbox = box;
393                                 }
394                         }
395                 }
396                 else if(n.getContent() == CONTENT_SIGN_WALL)
397                 {
398                         v3s16 dir = unpackDir(n.param2);
399                         v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
400                         dir_f *= BS/2 - BS/6 - BS/20;
401                         v3f cpf = npf + dir_f;
402                         f32 distance = (cpf - camera_position).getLength();
403
404                         v3f vertices[4] =
405                         {
406                                 v3f(BS*0.42,-BS*0.35,-BS*0.4),
407                                 v3f(BS*0.49, BS*0.35, BS*0.4),
408                         };
409
410                         for(s32 i=0; i<2; i++)
411                         {
412                                 if(dir == v3s16(1,0,0))
413                                         vertices[i].rotateXZBy(0);
414                                 if(dir == v3s16(-1,0,0))
415                                         vertices[i].rotateXZBy(180);
416                                 if(dir == v3s16(0,0,1))
417                                         vertices[i].rotateXZBy(90);
418                                 if(dir == v3s16(0,0,-1))
419                                         vertices[i].rotateXZBy(-90);
420                                 if(dir == v3s16(0,-1,0))
421                                         vertices[i].rotateXYBy(-90);
422                                 if(dir == v3s16(0,1,0))
423                                         vertices[i].rotateXYBy(90);
424
425                                 vertices[i] += npf;
426                         }
427
428                         core::aabbox3d<f32> box;
429
430                         box = core::aabbox3d<f32>(vertices[0]);
431                         box.addInternalPoint(vertices[1]);
432
433                         if(distance < mindistance)
434                         {
435                                 if(box.intersectsWithLine(shootline))
436                                 {
437                                         nodefound = true;
438                                         nodepos = np;
439                                         neighbourpos = np;
440                                         mindistance = distance;
441                                         nodehilightbox = box;
442                                 }
443                         }
444                 }
445
446                 else if(n.getContent() == CONTENT_LADDER)
447                 {
448                         v3s16 dir = unpackDir(n.param2);
449                         v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
450                         dir_f *= BS/2 - BS/6 - BS/20;
451                         v3f cpf = npf + dir_f;
452                         f32 distance = (cpf - camera_position).getLength();
453
454                         v3f vertices[4] =
455                         {
456                                 v3f(BS*0.42,-BS/2,-BS/2),
457                                 v3f(BS*0.49, BS/2, BS/2),
458                         };
459
460                         for(s32 i=0; i<2; i++)
461                         {
462                                 if(dir == v3s16(1,0,0))
463                                         vertices[i].rotateXZBy(0);
464                                 if(dir == v3s16(-1,0,0))
465                                         vertices[i].rotateXZBy(180);
466                                 if(dir == v3s16(0,0,1))
467                                         vertices[i].rotateXZBy(90);
468                                 if(dir == v3s16(0,0,-1))
469                                         vertices[i].rotateXZBy(-90);
470                                 if(dir == v3s16(0,-1,0))
471                                         vertices[i].rotateXYBy(-90);
472                                 if(dir == v3s16(0,1,0))
473                                         vertices[i].rotateXYBy(90);
474
475                                 vertices[i] += npf;
476                         }
477
478                         core::aabbox3d<f32> box;
479
480                         box = core::aabbox3d<f32>(vertices[0]);
481                         box.addInternalPoint(vertices[1]);
482
483                         if(distance < mindistance)
484                         {
485                                 if(box.intersectsWithLine(shootline))
486                                 {
487                                         nodefound = true;
488                                         nodepos = np;
489                                         neighbourpos = np;
490                                         mindistance = distance;
491                                         nodehilightbox = box;
492                                 }
493                         }
494                 }
495                 else if(n.getContent() == CONTENT_RAIL)
496                 {
497                         v3s16 dir = unpackDir(n.param0);
498                         v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
499                         dir_f *= BS/2 - BS/6 - BS/20;
500                         v3f cpf = npf + dir_f;
501                         f32 distance = (cpf - camera_position).getLength();
502
503                         float d = (float)BS/16;
504                         v3f vertices[4] =
505                         {
506                                 v3f(BS/2, -BS/2+d, -BS/2),
507                                 v3f(-BS/2, -BS/2, BS/2),
508                         };
509
510                         for(s32 i=0; i<2; i++)
511                         {
512                                 vertices[i] += npf;
513                         }
514
515                         core::aabbox3d<f32> box;
516
517                         box = core::aabbox3d<f32>(vertices[0]);
518                         box.addInternalPoint(vertices[1]);
519
520                         if(distance < mindistance)
521                         {
522                                 if(box.intersectsWithLine(shootline))
523                                 {
524                                         nodefound = true;
525                                         nodepos = np;
526                                         neighbourpos = np;
527                                         mindistance = distance;
528                                         nodehilightbox = box;
529                                 }
530                         }
531                 }
532                 /*
533                         Regular blocks
534                 */
535                 else
536                 {
537                         for(u16 i=0; i<6; i++)
538                         {
539                                 v3f dir_f = v3f(dirs[i].X,
540                                                 dirs[i].Y, dirs[i].Z);
541                                 v3f centerpoint = npf + dir_f * BS/2;
542                                 f32 distance =
543                                                 (centerpoint - camera_position).getLength();
544                                 
545                                 if(distance < mindistance)
546                                 {
547                                         core::CMatrix4<f32> m;
548                                         m.buildRotateFromTo(v3f(0,0,1), dir_f);
549
550                                         // This is the back face
551                                         v3f corners[2] = {
552                                                 v3f(BS/2, BS/2, BS/2),
553                                                 v3f(-BS/2, -BS/2, BS/2+d)
554                                         };
555                                         
556                                         for(u16 j=0; j<2; j++)
557                                         {
558                                                 m.rotateVect(corners[j]);
559                                                 corners[j] += npf;
560                                         }
561
562                                         core::aabbox3d<f32> facebox(corners[0]);
563                                         facebox.addInternalPoint(corners[1]);
564
565                                         if(facebox.intersectsWithLine(shootline))
566                                         {
567                                                 nodefound = true;
568                                                 nodepos = np;
569                                                 neighbourpos = np + dirs[i];
570                                                 mindistance = distance;
571
572                                                 //nodehilightbox = facebox;
573
574                                                 const float d = 0.502;
575                                                 core::aabbox3d<f32> nodebox
576                                                                 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
577                                                 v3f nodepos_f = intToFloat(nodepos, BS);
578                                                 nodebox.MinEdge += nodepos_f;
579                                                 nodebox.MaxEdge += nodepos_f;
580                                                 nodehilightbox = nodebox;
581                                         }
582                                 } // if distance < mindistance
583                         } // for dirs
584                 } // regular block
585         } // for coords
586 }
587
588 void update_skybox(video::IVideoDriver* driver,
589                 scene::ISceneManager* smgr, scene::ISceneNode* &skybox,
590                 float brightness)
591 {
592         if(skybox)
593         {
594                 skybox->remove();
595         }
596         
597         /*// Disable skybox if FarMesh is enabled
598         if(g_settings->getBool("enable_farmesh"))
599                 return;*/
600         
601         if(brightness >= 0.5)
602         {
603                 skybox = smgr->addSkyBoxSceneNode(
604                         driver->getTexture(getTexturePath("skybox2.png").c_str()),
605                         driver->getTexture(getTexturePath("skybox3.png").c_str()),
606                         driver->getTexture(getTexturePath("skybox1.png").c_str()),
607                         driver->getTexture(getTexturePath("skybox1.png").c_str()),
608                         driver->getTexture(getTexturePath("skybox1.png").c_str()),
609                         driver->getTexture(getTexturePath("skybox1.png").c_str()));
610         }
611         else if(brightness >= 0.2)
612         {
613                 skybox = smgr->addSkyBoxSceneNode(
614                         driver->getTexture(getTexturePath("skybox2_dawn.png").c_str()),
615                         driver->getTexture(getTexturePath("skybox3_dawn.png").c_str()),
616                         driver->getTexture(getTexturePath("skybox1_dawn.png").c_str()),
617                         driver->getTexture(getTexturePath("skybox1_dawn.png").c_str()),
618                         driver->getTexture(getTexturePath("skybox1_dawn.png").c_str()),
619                         driver->getTexture(getTexturePath("skybox1_dawn.png").c_str()));
620         }
621         else
622         {
623                 skybox = smgr->addSkyBoxSceneNode(
624                         driver->getTexture(getTexturePath("skybox2_night.png").c_str()),
625                         driver->getTexture(getTexturePath("skybox3_night.png").c_str()),
626                         driver->getTexture(getTexturePath("skybox1_night.png").c_str()),
627                         driver->getTexture(getTexturePath("skybox1_night.png").c_str()),
628                         driver->getTexture(getTexturePath("skybox1_night.png").c_str()),
629                         driver->getTexture(getTexturePath("skybox1_night.png").c_str()));
630         }
631 }
632
633 /*
634         Draws a screen with a single text on it.
635         Text will be removed when the screen is drawn the next time.
636 */
637 /*gui::IGUIStaticText **/
638 void draw_load_screen(const std::wstring &text,
639                 video::IVideoDriver* driver, gui::IGUIFont* font)
640 {
641         v2u32 screensize = driver->getScreenSize();
642         const wchar_t *loadingtext = text.c_str();
643         core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
644         core::vector2d<s32> textsize(textsize_u.X,textsize_u.Y);
645         core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
646         core::rect<s32> textrect(center - textsize/2, center + textsize/2);
647
648         gui::IGUIStaticText *guitext = guienv->addStaticText(
649                         loadingtext, textrect, false, false);
650         guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
651
652         driver->beginScene(true, true, video::SColor(255,0,0,0));
653         guienv->drawAll();
654         driver->endScene();
655         
656         guitext->remove();
657         
658         //return guitext;
659 }
660
661 void the_game(
662         bool &kill,
663         bool random_input,
664         InputHandler *input,
665         IrrlichtDevice *device,
666         gui::IGUIFont* font,
667         std::string map_dir,
668         std::string playername,
669         std::string password,
670         std::string address,
671         u16 port,
672         std::wstring &error_message,
673     std::string configpath
674 )
675 {
676         video::IVideoDriver* driver = device->getVideoDriver();
677         scene::ISceneManager* smgr = device->getSceneManager();
678         
679         // Calculate text height using the font
680         u32 text_height = font->getDimension(L"Random test string").Height;
681
682         v2u32 screensize(0,0);
683         v2u32 last_screensize(0,0);
684         screensize = driver->getScreenSize();
685
686         const s32 hotbar_itemcount = 8;
687         //const s32 hotbar_imagesize = 36;
688         //const s32 hotbar_imagesize = 64;
689         s32 hotbar_imagesize = 48;
690         
691         // The color of the sky
692
693         //video::SColor skycolor = video::SColor(255,140,186,250);
694
695         video::SColor bgcolor_bright = video::SColor(255,170,200,230);
696
697         /*
698                 Draw "Loading" screen
699         */
700         /*gui::IGUIStaticText *gui_loadingtext = */
701         //draw_load_screen(L"Loading and connecting...", driver, font);
702
703         draw_load_screen(L"Loading...", driver, font);
704         
705         /*
706                 Create server.
707                 SharedPtr will delete it when it goes out of scope.
708         */
709         SharedPtr<Server> server;
710         if(address == ""){
711                 draw_load_screen(L"Creating server...", driver, font);
712                 infostream<<"Creating server"<<std::endl;
713                 server = new Server(map_dir, configpath);
714                 server->start(port);
715         }
716         
717         /*
718                 Create client
719         */
720
721         draw_load_screen(L"Creating client...", driver, font);
722         infostream<<"Creating client"<<std::endl;
723         MapDrawControl draw_control;
724         Client client(device, playername.c_str(), password, draw_control);
725                         
726         draw_load_screen(L"Resolving address...", driver, font);
727         Address connect_address(0,0,0,0, port);
728         try{
729                 if(address == "")
730                         //connect_address.Resolve("localhost");
731                         connect_address.setAddress(127,0,0,1);
732                 else
733                         connect_address.Resolve(address.c_str());
734         }
735         catch(ResolveError &e)
736         {
737                 errorstream<<"Couldn't resolve address"<<std::endl;
738                 //return 0;
739                 error_message = L"Couldn't resolve address";
740                 //gui_loadingtext->remove();
741                 return;
742         }
743
744         /*
745                 Attempt to connect to the server
746         */
747         
748         infostream<<"Connecting to server at ";
749         connect_address.print(&infostream);
750         infostream<<std::endl;
751         client.connect(connect_address);
752
753         bool could_connect = false;
754         
755         try{
756                 float time_counter = 0.0;
757                 for(;;)
758                 {
759                         if(client.connectedAndInitialized())
760                         {
761                                 could_connect = true;
762                                 break;
763                         }
764                         if(client.accessDenied())
765                         {
766                                 break;
767                         }
768                         // Wait for 10 seconds
769                         if(time_counter >= 10.0)
770                         {
771                                 break;
772                         }
773                         
774                         std::wostringstream ss;
775                         ss<<L"Connecting to server... (timeout in ";
776                         ss<<(int)(10.0 - time_counter + 1.0);
777                         ss<<L" seconds)";
778                         draw_load_screen(ss.str(), driver, font);
779
780                         /*// Update screen
781                         driver->beginScene(true, true, video::SColor(255,0,0,0));
782                         guienv->drawAll();
783                         driver->endScene();*/
784
785                         // Update client and server
786
787                         client.step(0.1);
788
789                         if(server != NULL)
790                                 server->step(0.1);
791                         
792                         // Delay a bit
793                         sleep_ms(100);
794                         time_counter += 0.1;
795                 }
796         }
797         catch(con::PeerNotFoundException &e)
798         {}
799
800         if(could_connect == false)
801         {
802                 if(client.accessDenied())
803                 {
804                         error_message = L"Access denied. Reason: "
805                                         +client.accessDeniedReason();
806                         errorstream<<wide_to_narrow(error_message)<<std::endl;
807                 }
808                 else
809                 {
810                         error_message = L"Connection timed out.";
811                         errorstream<<"Timed out."<<std::endl;
812                 }
813                 //gui_loadingtext->remove();
814                 return;
815         }
816
817         /*
818                 Create skybox
819         */
820         float old_brightness = 1.0;
821         scene::ISceneNode* skybox = NULL;
822         update_skybox(driver, smgr, skybox, 1.0);
823         
824         /*
825                 Create the camera node
826         */
827         Camera camera(smgr, draw_control);
828         if (!camera.successfullyCreated(error_message))
829                 return;
830
831         f32 camera_yaw = 0; // "right/left"
832         f32 camera_pitch = 0; // "up/down"
833
834         /*
835                 Clouds
836         */
837         
838         float cloud_height = BS*100;
839         Clouds *clouds = NULL;
840         if(g_settings->getBool("enable_clouds"))
841         {
842                 clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1,
843                                 cloud_height, time(0));
844         }
845         
846         /*
847                 FarMesh
848         */
849
850         FarMesh *farmesh = NULL;
851         if(g_settings->getBool("enable_farmesh"))
852         {
853                 farmesh = new FarMesh(smgr->getRootSceneNode(), smgr, -1, client.getMapSeed(), &client);
854         }
855
856         /*
857                 Move into game
858         */
859         
860         //gui_loadingtext->remove();
861
862         /*
863                 Add some gui stuff
864         */
865
866         // First line of debug text
867         gui::IGUIStaticText *guitext = guienv->addStaticText(
868                         L"Minetest-c55",
869                         core::rect<s32>(5, 5, 795, 5+text_height),
870                         false, false);
871         // Second line of debug text
872         gui::IGUIStaticText *guitext2 = guienv->addStaticText(
873                         L"",
874                         core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
875                         false, false);
876         // At the middle of the screen
877         // Object infos are shown in this
878         gui::IGUIStaticText *guitext_info = guienv->addStaticText(
879                         L"",
880                         core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
881                         false, false);
882         
883         // Chat text
884         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
885                         L"",
886                         core::rect<s32>(0,0,0,0),
887                         //false, false); // Disable word wrap as of now
888                         false, true);
889         //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
890         core::list<ChatLine> chat_lines;
891         
892         // Profiler text (size is updated when text is updated)
893         gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
894                         L"<Profiler>",
895                         core::rect<s32>(6, 4+(text_height+5)*2, 400,
896                         (text_height+5)*2 + text_height*35),
897                         false, false);
898         guitext_profiler->setBackgroundColor(video::SColor(80,0,0,0));
899         guitext_profiler->setVisible(false);
900         
901         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
902                         (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
903         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
904                         (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
905         
906         // Test the text input system
907         /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
908                         NULL))->drop();*/
909         /*GUIMessageMenu *menu =
910                         new GUIMessageMenu(guienv, guiroot, -1, 
911                                 &g_menumgr,
912                                 L"Asd");
913         menu->drop();*/
914         
915         // Launch pause menu
916         (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
917                         &g_menumgr))->drop();
918         
919         // Enable texts
920         /*guitext2->setVisible(true);
921         guitext_info->setVisible(true);
922         guitext_chat->setVisible(true);*/
923
924         //s32 guitext_chat_pad_bottom = 70;
925
926         /*
927                 Some statistics are collected in these
928         */
929         u32 drawtime = 0;
930         u32 beginscenetime = 0;
931         u32 scenetime = 0;
932         u32 endscenetime = 0;
933         
934         // A test
935         //throw con::PeerNotFoundException("lol");
936
937         core::list<float> frametime_log;
938
939         float nodig_delay_counter = 0.0;
940         float dig_time = 0.0;
941         u16 dig_index = 0;
942         v3s16 nodepos_old(-32768,-32768,-32768);
943
944         float damage_flash_timer = 0;
945         s16 farmesh_range = 20*MAP_BLOCKSIZE;
946
947         const float object_hit_delay = 0.5;
948         float object_hit_delay_timer = 0.0;
949         
950         bool invert_mouse = g_settings->getBool("invert_mouse");
951
952         bool respawn_menu_active = false;
953
954         bool show_profiler = false;
955         bool force_fog_off = false;
956         bool disable_camera_update = false;
957
958         /*
959                 Main loop
960         */
961
962         bool first_loop_after_window_activation = true;
963
964         // TODO: Convert the static interval timers to these
965         // Interval limiter for profiler
966         IntervalLimiter m_profiler_interval;
967
968         // Time is in milliseconds
969         // NOTE: getRealTime() causes strange problems in wine (imprecision?)
970         // NOTE: So we have to use getTime() and call run()s between them
971         u32 lasttime = device->getTimer()->getTime();
972
973         while(device->run() && kill == false)
974         {
975                 //std::cerr<<"frame"<<std::endl;
976
977                 if(client.accessDenied())
978                 {
979                         error_message = L"Access denied. Reason: "
980                                         +client.accessDeniedReason();
981                         errorstream<<wide_to_narrow(error_message)<<std::endl;
982                         break;
983                 }
984
985                 if(g_gamecallback->disconnect_requested)
986                 {
987                         g_gamecallback->disconnect_requested = false;
988                         break;
989                 }
990
991                 if(g_gamecallback->changepassword_requested)
992                 {
993                         (new GUIPasswordChange(guienv, guiroot, -1,
994                                 &g_menumgr, &client))->drop();
995                         g_gamecallback->changepassword_requested = false;
996                 }
997
998                 /*
999                         Process TextureSource's queue
1000                 */
1001                 ((TextureSource*)g_texturesource)->processQueue();
1002
1003                 /*
1004                         Random calculations
1005                 */
1006                 last_screensize = screensize;
1007                 screensize = driver->getScreenSize();
1008                 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
1009                 //bool screensize_changed = screensize != last_screensize;
1010
1011                 // Resize hotbar
1012                 if(screensize.Y <= 800)
1013                         hotbar_imagesize = 32;
1014                 else if(screensize.Y <= 1280)
1015                         hotbar_imagesize = 48;
1016                 else
1017                         hotbar_imagesize = 64;
1018                 
1019                 // Hilight boxes collected during the loop and displayed
1020                 core::list< core::aabbox3d<f32> > hilightboxes;
1021                 
1022                 // Info text
1023                 std::wstring infotext;
1024
1025                 // When screen size changes, update positions and sizes of stuff
1026                 /*if(screensize_changed)
1027                 {
1028                         v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
1029                         quick_inventory->updatePosition(pos);
1030                 }*/
1031
1032                 //TimeTaker //timer1("//timer1");
1033                 
1034                 // Time of frame without fps limit
1035                 float busytime;
1036                 u32 busytime_u32;
1037                 {
1038                         // not using getRealTime is necessary for wine
1039                         u32 time = device->getTimer()->getTime();
1040                         if(time > lasttime)
1041                                 busytime_u32 = time - lasttime;
1042                         else
1043                                 busytime_u32 = 0;
1044                         busytime = busytime_u32 / 1000.0;
1045                 }
1046
1047                 //infostream<<"busytime_u32="<<busytime_u32<<std::endl;
1048         
1049                 // Necessary for device->getTimer()->getTime()
1050                 device->run();
1051
1052                 /*
1053                         FPS limiter
1054                 */
1055
1056                 {
1057                         float fps_max = g_settings->getFloat("fps_max");
1058                         u32 frametime_min = 1000./fps_max;
1059                         
1060                         if(busytime_u32 < frametime_min)
1061                         {
1062                                 u32 sleeptime = frametime_min - busytime_u32;
1063                                 device->sleep(sleeptime);
1064                         }
1065                 }
1066
1067                 // Necessary for device->getTimer()->getTime()
1068                 device->run();
1069
1070                 /*
1071                         Time difference calculation
1072                 */
1073                 f32 dtime; // in seconds
1074                 
1075                 u32 time = device->getTimer()->getTime();
1076                 if(time > lasttime)
1077                         dtime = (time - lasttime) / 1000.0;
1078                 else
1079                         dtime = 0;
1080                 lasttime = time;
1081
1082                 /* Run timers */
1083
1084                 object_hit_delay_timer -= dtime;
1085
1086                 g_profiler->add("Elapsed time", dtime);
1087                 g_profiler->avg("FPS", 1./dtime);
1088
1089                 /*
1090                         Log frametime for visualization
1091                 */
1092                 frametime_log.push_back(dtime);
1093                 if(frametime_log.size() > 100)
1094                 {
1095                         core::list<float>::Iterator i = frametime_log.begin();
1096                         frametime_log.erase(i);
1097                 }
1098
1099                 /*
1100                         Visualize frametime in terminal
1101                 */
1102                 /*for(u32 i=0; i<dtime*400; i++)
1103                         infostream<<"X";
1104                 infostream<<std::endl;*/
1105
1106                 /*
1107                         Time average and jitter calculation
1108                 */
1109
1110                 static f32 dtime_avg1 = 0.0;
1111                 dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
1112                 f32 dtime_jitter1 = dtime - dtime_avg1;
1113
1114                 static f32 dtime_jitter1_max_sample = 0.0;
1115                 static f32 dtime_jitter1_max_fraction = 0.0;
1116                 {
1117                         static f32 jitter1_max = 0.0;
1118                         static f32 counter = 0.0;
1119                         if(dtime_jitter1 > jitter1_max)
1120                                 jitter1_max = dtime_jitter1;
1121                         counter += dtime;
1122                         if(counter > 0.0)
1123                         {
1124                                 counter -= 3.0;
1125                                 dtime_jitter1_max_sample = jitter1_max;
1126                                 dtime_jitter1_max_fraction
1127                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
1128                                 jitter1_max = 0.0;
1129                         }
1130                 }
1131                 
1132                 /*
1133                         Busytime average and jitter calculation
1134                 */
1135
1136                 static f32 busytime_avg1 = 0.0;
1137                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
1138                 f32 busytime_jitter1 = busytime - busytime_avg1;
1139                 
1140                 static f32 busytime_jitter1_max_sample = 0.0;
1141                 static f32 busytime_jitter1_min_sample = 0.0;
1142                 {
1143                         static f32 jitter1_max = 0.0;
1144                         static f32 jitter1_min = 0.0;
1145                         static f32 counter = 0.0;
1146                         if(busytime_jitter1 > jitter1_max)
1147                                 jitter1_max = busytime_jitter1;
1148                         if(busytime_jitter1 < jitter1_min)
1149                                 jitter1_min = busytime_jitter1;
1150                         counter += dtime;
1151                         if(counter > 0.0){
1152                                 counter -= 3.0;
1153                                 busytime_jitter1_max_sample = jitter1_max;
1154                                 busytime_jitter1_min_sample = jitter1_min;
1155                                 jitter1_max = 0.0;
1156                                 jitter1_min = 0.0;
1157                         }
1158                 }
1159                 
1160                 /*
1161                         Debug info for client
1162                 */
1163                 {
1164                         static float counter = 0.0;
1165                         counter -= dtime;
1166                         if(counter < 0)
1167                         {
1168                                 counter = 30.0;
1169                                 client.printDebugInfo(infostream);
1170                         }
1171                 }
1172
1173                 /*
1174                         Profiler
1175                 */
1176                 float profiler_print_interval =
1177                                 g_settings->getFloat("profiler_print_interval");
1178                 bool print_to_log = true;
1179                 if(profiler_print_interval == 0){
1180                         print_to_log = false;
1181                         profiler_print_interval = 5;
1182                 }
1183                 if(m_profiler_interval.step(dtime, profiler_print_interval))
1184                 {
1185                         if(print_to_log){
1186                                 infostream<<"Profiler:"<<std::endl;
1187                                 g_profiler->print(infostream);
1188                         }
1189
1190                         std::ostringstream os(std::ios_base::binary);
1191                         g_profiler->print(os);
1192                         std::wstring text = narrow_to_wide(os.str());
1193                         guitext_profiler->setText(text.c_str());
1194
1195                         g_profiler->clear();
1196                         
1197                         s32 w = font->getDimension(text.c_str()).Width;
1198                         if(w < 400)
1199                                 w = 400;
1200                         core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
1201                                         8+(text_height+5)*2 +
1202                                         font->getDimension(text.c_str()).Height);
1203                         guitext_profiler->setRelativePosition(rect);
1204                 }
1205
1206                 /*
1207                         Direct handling of user input
1208                 */
1209                 
1210                 // Reset input if window not active or some menu is active
1211                 if(device->isWindowActive() == false || noMenuActive() == false)
1212                 {
1213                         input->clear();
1214                 }
1215
1216                 // Input handler step() (used by the random input generator)
1217                 input->step(dtime);
1218
1219                 /*
1220                         Launch menus according to keys
1221                 */
1222                 if(input->wasKeyDown(getKeySetting("keymap_inventory")))
1223                 {
1224                         infostream<<"the_game: "
1225                                         <<"Launching inventory"<<std::endl;
1226                         
1227                         GUIInventoryMenu *menu =
1228                                 new GUIInventoryMenu(guienv, guiroot, -1,
1229                                         &g_menumgr, v2s16(8,7),
1230                                         client.getInventoryContext(),
1231                                         &client);
1232
1233                         core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1234                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1235                                         "list", "current_player", "main",
1236                                         v2s32(0, 3), v2s32(8, 4)));
1237                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1238                                         "list", "current_player", "craft",
1239                                         v2s32(3, 0), v2s32(3, 3)));
1240                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1241                                         "list", "current_player", "craftresult",
1242                                         v2s32(7, 1), v2s32(1, 1)));
1243
1244                         menu->setDrawSpec(draw_spec);
1245
1246                         menu->drop();
1247                 }
1248                 else if(input->wasKeyDown(EscapeKey))
1249                 {
1250                         infostream<<"the_game: "
1251                                         <<"Launching pause menu"<<std::endl;
1252                         // It will delete itself by itself
1253                         (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
1254                                         &g_menumgr))->drop();
1255
1256                         // Move mouse cursor on top of the disconnect button
1257                         input->setMousePos(displaycenter.X, displaycenter.Y+25);
1258                 }
1259                 else if(input->wasKeyDown(getKeySetting("keymap_chat")))
1260                 {
1261                         TextDest *dest = new TextDestChat(&client);
1262
1263                         (new GUITextInputMenu(guienv, guiroot, -1,
1264                                         &g_menumgr, dest,
1265                                         L""))->drop();
1266                 }
1267                 else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
1268                 {
1269                         TextDest *dest = new TextDestChat(&client);
1270
1271                         (new GUITextInputMenu(guienv, guiroot, -1,
1272                                         &g_menumgr, dest,
1273                                         L"/"))->drop();
1274                 }
1275                 else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
1276                 {
1277                         if(g_settings->getBool("free_move"))
1278                         {
1279                                 g_settings->set("free_move","false");
1280                                 chat_lines.push_back(ChatLine(L"free_move disabled"));
1281                         }
1282                         else
1283                         {
1284                                 g_settings->set("free_move","true");
1285                                 chat_lines.push_back(ChatLine(L"free_move enabled"));
1286                         }
1287                 }
1288                 else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
1289                 {
1290                         if(g_settings->getBool("fast_move"))
1291                         {
1292                                 g_settings->set("fast_move","false");
1293                                 chat_lines.push_back(ChatLine(L"fast_move disabled"));
1294                         }
1295                         else
1296                         {
1297                                 g_settings->set("fast_move","true");
1298                                 chat_lines.push_back(ChatLine(L"fast_move enabled"));
1299                         }
1300                 }
1301                 else if(input->wasKeyDown(getKeySetting("keymap_frametime_graph")))
1302                 {
1303                         if(g_settings->getBool("frametime_graph"))
1304                         {
1305                                 g_settings->set("frametime_graph","false");
1306                                 chat_lines.push_back(ChatLine(L"frametime_graph disabled"));
1307                         }
1308                         else
1309                         {
1310                                 g_settings->set("frametime_graph","true");
1311                                 chat_lines.push_back(ChatLine(L"frametime_graph enabled"));
1312                         }
1313                 }
1314                 else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
1315                 {
1316                         irr::video::IImage* const image = driver->createScreenShot(); 
1317                         if (image) { 
1318                                 irr::c8 filename[256]; 
1319                                 snprintf(filename, 256, "%s" DIR_DELIM "screenshot_%u.png", 
1320                                                  g_settings->get("screenshot_path").c_str(),
1321                                                  device->getTimer()->getRealTime()); 
1322                                 if (driver->writeImageToFile(image, filename)) {
1323                                         std::wstringstream sstr;
1324                                         sstr<<"Saved screenshot to '"<<filename<<"'";
1325                                         infostream<<"Saved screenshot to '"<<filename<<"'"<<std::endl;
1326                                         chat_lines.push_back(ChatLine(sstr.str()));
1327                                 } else{
1328                                         infostream<<"Failed to save screenshot '"<<filename<<"'"<<std::endl;
1329                                 }
1330                                 image->drop(); 
1331                         }                        
1332                 }
1333                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
1334                 {
1335                         show_profiler = !show_profiler;
1336                         guitext_profiler->setVisible(show_profiler);
1337                         if(show_profiler)
1338                                 chat_lines.push_back(ChatLine(L"Profiler disabled"));
1339                         else
1340                                 chat_lines.push_back(ChatLine(L"Profiler enabled"));
1341                 }
1342                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
1343                 {
1344                         force_fog_off = !force_fog_off;
1345                         if(force_fog_off)
1346                                 chat_lines.push_back(ChatLine(L"Fog disabled"));
1347                         else
1348                                 chat_lines.push_back(ChatLine(L"Fog enabled"));
1349                 }
1350                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
1351                 {
1352                         disable_camera_update = !disable_camera_update;
1353                         if(disable_camera_update)
1354                                 chat_lines.push_back(ChatLine(L"Camera update disabled"));
1355                         else
1356                                 chat_lines.push_back(ChatLine(L"Camera update enabled"));
1357                 }
1358
1359                 // Item selection with mouse wheel
1360                 {
1361                         s32 wheel = input->getMouseWheel();
1362                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
1363                                         hotbar_itemcount-1);
1364
1365                         if(wheel < 0)
1366                         {
1367                                 if(g_selected_item < max_item)
1368                                         g_selected_item++;
1369                                 else
1370                                         g_selected_item = 0;
1371                         }
1372                         else if(wheel > 0)
1373                         {
1374                                 if(g_selected_item > 0)
1375                                         g_selected_item--;
1376                                 else
1377                                         g_selected_item = max_item;
1378                         }
1379                 }
1380                 
1381                 // Item selection
1382                 for(u16 i=0; i<10; i++)
1383                 {
1384                         const KeyPress *kp = NumberKey + (i + 1) % 10;
1385                         if(input->wasKeyDown(*kp))
1386                         {
1387                                 if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
1388                                 {
1389                                         g_selected_item = i;
1390
1391                                         infostream<<"Selected item: "
1392                                                         <<g_selected_item<<std::endl;
1393                                 }
1394                         }
1395                 }
1396
1397                 // Viewing range selection
1398                 if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
1399                 {
1400                         if(draw_control.range_all)
1401                         {
1402                                 draw_control.range_all = false;
1403                                 infostream<<"Disabled full viewing range"<<std::endl;
1404                         }
1405                         else
1406                         {
1407                                 draw_control.range_all = true;
1408                                 infostream<<"Enabled full viewing range"<<std::endl;
1409                         }
1410                 }
1411
1412                 // Print debug stacks
1413                 if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
1414                 {
1415                         dstream<<"-----------------------------------------"
1416                                         <<std::endl;
1417                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
1418                         dstream<<"-----------------------------------------"
1419                                         <<std::endl;
1420                         debug_stacks_print();
1421                 }
1422
1423                 /*
1424                         Mouse and camera control
1425                         NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
1426                 */
1427                 
1428                 if((device->isWindowActive() && noMenuActive()) || random_input)
1429                 {
1430                         if(!random_input)
1431                         {
1432                                 // Mac OSX gets upset if this is set every frame
1433                                 if(device->getCursorControl()->isVisible())
1434                                         device->getCursorControl()->setVisible(false);
1435                         }
1436
1437                         if(first_loop_after_window_activation){
1438                                 //infostream<<"window active, first loop"<<std::endl;
1439                                 first_loop_after_window_activation = false;
1440                         }
1441                         else{
1442                                 s32 dx = input->getMousePos().X - displaycenter.X;
1443                                 s32 dy = input->getMousePos().Y - displaycenter.Y;
1444                                 if(invert_mouse)
1445                                         dy = -dy;
1446                                 //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
1447                                 
1448                                 /*const float keyspeed = 500;
1449                                 if(input->isKeyDown(irr::KEY_UP))
1450                                         dy -= dtime * keyspeed;
1451                                 if(input->isKeyDown(irr::KEY_DOWN))
1452                                         dy += dtime * keyspeed;
1453                                 if(input->isKeyDown(irr::KEY_LEFT))
1454                                         dx -= dtime * keyspeed;
1455                                 if(input->isKeyDown(irr::KEY_RIGHT))
1456                                         dx += dtime * keyspeed;*/
1457
1458                                 camera_yaw -= dx*0.2;
1459                                 camera_pitch += dy*0.2;
1460                                 if(camera_pitch < -89.5) camera_pitch = -89.5;
1461                                 if(camera_pitch > 89.5) camera_pitch = 89.5;
1462                         }
1463                         input->setMousePos(displaycenter.X, displaycenter.Y);
1464                 }
1465                 else{
1466                         // Mac OSX gets upset if this is set every frame
1467                         if(device->getCursorControl()->isVisible() == false)
1468                                 device->getCursorControl()->setVisible(true);
1469
1470                         //infostream<<"window inactive"<<std::endl;
1471                         first_loop_after_window_activation = true;
1472                 }
1473
1474                 /*
1475                         Player speed control
1476                 */
1477                 
1478                 if(!noMenuActive() || !device->isWindowActive())
1479                 {
1480                         PlayerControl control(
1481                                 false,
1482                                 false,
1483                                 false,
1484                                 false,
1485                                 false,
1486                                 false,
1487                                 false,
1488                                 camera_pitch,
1489                                 camera_yaw
1490                         );
1491                         client.setPlayerControl(control);
1492                 }
1493                 else
1494                 {
1495                         /*bool a_up,
1496                         bool a_down,
1497                         bool a_left,
1498                         bool a_right,
1499                         bool a_jump,
1500                         bool a_superspeed,
1501                         bool a_sneak,
1502                         float a_pitch,
1503                         float a_yaw*/
1504                         PlayerControl control(
1505                                 input->isKeyDown(getKeySetting("keymap_forward")),
1506                                 input->isKeyDown(getKeySetting("keymap_backward")),
1507                                 input->isKeyDown(getKeySetting("keymap_left")),
1508                                 input->isKeyDown(getKeySetting("keymap_right")),
1509                                 input->isKeyDown(getKeySetting("keymap_jump")),
1510                                 input->isKeyDown(getKeySetting("keymap_special1")),
1511                                 input->isKeyDown(getKeySetting("keymap_sneak")),
1512                                 camera_pitch,
1513                                 camera_yaw
1514                         );
1515                         client.setPlayerControl(control);
1516                 }
1517                 
1518                 /*
1519                         Run server
1520                 */
1521
1522                 if(server != NULL)
1523                 {
1524                         //TimeTaker timer("server->step(dtime)");
1525                         server->step(dtime);
1526                 }
1527
1528                 /*
1529                         Process environment
1530                 */
1531                 
1532                 {
1533                         //TimeTaker timer("client.step(dtime)");
1534                         client.step(dtime);
1535                         //client.step(dtime_avg1);
1536                 }
1537
1538                 {
1539                         // Read client events
1540                         for(;;)
1541                         {
1542                                 ClientEvent event = client.getClientEvent();
1543                                 if(event.type == CE_NONE)
1544                                 {
1545                                         break;
1546                                 }
1547                                 else if(event.type == CE_PLAYER_DAMAGE)
1548                                 {
1549                                         //u16 damage = event.player_damage.amount;
1550                                         //infostream<<"Player damage: "<<damage<<std::endl;
1551                                         damage_flash_timer = 0.05;
1552                                         if(event.player_damage.amount >= 2){
1553                                                 damage_flash_timer += 0.05 * event.player_damage.amount;
1554                                         }
1555                                 }
1556                                 else if(event.type == CE_PLAYER_FORCE_MOVE)
1557                                 {
1558                                         camera_yaw = event.player_force_move.yaw;
1559                                         camera_pitch = event.player_force_move.pitch;
1560                                 }
1561                                 else if(event.type == CE_DEATHSCREEN)
1562                                 {
1563                                         if(respawn_menu_active)
1564                                                 continue;
1565
1566                                         /*bool set_camera_point_target =
1567                                                         event.deathscreen.set_camera_point_target;
1568                                         v3f camera_point_target;
1569                                         camera_point_target.X = event.deathscreen.camera_point_target_x;
1570                                         camera_point_target.Y = event.deathscreen.camera_point_target_y;
1571                                         camera_point_target.Z = event.deathscreen.camera_point_target_z;*/
1572                                         MainRespawnInitiator *respawner =
1573                                                         new MainRespawnInitiator(
1574                                                                         &respawn_menu_active, &client);
1575                                         GUIDeathScreen *menu =
1576                                                         new GUIDeathScreen(guienv, guiroot, -1, 
1577                                                                 &g_menumgr, respawner);
1578                                         menu->drop();
1579                                         
1580                                         /* Handle visualization */
1581
1582                                         damage_flash_timer = 0;
1583
1584                                         /*LocalPlayer* player = client.getLocalPlayer();
1585                                         player->setPosition(player->getPosition() + v3f(0,-BS,0));
1586                                         camera.update(player, busytime, screensize);*/
1587                                 }
1588                         }
1589                 }
1590                 
1591                 //TimeTaker //timer2("//timer2");
1592
1593                 LocalPlayer* player = client.getLocalPlayer();
1594                 camera.update(player, busytime, screensize);
1595                 camera.step(dtime);
1596
1597                 v3f player_position = player->getPosition();
1598                 v3f camera_position = camera.getPosition();
1599                 v3f camera_direction = camera.getDirection();
1600                 f32 camera_fov = camera.getFovMax();
1601                 
1602                 if(!disable_camera_update){
1603                         client.updateCamera(camera_position,
1604                                 camera_direction, camera_fov);
1605                 }
1606
1607                 //timer2.stop();
1608                 //TimeTaker //timer3("//timer3");
1609
1610                 /*
1611                         Calculate what block is the crosshair pointing to
1612                 */
1613                 
1614                 //u32 t1 = device->getTimer()->getRealTime();
1615                 
1616                 //f32 d = 4; // max. distance
1617                 f32 d = 4; // max. distance
1618                 core::line3d<f32> shootline(camera_position,
1619                                 camera_position + camera_direction * BS * (d+1));
1620
1621                 ClientActiveObject *selected_active_object
1622                                 = client.getSelectedActiveObject
1623                                         (d*BS, camera_position, shootline);
1624                 
1625                 bool left_punch = false;
1626                 bool left_punch_muted = false;
1627
1628                 if(selected_active_object != NULL)
1629                 {
1630                         /* Clear possible cracking animation */
1631                         if(nodepos_old != v3s16(-32768,-32768,-32768))
1632                         {
1633                                 client.clearTempMod(nodepos_old);
1634                                 dig_time = 0.0;
1635                                 nodepos_old = v3s16(-32768,-32768,-32768);
1636                         }
1637
1638                         //infostream<<"Client returned selected_active_object != NULL"<<std::endl;
1639                         
1640                         core::aabbox3d<f32> *selection_box
1641                                         = selected_active_object->getSelectionBox();
1642                         // Box should exist because object was returned in the
1643                         // first place
1644                         assert(selection_box);
1645
1646                         v3f pos = selected_active_object->getPosition();
1647
1648                         core::aabbox3d<f32> box_on_map(
1649                                         selection_box->MinEdge + pos,
1650                                         selection_box->MaxEdge + pos
1651                         );
1652                         
1653                         if(selected_active_object->doShowSelectionBox())
1654                                 hilightboxes.push_back(box_on_map);
1655
1656                         //infotext = narrow_to_wide("A ClientActiveObject");
1657                         infotext = narrow_to_wide(selected_active_object->infoText());
1658
1659                         //if(input->getLeftClicked())
1660                         if(input->getLeftState())
1661                         {
1662                                 bool do_punch = false;
1663                                 bool do_punch_damage = false;
1664                                 if(object_hit_delay_timer <= 0.0){
1665                                         do_punch = true;
1666                                         do_punch_damage = true;
1667                                         object_hit_delay_timer = object_hit_delay;
1668                                 }
1669                                 if(input->getLeftClicked()){
1670                                         do_punch = true;
1671                                 }
1672                                 if(do_punch){
1673                                         infostream<<"Left-clicked object"<<std::endl;
1674                                         left_punch = true;
1675                                 }
1676                                 if(do_punch_damage){
1677                                         client.clickActiveObject(0,
1678                                                         selected_active_object->getId(), g_selected_item);
1679                                 }
1680                         }
1681                         else if(input->getRightClicked())
1682                         {
1683                                 infostream<<"Right-clicked object"<<std::endl;
1684                                 client.clickActiveObject(1,
1685                                                 selected_active_object->getId(), g_selected_item);
1686                         }
1687                 }
1688                 else // selected_object == NULL
1689                 {
1690
1691                 /*
1692                         Find out which node we are pointing at
1693                 */
1694                 
1695                 bool nodefound = false;
1696                 v3s16 nodepos;
1697                 v3s16 neighbourpos;
1698                 core::aabbox3d<f32> nodehilightbox;
1699
1700                 getPointedNode(&client, player_position,
1701                                 camera_direction, camera_position,
1702                                 nodefound, shootline,
1703                                 nodepos, neighbourpos,
1704                                 nodehilightbox, d);
1705         
1706                 if(!nodefound){
1707                         if(nodepos_old != v3s16(-32768,-32768,-32768))
1708                         {
1709                                 client.clearTempMod(nodepos_old);
1710                                 dig_time = 0.0;
1711                                 nodepos_old = v3s16(-32768,-32768,-32768);
1712                         }
1713                 } else {
1714                         /*
1715                                 Visualize selection
1716                         */
1717
1718                         hilightboxes.push_back(nodehilightbox);
1719
1720                         /*
1721                                 Check information text of node
1722                         */
1723
1724                         NodeMetadata *meta = client.getNodeMetadata(nodepos);
1725                         if(meta)
1726                         {
1727                                 infotext = narrow_to_wide(meta->infoText());
1728                         }
1729                         
1730                         //MapNode node = client.getNode(nodepos);
1731
1732                         /*
1733                                 Handle digging
1734                         */
1735                         
1736                         if(input->getLeftReleased())
1737                         {
1738                                 client.clearTempMod(nodepos);
1739                                 dig_time = 0.0;
1740                         }
1741                         
1742                         if(nodig_delay_counter > 0.0)
1743                         {
1744                                 nodig_delay_counter -= dtime;
1745                         }
1746                         else
1747                         {
1748                                 if(nodepos != nodepos_old)
1749                                 {
1750                                         infostream<<"Pointing at ("<<nodepos.X<<","
1751                                                         <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
1752
1753                                         if(nodepos_old != v3s16(-32768,-32768,-32768))
1754                                         {
1755                                                 client.clearTempMod(nodepos_old);
1756                                                 dig_time = 0.0;
1757                                                 nodepos_old = v3s16(-32768,-32768,-32768);
1758                                         }
1759                                 }
1760
1761                                 if(input->getLeftClicked() ||
1762                                                 (input->getLeftState() && nodepos != nodepos_old))
1763                                 {
1764                                         infostream<<"Started digging"<<std::endl;
1765                                         client.groundAction(0, nodepos, neighbourpos, g_selected_item);
1766                                 }
1767                                 if(input->getLeftClicked())
1768                                 {
1769                                         client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
1770                                 }
1771                                 if(input->getLeftState())
1772                                 {
1773                                         MapNode n = client.getNode(nodepos);
1774                                 
1775                                         // Get tool name. Default is "" = bare hands
1776                                         std::string toolname = "";
1777                                         InventoryList *mlist = local_inventory.getList("main");
1778                                         if(mlist != NULL)
1779                                         {
1780                                                 InventoryItem *item = mlist->getItem(g_selected_item);
1781                                                 if(item && (std::string)item->getName() == "ToolItem")
1782                                                 {
1783                                                         ToolItem *titem = (ToolItem*)item;
1784                                                         toolname = titem->getToolName();
1785                                                 }
1786                                         }
1787
1788                                         // Get digging properties for material and tool
1789                                         content_t material = n.getContent();
1790                                         DiggingProperties prop =
1791                                                         getDiggingProperties(material, toolname);
1792                                         
1793                                         float dig_time_complete = 0.0;
1794
1795                                         if(prop.diggable == false)
1796                                         {
1797                                                 /*infostream<<"Material "<<(int)material
1798                                                                 <<" not diggable with \""
1799                                                                 <<toolname<<"\""<<std::endl;*/
1800                                                 // I guess nobody will wait for this long
1801                                                 dig_time_complete = 10000000.0;
1802                                         }
1803                                         else
1804                                         {
1805                                                 dig_time_complete = prop.time;
1806                                         }
1807                                         
1808                                         if(dig_time_complete >= 0.001)
1809                                         {
1810                                                 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
1811                                                                 * dig_time/dig_time_complete);
1812                                         }
1813                                         // This is for torches
1814                                         else
1815                                         {
1816                                                 dig_index = CRACK_ANIMATION_LENGTH;
1817                                         }
1818
1819                                         if(dig_index < CRACK_ANIMATION_LENGTH)
1820                                         {
1821                                                 //TimeTaker timer("client.setTempMod");
1822                                                 //infostream<<"dig_index="<<dig_index<<std::endl;
1823                                                 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
1824                                         }
1825                                         else
1826                                         {
1827                                                 infostream<<"Digging completed"<<std::endl;
1828                                                 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
1829                                                 client.clearTempMod(nodepos);
1830                                                 client.removeNode(nodepos);
1831
1832                                                 dig_time = 0;
1833
1834                                                 nodig_delay_counter = dig_time_complete
1835                                                                 / (float)CRACK_ANIMATION_LENGTH;
1836
1837                                                 // We don't want a corresponding delay to
1838                                                 // very time consuming nodes
1839                                                 if(nodig_delay_counter > 0.5)
1840                                                 {
1841                                                         nodig_delay_counter = 0.5;
1842                                                 }
1843                                                 // We want a slight delay to very little
1844                                                 // time consuming nodes
1845                                                 float mindelay = 0.15;
1846                                                 if(nodig_delay_counter < mindelay)
1847                                                 {
1848                                                         nodig_delay_counter = mindelay;
1849                                                 }
1850                                         }
1851
1852                                         dig_time += dtime;
1853
1854                                         camera.setDigging(0);  // left click animation
1855                                 }
1856                         }
1857                         
1858                         
1859                         if(input->getRightClicked())
1860                         {
1861                                 infostream<<"Ground right-clicked"<<std::endl;
1862                                 
1863                                 // If metadata provides an inventory view, activate it
1864                                 if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
1865                                 {
1866                                         infostream<<"Launching custom inventory view"<<std::endl;
1867                                         /*
1868                                                 Construct the unique identification string of the node
1869                                         */
1870                                         std::string current_name;
1871                                         current_name += "nodemeta:";
1872                                         current_name += itos(nodepos.X);
1873                                         current_name += ",";
1874                                         current_name += itos(nodepos.Y);
1875                                         current_name += ",";
1876                                         current_name += itos(nodepos.Z);
1877                                         
1878                                         /*
1879                                                 Create menu
1880                                         */
1881
1882                                         core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1883                                         v2s16 invsize =
1884                                                 GUIInventoryMenu::makeDrawSpecArrayFromString(
1885                                                         draw_spec,
1886                                                         meta->getInventoryDrawSpecString(),
1887                                                         current_name);
1888
1889                                         GUIInventoryMenu *menu =
1890                                                 new GUIInventoryMenu(guienv, guiroot, -1,
1891                                                         &g_menumgr, invsize,
1892                                                         client.getInventoryContext(),
1893                                                         &client);
1894                                         menu->setDrawSpec(draw_spec);
1895                                         menu->drop();
1896                                 }
1897                                 else if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
1898                                 {
1899                                         infostream<<"Sign node right-clicked"<<std::endl;
1900                                         
1901                                         SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
1902                                         
1903                                         // Get a new text for it
1904
1905                                         TextDest *dest = new TextDestSignNode(nodepos, &client);
1906
1907                                         std::wstring wtext =
1908                                                         narrow_to_wide(signmeta->getText());
1909
1910                                         (new GUITextInputMenu(guienv, guiroot, -1,
1911                                                         &g_menumgr, dest,
1912                                                         wtext))->drop();
1913                                 }
1914                                 else
1915                                 {
1916                                         client.groundAction(1, nodepos, neighbourpos, g_selected_item);
1917                                         camera.setDigging(1);  // right click animation
1918                                 }
1919                         }
1920                         
1921                         nodepos_old = nodepos;
1922                 }
1923
1924                 } // selected_object == NULL
1925                 
1926                 if(left_punch || (input->getLeftClicked() && !left_punch_muted))
1927                 {
1928                         camera.setDigging(0); // left click animation
1929                 }
1930
1931                 input->resetLeftClicked();
1932                 input->resetRightClicked();
1933                 
1934                 if(input->getLeftReleased())
1935                 {
1936                         infostream<<"Left button released (stopped digging)"
1937                                         <<std::endl;
1938                         client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
1939                 }
1940                 if(input->getRightReleased())
1941                 {
1942                         //inostream<<DTIME<<"Right released"<<std::endl;
1943                         // Nothing here
1944                 }
1945                 
1946                 input->resetLeftReleased();
1947                 input->resetRightReleased();
1948                 
1949                 /*
1950                         Calculate stuff for drawing
1951                 */
1952
1953                 u32 daynight_ratio = client.getDayNightRatio();
1954                 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
1955                 video::SColor bgcolor = video::SColor(
1956                                 255,
1957                                 bgcolor_bright.getRed() * l / 255,
1958                                 bgcolor_bright.getGreen() * l / 255,
1959                                 bgcolor_bright.getBlue() * l / 255);
1960                                 /*skycolor.getRed() * l / 255,
1961                                 skycolor.getGreen() * l / 255,
1962                                 skycolor.getBlue() * l / 255);*/
1963
1964                 float brightness = (float)l/255.0;
1965
1966                 /*
1967                         Update skybox
1968                 */
1969                 if(fabs(brightness - old_brightness) > 0.01)
1970                         update_skybox(driver, smgr, skybox, brightness);
1971
1972                 /*
1973                         Update clouds
1974                 */
1975                 if(clouds)
1976                 {
1977                         clouds->step(dtime);
1978                         clouds->update(v2f(player_position.X, player_position.Z),
1979                                         0.05+brightness*0.95);
1980                 }
1981                 
1982                 /*
1983                         Update farmesh
1984                 */
1985                 if(farmesh)
1986                 {
1987                         farmesh_range = draw_control.wanted_range * 10;
1988                         if(draw_control.range_all && farmesh_range < 500)
1989                                 farmesh_range = 500;
1990                         if(farmesh_range > 1000)
1991                                 farmesh_range = 1000;
1992
1993                         farmesh->step(dtime);
1994                         farmesh->update(v2f(player_position.X, player_position.Z),
1995                                         0.05+brightness*0.95, farmesh_range);
1996                 }
1997                 
1998                 // Store brightness value
1999                 old_brightness = brightness;
2000
2001                 /*
2002                         Fog
2003                 */
2004                 
2005                 if(g_settings->getBool("enable_fog") == true && !force_fog_off)
2006                 {
2007                         f32 range;
2008                         if(farmesh)
2009                         {
2010                                 range = BS*farmesh_range;
2011                         }
2012                         else
2013                         {
2014                                 range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5;
2015                                 range *= 0.9;
2016                                 if(draw_control.range_all)
2017                                         range = 100000*BS;
2018                                 /*if(range < 50*BS)
2019                                         range = range * 0.5 + 25*BS;*/
2020                         }
2021
2022                         driver->setFog(
2023                                 bgcolor,
2024                                 video::EFT_FOG_LINEAR,
2025                                 range*0.4,
2026                                 range*1.0,
2027                                 0.01,
2028                                 false, // pixel fog
2029                                 false // range fog
2030                         );
2031                 }
2032                 else
2033                 {
2034                         driver->setFog(
2035                                 bgcolor,
2036                                 video::EFT_FOG_LINEAR,
2037                                 100000*BS,
2038                                 110000*BS,
2039                                 0.01,
2040                                 false, // pixel fog
2041                                 false // range fog
2042                         );
2043                 }
2044
2045                 /*
2046                         Update gui stuff (0ms)
2047                 */
2048
2049                 //TimeTaker guiupdatetimer("Gui updating");
2050                 
2051                 {
2052                         static float drawtime_avg = 0;
2053                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
2054                         static float beginscenetime_avg = 0;
2055                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
2056                         static float scenetime_avg = 0;
2057                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
2058                         static float endscenetime_avg = 0;
2059                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
2060                         
2061                         char temptext[300];
2062                         snprintf(temptext, 300, "Minetest-c55 %s ("
2063                                         "R: range_all=%i"
2064                                         ")"
2065                                         " drawtime=%.0f, beginscenetime=%.0f"
2066                                         ", scenetime=%.0f, endscenetime=%.0f",
2067                                         VERSION_STRING,
2068                                         draw_control.range_all,
2069                                         drawtime_avg,
2070                                         beginscenetime_avg,
2071                                         scenetime_avg,
2072                                         endscenetime_avg
2073                                         );
2074                         
2075                         guitext->setText(narrow_to_wide(temptext).c_str());
2076                 }
2077                 
2078                 {
2079                         char temptext[300];
2080                         snprintf(temptext, 300,
2081                                         "(% .1f, % .1f, % .1f)"
2082                                         " (% .3f < btime_jitter < % .3f"
2083                                         ", dtime_jitter = % .1f %%"
2084                                         ", v_range = %.1f, RTT = %.3f)",
2085                                         player_position.X/BS,
2086                                         player_position.Y/BS,
2087                                         player_position.Z/BS,
2088                                         busytime_jitter1_min_sample,
2089                                         busytime_jitter1_max_sample,
2090                                         dtime_jitter1_max_fraction * 100.0,
2091                                         draw_control.wanted_range,
2092                                         client.getRTT()
2093                                         );
2094
2095                         guitext2->setText(narrow_to_wide(temptext).c_str());
2096                 }
2097                 
2098                 {
2099                         guitext_info->setText(infotext.c_str());
2100                 }
2101                 
2102                 /*
2103                         Get chat messages from client
2104                 */
2105                 {
2106                         // Get new messages
2107                         std::wstring message;
2108                         while(client.getChatMessage(message))
2109                         {
2110                                 chat_lines.push_back(ChatLine(message));
2111                                 /*if(chat_lines.size() > 6)
2112                                 {
2113                                         core::list<ChatLine>::Iterator
2114                                                         i = chat_lines.begin();
2115                                         chat_lines.erase(i);
2116                                 }*/
2117                         }
2118                         // Append them to form the whole static text and throw
2119                         // it to the gui element
2120                         std::wstring whole;
2121                         // This will correspond to the line number counted from
2122                         // top to bottom, from size-1 to 0
2123                         s16 line_number = chat_lines.size();
2124                         // Count of messages to be removed from the top
2125                         u16 to_be_removed_count = 0;
2126                         for(core::list<ChatLine>::Iterator
2127                                         i = chat_lines.begin();
2128                                         i != chat_lines.end(); i++)
2129                         {
2130                                 // After this, line number is valid for this loop
2131                                 line_number--;
2132                                 // Increment age
2133                                 (*i).age += dtime;
2134                                 /*
2135                                         This results in a maximum age of 60*6 to the
2136                                         lowermost line and a maximum of 6 lines
2137                                 */
2138                                 float allowed_age = (6-line_number) * 60.0;
2139
2140                                 if((*i).age > allowed_age)
2141                                 {
2142                                         to_be_removed_count++;
2143                                         continue;
2144                                 }
2145                                 whole += (*i).text + L'\n';
2146                         }
2147                         for(u16 i=0; i<to_be_removed_count; i++)
2148                         {
2149                                 core::list<ChatLine>::Iterator
2150                                                 it = chat_lines.begin();
2151                                 chat_lines.erase(it);
2152                         }
2153                         guitext_chat->setText(whole.c_str());
2154
2155                         // Update gui element size and position
2156
2157                         /*core::rect<s32> rect(
2158                                         10,
2159                                         screensize.Y - guitext_chat_pad_bottom
2160                                                         - text_height*chat_lines.size(),
2161                                         screensize.X - 10,
2162                                         screensize.Y - guitext_chat_pad_bottom
2163                         );*/
2164                         core::rect<s32> rect(
2165                                         10,
2166                                         50,
2167                                         screensize.X - 10,
2168                                         50 + guitext_chat->getTextHeight()
2169                         );
2170
2171                         guitext_chat->setRelativePosition(rect);
2172
2173                         // Don't show chat if empty or profiler is enabled
2174                         if(chat_lines.size() == 0 || show_profiler)
2175                                 guitext_chat->setVisible(false);
2176                         else
2177                                 guitext_chat->setVisible(true);
2178                 }
2179
2180                 /*
2181                         Inventory
2182                 */
2183                 
2184                 static u16 old_selected_item = 65535;
2185                 if(client.getLocalInventoryUpdated()
2186                                 || g_selected_item != old_selected_item)
2187                 {
2188                         client.selectPlayerItem(g_selected_item);
2189                         old_selected_item = g_selected_item;
2190                         //infostream<<"Updating local inventory"<<std::endl;
2191                         client.getLocalInventory(local_inventory);
2192
2193                         // Update wielded tool
2194                         InventoryList *mlist = local_inventory.getList("main");
2195                         InventoryItem *item = NULL;
2196                         if(mlist != NULL)
2197                                 item = mlist->getItem(g_selected_item);
2198                         camera.wield(item);
2199                 }
2200                 
2201                 /*
2202                         Send actions returned by the inventory menu
2203                 */
2204                 while(inventory_action_queue.size() != 0)
2205                 {
2206                         InventoryAction *a = inventory_action_queue.pop_front();
2207
2208                         client.sendInventoryAction(a);
2209                         // Eat it
2210                         delete a;
2211                 }
2212
2213                 /*
2214                         Drawing begins
2215                 */
2216
2217                 TimeTaker drawtimer("Drawing");
2218
2219                 
2220                 {
2221                         TimeTaker timer("beginScene");
2222                         driver->beginScene(true, true, bgcolor);
2223                         //driver->beginScene(false, true, bgcolor);
2224                         beginscenetime = timer.stop(true);
2225                 }
2226                 
2227                 //timer3.stop();
2228                 
2229                 //infostream<<"smgr->drawAll()"<<std::endl;
2230                 
2231                 {
2232                         TimeTaker timer("smgr");
2233                         smgr->drawAll();
2234                         scenetime = timer.stop(true);
2235                 }
2236                 
2237                 {
2238                 //TimeTaker timer9("auxiliary drawings");
2239                 // 0ms
2240                 
2241                 //timer9.stop();
2242                 //TimeTaker //timer10("//timer10");
2243                 
2244                 video::SMaterial m;
2245                 //m.Thickness = 10;
2246                 m.Thickness = 3;
2247                 m.Lighting = false;
2248                 driver->setMaterial(m);
2249
2250                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
2251
2252                 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
2253                                 i != hilightboxes.end(); i++)
2254                 {
2255                         /*infostream<<"hilightbox min="
2256                                         <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
2257                                         <<" max="
2258                                         <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
2259                                         <<std::endl;*/
2260                         driver->draw3DBox(*i, video::SColor(255,0,0,0));
2261                 }
2262
2263                 /*
2264                         Wielded tool
2265                 */
2266                 {
2267                         // Warning: This clears the Z buffer.
2268                         camera.drawWieldedTool();
2269                 }
2270
2271                 /*
2272                         Post effects
2273                 */
2274                 {
2275                         client.renderPostFx();
2276                 }
2277
2278                 /*
2279                         Frametime log
2280                 */
2281                 if(g_settings->getBool("frametime_graph") == true)
2282                 {
2283                         s32 x = 10;
2284                         for(core::list<float>::Iterator
2285                                         i = frametime_log.begin();
2286                                         i != frametime_log.end();
2287                                         i++)
2288                         {
2289                                 driver->draw2DLine(v2s32(x,50),
2290                                                 v2s32(x,50+(*i)*1000),
2291                                                 video::SColor(255,255,255,255));
2292                                 x++;
2293                         }
2294                 }
2295
2296                 /*
2297                         Draw crosshair
2298                 */
2299                 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
2300                                 displaycenter + core::vector2d<s32>(10,0),
2301                                 video::SColor(255,255,255,255));
2302                 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
2303                                 displaycenter + core::vector2d<s32>(0,10),
2304                                 video::SColor(255,255,255,255));
2305
2306                 } // timer
2307
2308                 //timer10.stop();
2309                 //TimeTaker //timer11("//timer11");
2310
2311                 /*
2312                         Draw gui
2313                 */
2314                 // 0-1ms
2315                 guienv->drawAll();
2316
2317                 /*
2318                         Draw hotbar
2319                 */
2320                 {
2321                         draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
2322                                         hotbar_imagesize, hotbar_itemcount, &local_inventory,
2323                                         client.getHP());
2324                 }
2325
2326                 /*
2327                         Damage flash
2328                 */
2329                 if(damage_flash_timer > 0.0)
2330                 {
2331                         damage_flash_timer -= dtime;
2332                         
2333                         video::SColor color(128,255,0,0);
2334                         driver->draw2DRectangle(color,
2335                                         core::rect<s32>(0,0,screensize.X,screensize.Y),
2336                                         NULL);
2337                 }
2338
2339                 /*
2340                         End scene
2341                 */
2342                 {
2343                         TimeTaker timer("endScene");
2344                         endSceneX(driver);
2345                         endscenetime = timer.stop(true);
2346                 }
2347
2348                 drawtime = drawtimer.stop(true);
2349
2350                 /*
2351                         End of drawing
2352                 */
2353
2354                 static s16 lastFPS = 0;
2355                 //u16 fps = driver->getFPS();
2356                 u16 fps = (1.0/dtime_avg1);
2357
2358                 if (lastFPS != fps)
2359                 {
2360                         core::stringw str = L"Minetest [";
2361                         str += driver->getName();
2362                         str += "] FPS=";
2363                         str += fps;
2364
2365                         device->setWindowCaption(str.c_str());
2366                         lastFPS = fps;
2367                 }
2368         }
2369
2370         /*
2371                 Drop stuff
2372         */
2373         if(clouds)
2374                 clouds->drop();
2375         
2376         /*
2377                 Draw a "shutting down" screen, which will be shown while the map
2378                 generator and other stuff quits
2379         */
2380         {
2381                 /*gui::IGUIStaticText *gui_shuttingdowntext = */
2382                 draw_load_screen(L"Shutting down stuff...", driver, font);
2383                 /*driver->beginScene(true, true, video::SColor(255,0,0,0));
2384                 guienv->drawAll();
2385                 driver->endScene();
2386                 gui_shuttingdowntext->remove();*/
2387         }
2388 }
2389
2390