3 Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "common_irrlicht.h"
24 #include "guiPauseMenu.h"
25 #include "guiInventoryMenu.h"
26 #include "guiTextInputMenu.h"
27 #include "guiFurnaceMenu.h"
28 #include "materials.h"
32 Setting this to 1 enables a special camera mode that forces
33 the renderers to think that the camera statically points from
34 the starting place to a static direction.
36 This allows one to move around with the player and see what
37 is actually drawn behind solid things and behind the player.
39 #define FIELD_OF_VIEW_TEST 0
42 MapDrawControl draw_control;
51 ChatLine(const std::wstring &a_text):
64 // Inventory actions from the menu are buffered here before sending
65 Queue<InventoryAction*> inventory_action_queue;
66 // This is a copy of the inventory that the client's environment has
67 Inventory local_inventory;
69 u16 g_selected_item = 0;
75 struct TextDestSign : public TextDest
77 TextDestSign(v3s16 blockpos, s16 id, Client *client)
79 m_blockpos = blockpos;
83 void gotText(std::wstring text)
85 std::string ntext = wide_to_narrow(text);
86 dstream<<"Changing text of a sign object: "
88 m_client->sendSignText(m_blockpos, m_id, ntext);
96 struct TextDestChat : public TextDest
98 TextDestChat(Client *client)
102 void gotText(std::wstring text)
104 // Discard empty line
108 // Parse command (server command starts with "/#")
109 if(text[0] == L'/' && text[1] != L'#')
111 std::wstring reply = L"Local: ";
113 reply += L"Local commands not yet supported. "
114 L"Server prefix is \"/#\".";
116 m_client->addChatMessage(reply);
121 m_client->sendChatMessage(text);
123 m_client->addChatMessage(text);
129 struct TextDestSignNode : public TextDest
131 TextDestSignNode(v3s16 p, Client *client)
136 void gotText(std::wstring text)
138 std::string ntext = wide_to_narrow(text);
139 dstream<<"Changing text of a sign node: "
141 m_client->sendSignNodeText(m_p, ntext);
149 Render distance feedback loop
151 void updateViewingRange(f32 frametime_in, Client *client)
153 if(draw_control.range_all == true)
156 static f32 added_frametime = 0;
157 static s16 added_frames = 0;
159 added_frametime += frametime_in;
162 // Actually this counter kind of sucks because frametime is busytime
163 static f32 counter = 0;
164 counter -= frametime_in;
170 /*dstream<<__FUNCTION_NAME
171 <<": Collected "<<added_frames<<" frames, total of "
172 <<added_frametime<<"s."<<std::endl;*/
174 /*dstream<<"draw_control.blocks_drawn="
175 <<draw_control.blocks_drawn
176 <<", draw_control.blocks_would_have_drawn="
177 <<draw_control.blocks_would_have_drawn
180 float range_min = g_settings.getS16("viewing_range_nodes_min");
181 float range_max = g_settings.getS16("viewing_range_nodes_max");
183 draw_control.wanted_min_range = range_min;
184 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
186 float block_draw_ratio = 1.0;
187 if(draw_control.blocks_would_have_drawn != 0)
189 block_draw_ratio = (float)draw_control.blocks_drawn
190 / (float)draw_control.blocks_would_have_drawn;
193 // Calculate the average frametime in the case that all wanted
194 // blocks had been drawn
195 f32 frametime = added_frametime / added_frames / block_draw_ratio;
197 added_frametime = 0.0;
200 float wanted_fps = g_settings.getFloat("wanted_fps");
201 float wanted_frametime = 1.0 / wanted_fps;
203 f32 wanted_frametime_change = wanted_frametime - frametime;
204 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
206 // If needed frametime change is small, just return
207 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
209 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
213 float range = draw_control.wanted_range;
214 float new_range = range;
216 static s16 range_old = 0;
217 static f32 frametime_old = 0;
219 float d_range = range - range_old;
220 f32 d_frametime = frametime - frametime_old;
221 // A sane default of 30ms per 50 nodes of range
222 static f32 time_per_range = 30. / 50;
225 time_per_range = d_frametime / d_range;
228 // The minimum allowed calculated frametime-range derivative:
229 // Practically this sets the maximum speed of changing the range.
230 // The lower this value, the higher the maximum changing speed.
231 // A low value here results in wobbly range (0.001)
232 // A high value here results in slow changing range (0.0025)
233 // SUGG: This could be dynamically adjusted so that when
234 // the camera is turning, this is lower
235 //float min_time_per_range = 0.0015;
236 float min_time_per_range = 0.0010;
237 //float min_time_per_range = 0.05 / range;
238 if(time_per_range < min_time_per_range)
240 time_per_range = min_time_per_range;
241 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
245 //dstream<<"time_per_range="<<time_per_range<<std::endl;
248 f32 wanted_range_change = wanted_frametime_change / time_per_range;
249 // Dampen the change a bit to kill oscillations
250 //wanted_range_change *= 0.9;
251 //wanted_range_change *= 0.75;
252 wanted_range_change *= 0.5;
253 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
255 // If needed range change is very small, just return
256 if(fabs(wanted_range_change) < 0.001)
258 //dstream<<"ignoring small wanted_range_change"<<std::endl;
262 new_range += wanted_range_change;
263 //dstream<<"new_range="<<new_range/*<<std::endl*/;
265 //float new_range_unclamped = new_range;
266 if(new_range < range_min)
267 new_range = range_min;
268 if(new_range > range_max)
269 new_range = range_max;
271 /*if(new_range != new_range_unclamped)
272 dstream<<", clamped to "<<new_range<<std::endl;
274 dstream<<std::endl;*/
276 draw_control.wanted_range = new_range;
278 range_old = new_range;
279 frametime_old = frametime;
285 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
286 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
287 Inventory *inventory, s32 halfheartcount)
289 InventoryList *mainlist = inventory->getList("main");
292 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
296 s32 padding = imgsize/12;
297 //s32 height = imgsize + padding*2;
298 s32 width = itemcount*(imgsize+padding*2);
300 // Position of upper left corner of bar
301 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
303 // Draw background color
304 /*core::rect<s32> barrect(0,0,width,height);
306 video::SColor bgcolor(255,128,128,128);
307 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
309 core::rect<s32> imgrect(0,0,imgsize,imgsize);
311 for(s32 i=0; i<itemcount; i++)
313 InventoryItem *item = mainlist->getItem(i);
315 core::rect<s32> rect = imgrect + pos
316 + v2s32(padding+i*(imgsize+padding*2), padding);
318 if(g_selected_item == i)
320 driver->draw2DRectangle(video::SColor(255,255,0,0),
321 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
322 rect.LowerRightCorner + v2s32(1,1)*padding),
327 video::SColor bgcolor2(128,0,0,0);
328 driver->draw2DRectangle(bgcolor2, rect, NULL);
333 drawInventoryItem(driver, font, item, rect, NULL);
341 video::ITexture *heart_texture =
342 driver->getTexture(porting::getDataPath("heart.png").c_str());
343 v2s32 p = pos + v2s32(0, -20);
344 for(s32 i=0; i<halfheartcount/2; i++)
346 const video::SColor color(255,255,255,255);
347 const video::SColor colors[] = {color,color,color,color};
348 core::rect<s32> rect(0,0,16,16);
350 driver->draw2DImage(heart_texture, rect,
351 core::rect<s32>(core::position2d<s32>(0,0),
352 core::dimension2di(heart_texture->getOriginalSize())),
356 if(halfheartcount % 2 == 1)
358 const video::SColor color(255,255,255,255);
359 const video::SColor colors[] = {color,color,color,color};
360 core::rect<s32> rect(0,0,16/2,16);
362 core::dimension2di srcd(heart_texture->getOriginalSize());
364 driver->draw2DImage(heart_texture, rect,
365 core::rect<s32>(core::position2d<s32>(0,0), srcd),
373 Find what the player is pointing at
375 void getPointedNode(Client *client, v3f player_position,
376 v3f camera_direction, v3f camera_position,
377 bool &nodefound, core::line3d<f32> shootline,
378 v3s16 &nodepos, v3s16 &neighbourpos,
379 core::aabbox3d<f32> &nodehilightbox,
382 f32 mindistance = BS * 1001;
384 v3s16 pos_i = floatToInt(player_position, BS);
386 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
390 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
391 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
392 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
393 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
394 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
395 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
397 for(s16 y = ystart; y <= yend; y++)
398 for(s16 z = zstart; z <= zend; z++)
399 for(s16 x = xstart; x <= xend; x++)
404 n = client->getNode(v3s16(x,y,z));
405 if(content_pointable(n.d) == false)
408 catch(InvalidPositionException &e)
414 v3f npf = intToFloat(np, BS);
419 v3s16(0,0,1), // back
421 v3s16(1,0,0), // right
422 v3s16(0,0,-1), // front
423 v3s16(0,-1,0), // bottom
424 v3s16(-1,0,0), // left
430 if(n.d == CONTENT_TORCH)
432 v3s16 dir = unpackDir(n.dir);
433 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
434 dir_f *= BS/2 - BS/6 - BS/20;
435 v3f cpf = npf + dir_f;
436 f32 distance = (cpf - camera_position).getLength();
438 core::aabbox3d<f32> box;
441 if(dir == v3s16(0,-1,0))
443 box = core::aabbox3d<f32>(
444 npf - v3f(BS/6, BS/2, BS/6),
445 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
449 else if(dir == v3s16(0,1,0))
451 box = core::aabbox3d<f32>(
452 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
453 npf + v3f(BS/6, BS/2, BS/6)
459 box = core::aabbox3d<f32>(
460 cpf - v3f(BS/6, BS/3, BS/6),
461 cpf + v3f(BS/6, BS/3, BS/6)
465 if(distance < mindistance)
467 if(box.intersectsWithLine(shootline))
472 mindistance = distance;
473 nodehilightbox = box;
477 else if(n.d == CONTENT_SIGN_WALL)
479 v3s16 dir = unpackDir(n.dir);
480 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
481 dir_f *= BS/2 - BS/6 - BS/20;
482 v3f cpf = npf + dir_f;
483 f32 distance = (cpf - camera_position).getLength();
487 v3f(BS*0.42,-BS*0.35,-BS*0.4),
488 v3f(BS*0.49, BS*0.35, BS*0.4),
491 for(s32 i=0; i<2; i++)
493 if(dir == v3s16(1,0,0))
494 vertices[i].rotateXZBy(0);
495 if(dir == v3s16(-1,0,0))
496 vertices[i].rotateXZBy(180);
497 if(dir == v3s16(0,0,1))
498 vertices[i].rotateXZBy(90);
499 if(dir == v3s16(0,0,-1))
500 vertices[i].rotateXZBy(-90);
501 if(dir == v3s16(0,-1,0))
502 vertices[i].rotateXYBy(-90);
503 if(dir == v3s16(0,1,0))
504 vertices[i].rotateXYBy(90);
509 core::aabbox3d<f32> box;
511 box = core::aabbox3d<f32>(vertices[0]);
512 box.addInternalPoint(vertices[1]);
514 if(distance < mindistance)
516 if(box.intersectsWithLine(shootline))
521 mindistance = distance;
522 nodehilightbox = box;
531 for(u16 i=0; i<6; i++)
533 v3f dir_f = v3f(dirs[i].X,
534 dirs[i].Y, dirs[i].Z);
535 v3f centerpoint = npf + dir_f * BS/2;
537 (centerpoint - camera_position).getLength();
539 if(distance < mindistance)
541 core::CMatrix4<f32> m;
542 m.buildRotateFromTo(v3f(0,0,1), dir_f);
544 // This is the back face
546 v3f(BS/2, BS/2, BS/2),
547 v3f(-BS/2, -BS/2, BS/2+d)
550 for(u16 j=0; j<2; j++)
552 m.rotateVect(corners[j]);
556 core::aabbox3d<f32> facebox(corners[0]);
557 facebox.addInternalPoint(corners[1]);
559 if(facebox.intersectsWithLine(shootline))
563 neighbourpos = np + dirs[i];
564 mindistance = distance;
566 //nodehilightbox = facebox;
568 const float d = 0.502;
569 core::aabbox3d<f32> nodebox
570 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
571 v3f nodepos_f = intToFloat(nodepos, BS);
572 nodebox.MinEdge += nodepos_f;
573 nodebox.MaxEdge += nodepos_f;
574 nodehilightbox = nodebox;
576 } // if distance < mindistance
586 IrrlichtDevice *device,
589 std::string playername,
592 std::wstring &error_message
595 video::IVideoDriver* driver = device->getVideoDriver();
596 scene::ISceneManager* smgr = device->getSceneManager();
598 v2u32 screensize(0,0);
599 v2u32 last_screensize(0,0);
600 screensize = driver->getScreenSize();
602 const s32 hotbar_itemcount = 8;
603 const s32 hotbar_imagesize = 36;
606 Draw "Loading" screen
608 const wchar_t *text = L"Loading and connecting...";
609 u32 text_height = font->getDimension(text).Height;
610 core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
611 core::vector2d<s32> textsize(300, text_height);
612 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
614 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
615 text, textrect, false, false);
616 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
618 driver->beginScene(true, true, video::SColor(255,0,0,0));
622 std::cout<<DTIME<<"Creating server and client"<<std::endl;
626 SharedPtr will delete it when it goes out of scope.
628 SharedPtr<Server> server;
630 server = new Server(map_dir);
638 Client client(device, playername.c_str(), draw_control);
640 Address connect_address(0,0,0,0, port);
643 //connect_address.Resolve("localhost");
644 connect_address.setAddress(127,0,0,1);
646 connect_address.Resolve(address.c_str());
648 catch(ResolveError &e)
650 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
652 error_message = L"Couldn't resolve address";
653 gui_loadingtext->remove();
658 Attempt to connect to the server
661 dstream<<DTIME<<"Connecting to server at ";
662 connect_address.print(&dstream);
664 client.connect(connect_address);
666 bool could_connect = false;
669 float time_counter = 0.0;
672 if(client.connectedAndInitialized())
674 could_connect = true;
677 // Wait for 10 seconds
678 if(time_counter >= 10.0)
684 driver->beginScene(true, true, video::SColor(255,0,0,0));
688 // Update client and server
700 catch(con::PeerNotFoundException &e)
703 if(could_connect == false)
705 std::cout<<DTIME<<"Timed out."<<std::endl;
706 error_message = L"Connection timed out.";
707 gui_loadingtext->remove();
714 /*scene::ISceneNode* skybox;
715 skybox = smgr->addSkyBoxSceneNode(
716 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
717 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
718 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
719 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
720 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
721 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
724 Create the camera node
727 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
729 v3f(BS*100, BS*2, BS*100), // Look from
730 v3f(BS*100+1, BS*2, BS*100), // Look to
736 error_message = L"Failed to create the camera node";
740 //video::SColor skycolor = video::SColor(255,90,140,200);
741 //video::SColor skycolor = video::SColor(255,166,202,244);
742 //video::SColor skycolor = video::SColor(255,120,185,244);
743 video::SColor skycolor = video::SColor(255,140,186,250);
745 camera->setFOV(FOV_ANGLE);
747 // Just so big a value that everything rendered is visible
748 camera->setFarValue(100000*BS);
750 f32 camera_yaw = 0; // "right/left"
751 f32 camera_pitch = 0; // "up/down"
757 gui_loadingtext->remove();
763 // First line of debug text
764 gui::IGUIStaticText *guitext = guienv->addStaticText(
766 core::rect<s32>(5, 5, 795, 5+text_height),
768 // Second line of debug text
769 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
771 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
774 // At the middle of the screen
775 // Object infos are shown in this
776 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
778 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
782 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
784 core::rect<s32>(0,0,0,0),
785 false, false); // Disable word wrap as of now
787 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
788 core::list<ChatLine> chat_lines;
790 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
791 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
792 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
793 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
795 // Test the text input system
796 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
798 /*GUIMessageMenu *menu =
799 new GUIMessageMenu(guienv, guiroot, -1,
805 (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
806 &g_menumgr))->drop();
809 /*guitext2->setVisible(true);
810 guitext_info->setVisible(true);
811 guitext_chat->setVisible(true);*/
813 //s32 guitext_chat_pad_bottom = 70;
816 Some statistics are collected in these
819 u32 beginscenetime = 0;
821 u32 endscenetime = 0;
824 //throw con::PeerNotFoundException("lol");
826 core::list<float> frametime_log;
828 float damage_flash_timer = 0;
834 bool first_loop_after_window_activation = true;
836 // Time is in milliseconds
837 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
838 // NOTE: So we have to use getTime() and call run()s between them
839 u32 lasttime = device->getTimer()->getTime();
841 while(device->run() && kill == false)
843 if(g_gamecallback->disconnect_requested)
845 g_gamecallback->disconnect_requested = false;
850 Process TextureSource's queue
852 ((TextureSource*)g_texturesource)->processQueue();
857 last_screensize = screensize;
858 screensize = driver->getScreenSize();
859 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
860 //bool screensize_changed = screensize != last_screensize;
862 // Hilight boxes collected during the loop and displayed
863 core::list< core::aabbox3d<f32> > hilightboxes;
866 std::wstring infotext;
868 // When screen size changes, update positions and sizes of stuff
869 /*if(screensize_changed)
871 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
872 quick_inventory->updatePosition(pos);
875 //TimeTaker //timer1("//timer1");
877 // Time of frame without fps limit
881 // not using getRealTime is necessary for wine
882 u32 time = device->getTimer()->getTime();
884 busytime_u32 = time - lasttime;
887 busytime = busytime_u32 / 1000.0;
890 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
892 // Necessary for device->getTimer()->getTime()
899 updateViewingRange(busytime, &client);
906 float fps_max = g_settings.getFloat("fps_max");
907 u32 frametime_min = 1000./fps_max;
909 if(busytime_u32 < frametime_min)
911 u32 sleeptime = frametime_min - busytime_u32;
912 device->sleep(sleeptime);
916 // Necessary for device->getTimer()->getTime()
920 Time difference calculation
922 f32 dtime; // in seconds
924 u32 time = device->getTimer()->getTime();
926 dtime = (time - lasttime) / 1000.0;
932 Log frametime for visualization
934 frametime_log.push_back(dtime);
935 if(frametime_log.size() > 100)
937 core::list<float>::Iterator i = frametime_log.begin();
938 frametime_log.erase(i);
942 Visualize frametime in terminal
944 /*for(u32 i=0; i<dtime*400; i++)
946 std::cout<<std::endl;*/
949 Time average and jitter calculation
952 static f32 dtime_avg1 = 0.0;
953 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
954 f32 dtime_jitter1 = dtime - dtime_avg1;
956 static f32 dtime_jitter1_max_sample = 0.0;
957 static f32 dtime_jitter1_max_fraction = 0.0;
959 static f32 jitter1_max = 0.0;
960 static f32 counter = 0.0;
961 if(dtime_jitter1 > jitter1_max)
962 jitter1_max = dtime_jitter1;
967 dtime_jitter1_max_sample = jitter1_max;
968 dtime_jitter1_max_fraction
969 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
975 Busytime average and jitter calculation
978 static f32 busytime_avg1 = 0.0;
979 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
980 f32 busytime_jitter1 = busytime - busytime_avg1;
982 static f32 busytime_jitter1_max_sample = 0.0;
983 static f32 busytime_jitter1_min_sample = 0.0;
985 static f32 jitter1_max = 0.0;
986 static f32 jitter1_min = 0.0;
987 static f32 counter = 0.0;
988 if(busytime_jitter1 > jitter1_max)
989 jitter1_max = busytime_jitter1;
990 if(busytime_jitter1 < jitter1_min)
991 jitter1_min = busytime_jitter1;
995 busytime_jitter1_max_sample = jitter1_max;
996 busytime_jitter1_min_sample = jitter1_min;
1003 Debug info for client
1006 static float counter = 0.0;
1011 client.printDebugInfo(std::cout);
1016 Direct handling of user input
1019 // Reset input if window not active or some menu is active
1020 if(device->isWindowActive() == false || noMenuActive() == false)
1025 // Input handler step() (used by the random input generator)
1029 Launch menus according to keys
1031 if(input->wasKeyDown(irr::KEY_KEY_I))
1033 dstream<<DTIME<<"the_game: "
1034 <<"Launching inventory"<<std::endl;
1036 GUIInventoryMenu *menu =
1037 new GUIInventoryMenu(guienv, guiroot, -1,
1038 &g_menumgr, v2s16(8,7),
1039 client.getInventoryContext(),
1042 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1043 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1044 "list", "current_player", "main",
1045 v2s32(0, 3), v2s32(8, 4)));
1046 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1047 "list", "current_player", "craft",
1048 v2s32(3, 0), v2s32(3, 3)));
1049 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1050 "list", "current_player", "craftresult",
1051 v2s32(7, 1), v2s32(1, 1)));
1053 menu->setDrawSpec(draw_spec);
1057 else if(input->wasKeyDown(irr::KEY_ESCAPE))
1059 dstream<<DTIME<<"the_game: "
1060 <<"Launching pause menu"<<std::endl;
1061 // It will delete itself by itself
1062 (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
1063 &g_menumgr))->drop();
1065 else if(input->wasKeyDown(irr::KEY_KEY_T))
1067 TextDest *dest = new TextDestChat(&client);
1069 (new GUITextInputMenu(guienv, guiroot, -1,
1074 // Item selection with mouse wheel
1076 s32 wheel = input->getMouseWheel();
1077 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
1078 hotbar_itemcount-1);
1082 if(g_selected_item < max_item)
1085 g_selected_item = 0;
1089 if(g_selected_item > 0)
1092 g_selected_item = max_item;
1097 for(u16 i=0; i<10; i++)
1099 s32 keycode = irr::KEY_KEY_1 + i;
1101 keycode = irr::KEY_KEY_0;
1102 if(input->wasKeyDown((irr::EKEY_CODE)keycode))
1104 if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
1106 g_selected_item = i;
1108 dstream<<DTIME<<"Selected item: "
1109 <<g_selected_item<<std::endl;
1114 // Viewing range selection
1115 if(input->wasKeyDown(irr::KEY_KEY_R))
1117 if(draw_control.range_all)
1119 draw_control.range_all = false;
1120 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
1124 draw_control.range_all = true;
1125 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
1129 // Print debug stacks
1130 if(input->wasKeyDown(irr::KEY_KEY_P))
1132 dstream<<"-----------------------------------------"
1134 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
1135 dstream<<"-----------------------------------------"
1137 debug_stacks_print();
1141 Player speed control
1154 PlayerControl control(
1155 input->isKeyDown(irr::KEY_KEY_W),
1156 input->isKeyDown(irr::KEY_KEY_S),
1157 input->isKeyDown(irr::KEY_KEY_A),
1158 input->isKeyDown(irr::KEY_KEY_D),
1159 input->isKeyDown(irr::KEY_SPACE),
1160 input->isKeyDown(irr::KEY_KEY_E),
1161 input->isKeyDown(irr::KEY_LSHIFT)
1162 || input->isKeyDown(irr::KEY_RSHIFT),
1166 client.setPlayerControl(control);
1175 //TimeTaker timer("server->step(dtime)");
1176 server->step(dtime);
1184 //TimeTaker timer("client.step(dtime)");
1186 //client.step(dtime_avg1);
1189 // Read client events
1192 ClientEvent event = client.getClientEvent();
1193 if(event.type == CE_NONE)
1197 else if(event.type == CE_PLAYER_DAMAGE)
1199 //u16 damage = event.player_damage.amount;
1200 //dstream<<"Player damage: "<<damage<<std::endl;
1201 damage_flash_timer = 0.05;
1203 else if(event.type == CE_PLAYER_FORCE_MOVE)
1205 camera_yaw = event.player_force_move.yaw;
1206 camera_pitch = event.player_force_move.pitch;
1210 // Get player position
1211 v3f player_position = client.getPlayerPosition();
1213 //TimeTaker //timer2("//timer2");
1216 Mouse and camera control
1219 if((device->isWindowActive() && noMenuActive()) || random_input)
1222 device->getCursorControl()->setVisible(false);
1224 if(first_loop_after_window_activation){
1225 //std::cout<<"window active, first loop"<<std::endl;
1226 first_loop_after_window_activation = false;
1229 s32 dx = input->getMousePos().X - displaycenter.X;
1230 s32 dy = input->getMousePos().Y - displaycenter.Y;
1231 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
1233 const float keyspeed = 500;
1234 if(input->isKeyDown(irr::KEY_UP))
1235 dy -= dtime * keyspeed;
1236 if(input->isKeyDown(irr::KEY_DOWN))
1237 dy += dtime * keyspeed;
1238 if(input->isKeyDown(irr::KEY_LEFT))
1239 dx -= dtime * keyspeed;
1240 if(input->isKeyDown(irr::KEY_RIGHT))
1241 dx += dtime * keyspeed;
1243 camera_yaw -= dx*0.2;
1244 camera_pitch += dy*0.2;
1245 if(camera_pitch < -89.5) camera_pitch = -89.5;
1246 if(camera_pitch > 89.5) camera_pitch = 89.5;
1248 input->setMousePos(displaycenter.X, displaycenter.Y);
1251 device->getCursorControl()->setVisible(true);
1253 //std::cout<<"window inactive"<<std::endl;
1254 first_loop_after_window_activation = true;
1257 camera_yaw = wrapDegrees(camera_yaw);
1258 camera_pitch = wrapDegrees(camera_pitch);
1260 v3f camera_direction = v3f(0,0,1);
1261 camera_direction.rotateYZBy(camera_pitch);
1262 camera_direction.rotateXZBy(camera_yaw);
1264 // This is at the height of the eyes of the current figure
1265 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
1266 // This is more like in minecraft
1267 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
1269 camera->setPosition(camera_position);
1270 // *100.0 helps in large map coordinates
1271 camera->setTarget(camera_position + camera_direction * 100.0);
1273 if(FIELD_OF_VIEW_TEST){
1274 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
1277 //TimeTaker timer("client.updateCamera");
1278 client.updateCamera(camera_position, camera_direction);
1282 //TimeTaker //timer3("//timer3");
1285 Calculate what block is the crosshair pointing to
1288 //u32 t1 = device->getTimer()->getRealTime();
1290 //f32 d = 4; // max. distance
1291 f32 d = 4; // max. distance
1292 core::line3d<f32> shootline(camera_position,
1293 camera_position + camera_direction * BS * (d+1));
1295 MapBlockObject *selected_object = client.getSelectedObject
1296 (d*BS, camera_position, shootline);
1298 ClientActiveObject *selected_active_object
1299 = client.getSelectedActiveObject
1300 (d*BS, camera_position, shootline);
1302 if(selected_object != NULL)
1304 //dstream<<"Client returned selected_object != NULL"<<std::endl;
1306 core::aabbox3d<f32> box_on_map
1307 = selected_object->getSelectionBoxOnMap();
1309 hilightboxes.push_back(box_on_map);
1311 infotext = narrow_to_wide(selected_object->infoText());
1313 if(input->getLeftClicked())
1315 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
1316 client.clickObject(0, selected_object->getBlock()->getPos(),
1317 selected_object->getId(), g_selected_item);
1319 else if(input->getRightClicked())
1321 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
1323 Check if we want to modify the object ourselves
1325 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
1327 dstream<<"Sign object right-clicked"<<std::endl;
1329 if(random_input == false)
1331 // Get a new text for it
1333 TextDest *dest = new TextDestSign(
1334 selected_object->getBlock()->getPos(),
1335 selected_object->getId(),
1338 SignObject *sign_object = (SignObject*)selected_object;
1340 std::wstring wtext =
1341 narrow_to_wide(sign_object->getText());
1343 (new GUITextInputMenu(guienv, guiroot, -1,
1349 Otherwise pass the event to the server as-is
1353 client.clickObject(1, selected_object->getBlock()->getPos(),
1354 selected_object->getId(), g_selected_item);
1358 else if(selected_active_object != NULL)
1360 //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
1362 core::aabbox3d<f32> *selection_box
1363 = selected_active_object->getSelectionBox();
1364 // Box should exist because object was returned in the
1366 assert(selection_box);
1368 v3f pos = selected_active_object->getPosition();
1370 core::aabbox3d<f32> box_on_map(
1371 selection_box->MinEdge + pos,
1372 selection_box->MaxEdge + pos
1375 hilightboxes.push_back(box_on_map);
1377 //infotext = narrow_to_wide("A ClientActiveObject");
1378 infotext = narrow_to_wide(selected_active_object->infoText());
1380 if(input->getLeftClicked())
1382 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
1383 client.clickActiveObject(0,
1384 selected_active_object->getId(), g_selected_item);
1386 else if(input->getRightClicked())
1388 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
1391 else // selected_object == NULL
1395 Find out which node we are pointing at
1398 bool nodefound = false;
1401 core::aabbox3d<f32> nodehilightbox;
1403 getPointedNode(&client, player_position,
1404 camera_direction, camera_position,
1405 nodefound, shootline,
1406 nodepos, neighbourpos,
1409 static float nodig_delay_counter = 0.0;
1413 static v3s16 nodepos_old(-32768,-32768,-32768);
1415 static float dig_time = 0.0;
1416 static u16 dig_index = 0;
1422 hilightboxes.push_back(nodehilightbox);
1425 Check information text of node
1428 NodeMetadata *meta = client.getNodeMetadata(nodepos);
1431 infotext = narrow_to_wide(meta->infoText());
1434 //MapNode node = client.getNode(nodepos);
1440 if(input->getLeftReleased())
1442 client.clearTempMod(nodepos);
1446 if(nodig_delay_counter > 0.0)
1448 nodig_delay_counter -= dtime;
1452 if(nodepos != nodepos_old)
1454 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
1455 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
1457 if(nodepos_old != v3s16(-32768,-32768,-32768))
1459 client.clearTempMod(nodepos_old);
1464 if(input->getLeftClicked() ||
1465 (input->getLeftState() && nodepos != nodepos_old))
1467 dstream<<DTIME<<"Started digging"<<std::endl;
1468 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
1470 if(input->getLeftClicked())
1472 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
1474 if(input->getLeftState())
1476 MapNode n = client.getNode(nodepos);
1478 // Get tool name. Default is "" = bare hands
1479 std::string toolname = "";
1480 InventoryList *mlist = local_inventory.getList("main");
1483 InventoryItem *item = mlist->getItem(g_selected_item);
1484 if(item && (std::string)item->getName() == "ToolItem")
1486 ToolItem *titem = (ToolItem*)item;
1487 toolname = titem->getToolName();
1491 // Get digging properties for material and tool
1493 DiggingProperties prop =
1494 getDiggingProperties(material, toolname);
1496 float dig_time_complete = 0.0;
1498 if(prop.diggable == false)
1500 /*dstream<<"Material "<<(int)material
1501 <<" not diggable with \""
1502 <<toolname<<"\""<<std::endl;*/
1503 // I guess nobody will wait for this long
1504 dig_time_complete = 10000000.0;
1508 dig_time_complete = prop.time;
1511 if(dig_time_complete >= 0.001)
1513 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
1514 * dig_time/dig_time_complete);
1516 // This is for torches
1519 dig_index = CRACK_ANIMATION_LENGTH;
1522 if(dig_index < CRACK_ANIMATION_LENGTH)
1524 //TimeTaker timer("client.setTempMod");
1525 //dstream<<"dig_index="<<dig_index<<std::endl;
1526 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
1530 dstream<<DTIME<<"Digging completed"<<std::endl;
1531 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
1532 client.clearTempMod(nodepos);
1533 client.removeNode(nodepos);
1537 nodig_delay_counter = dig_time_complete
1538 / (float)CRACK_ANIMATION_LENGTH;
1540 // We don't want a corresponding delay to
1541 // very time consuming nodes
1542 if(nodig_delay_counter > 0.5)
1544 nodig_delay_counter = 0.5;
1546 // We want a slight delay to very little
1547 // time consuming nodes
1548 float mindelay = 0.15;
1549 if(nodig_delay_counter < mindelay)
1551 nodig_delay_counter = mindelay;
1559 if(input->getRightClicked())
1561 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
1563 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
1565 dstream<<"Sign node right-clicked"<<std::endl;
1567 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
1569 // Get a new text for it
1571 TextDest *dest = new TextDestSignNode(nodepos, &client);
1573 std::wstring wtext =
1574 narrow_to_wide(signmeta->getText());
1576 (new GUITextInputMenu(guienv, guiroot, -1,
1580 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
1582 dstream<<"Chest node right-clicked"<<std::endl;
1584 //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
1586 std::string chest_inv_id;
1587 chest_inv_id += "nodemeta:";
1588 chest_inv_id += itos(nodepos.X);
1589 chest_inv_id += ",";
1590 chest_inv_id += itos(nodepos.Y);
1591 chest_inv_id += ",";
1592 chest_inv_id += itos(nodepos.Z);
1594 GUIInventoryMenu *menu =
1595 new GUIInventoryMenu(guienv, guiroot, -1,
1596 &g_menumgr, v2s16(8,9),
1597 client.getInventoryContext(),
1600 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1602 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1603 "list", chest_inv_id, "0",
1604 v2s32(0, 0), v2s32(8, 4)));
1605 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1606 "list", "current_player", "main",
1607 v2s32(0, 5), v2s32(8, 4)));
1609 menu->setDrawSpec(draw_spec);
1614 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
1616 dstream<<"Furnace node right-clicked"<<std::endl;
1618 GUIFurnaceMenu *menu =
1619 new GUIFurnaceMenu(guienv, guiroot, -1,
1620 &g_menumgr, nodepos, &client);
1627 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
1631 nodepos_old = nodepos;
1636 } // selected_object == NULL
1638 input->resetLeftClicked();
1639 input->resetRightClicked();
1641 if(input->getLeftReleased())
1643 std::cout<<DTIME<<"Left button released (stopped digging)"
1645 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
1647 if(input->getRightReleased())
1649 //std::cout<<DTIME<<"Right released"<<std::endl;
1653 input->resetLeftReleased();
1654 input->resetRightReleased();
1657 Calculate stuff for drawing
1660 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
1662 u32 daynight_ratio = client.getDayNightRatio();
1663 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
1664 video::SColor bgcolor = video::SColor(
1666 skycolor.getRed() * l / 255,
1667 skycolor.getGreen() * l / 255,
1668 skycolor.getBlue() * l / 255);
1674 if(g_settings.getBool("enable_fog") == true)
1676 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
1677 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
1678 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
1679 if(draw_control.range_all)
1684 video::EFT_FOG_LINEAR,
1696 video::EFT_FOG_LINEAR,
1707 Update gui stuff (0ms)
1710 //TimeTaker guiupdatetimer("Gui updating");
1713 static float drawtime_avg = 0;
1714 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
1715 static float beginscenetime_avg = 0;
1716 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
1717 static float scenetime_avg = 0;
1718 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
1719 static float endscenetime_avg = 0;
1720 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
1723 snprintf(temptext, 300, "Minetest-c55 %s ("
1726 " drawtime=%.0f, beginscenetime=%.0f"
1727 ", scenetime=%.0f, endscenetime=%.0f",
1729 draw_control.range_all,
1736 guitext->setText(narrow_to_wide(temptext).c_str());
1741 snprintf(temptext, 300,
1742 "(% .1f, % .1f, % .1f)"
1743 " (% .3f < btime_jitter < % .3f"
1744 ", dtime_jitter = % .1f %%"
1745 ", v_range = %.1f)",
1746 player_position.X/BS,
1747 player_position.Y/BS,
1748 player_position.Z/BS,
1749 busytime_jitter1_min_sample,
1750 busytime_jitter1_max_sample,
1751 dtime_jitter1_max_fraction * 100.0,
1752 draw_control.wanted_range
1755 guitext2->setText(narrow_to_wide(temptext).c_str());
1759 guitext_info->setText(infotext.c_str());
1763 Get chat messages from client
1767 std::wstring message;
1768 while(client.getChatMessage(message))
1770 chat_lines.push_back(ChatLine(message));
1771 /*if(chat_lines.size() > 6)
1773 core::list<ChatLine>::Iterator
1774 i = chat_lines.begin();
1775 chat_lines.erase(i);
1778 // Append them to form the whole static text and throw
1779 // it to the gui element
1781 // This will correspond to the line number counted from
1782 // top to bottom, from size-1 to 0
1783 s16 line_number = chat_lines.size();
1784 // Count of messages to be removed from the top
1785 u16 to_be_removed_count = 0;
1786 for(core::list<ChatLine>::Iterator
1787 i = chat_lines.begin();
1788 i != chat_lines.end(); i++)
1790 // After this, line number is valid for this loop
1795 This results in a maximum age of 60*6 to the
1796 lowermost line and a maximum of 6 lines
1798 float allowed_age = (6-line_number) * 60.0;
1800 if((*i).age > allowed_age)
1802 to_be_removed_count++;
1805 whole += (*i).text + L'\n';
1807 for(u16 i=0; i<to_be_removed_count; i++)
1809 core::list<ChatLine>::Iterator
1810 it = chat_lines.begin();
1811 chat_lines.erase(it);
1813 guitext_chat->setText(whole.c_str());
1815 // Update gui element size and position
1817 /*core::rect<s32> rect(
1819 screensize.Y - guitext_chat_pad_bottom
1820 - text_height*chat_lines.size(),
1822 screensize.Y - guitext_chat_pad_bottom
1824 core::rect<s32> rect(
1828 50 + text_height*chat_lines.size()
1831 guitext_chat->setRelativePosition(rect);
1833 if(chat_lines.size() == 0)
1834 guitext_chat->setVisible(false);
1836 guitext_chat->setVisible(true);
1843 static u16 old_selected_item = 65535;
1844 if(client.getLocalInventoryUpdated()
1845 || g_selected_item != old_selected_item)
1847 old_selected_item = g_selected_item;
1848 //std::cout<<"Updating local inventory"<<std::endl;
1849 client.getLocalInventory(local_inventory);
1853 Send actions returned by the inventory menu
1855 while(inventory_action_queue.size() != 0)
1857 InventoryAction *a = inventory_action_queue.pop_front();
1859 client.sendInventoryAction(a);
1868 TimeTaker drawtimer("Drawing");
1872 TimeTaker timer("beginScene");
1873 driver->beginScene(true, true, bgcolor);
1874 //driver->beginScene(false, true, bgcolor);
1875 beginscenetime = timer.stop(true);
1880 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
1883 TimeTaker timer("smgr");
1885 scenetime = timer.stop(true);
1889 //TimeTaker timer9("auxiliary drawings");
1893 //TimeTaker //timer10("//timer10");
1899 driver->setMaterial(m);
1901 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
1903 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
1904 i != hilightboxes.end(); i++)
1906 /*std::cout<<"hilightbox min="
1907 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
1909 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
1911 driver->draw3DBox(*i, video::SColor(255,0,0,0));
1917 if(g_settings.getBool("frametime_graph") == true)
1920 for(core::list<float>::Iterator
1921 i = frametime_log.begin();
1922 i != frametime_log.end();
1925 driver->draw2DLine(v2s32(x,50),
1926 v2s32(x,50+(*i)*1000),
1927 video::SColor(255,255,255,255));
1935 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
1936 displaycenter + core::vector2d<s32>(10,0),
1937 video::SColor(255,255,255,255));
1938 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
1939 displaycenter + core::vector2d<s32>(0,10),
1940 video::SColor(255,255,255,255));
1945 //TimeTaker //timer11("//timer11");
1957 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
1958 hotbar_imagesize, hotbar_itemcount, &local_inventory,
1965 if(damage_flash_timer > 0.0)
1967 damage_flash_timer -= dtime;
1969 video::SColor color(128,255,0,0);
1970 driver->draw2DRectangle(color,
1971 core::rect<s32>(0,0,screensize.X,screensize.Y),
1979 TimeTaker timer("endScene");
1981 endscenetime = timer.stop(true);
1984 drawtime = drawtimer.stop(true);
1990 static s16 lastFPS = 0;
1991 //u16 fps = driver->getFPS();
1992 u16 fps = (1.0/dtime_avg1);
1996 core::stringw str = L"Minetest [";
1997 str += driver->getName();
2001 device->setWindowCaption(str.c_str());