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