3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
\r
5 This program is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU General Public License as published by
\r
7 the Free Software Foundation; either version 2 of the License, or
\r
8 (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 =============================== NOTES ==============================
\r
22 NOTE: Things starting with TODO are sometimes only suggestions.
\r
24 NOTE: VBO cannot be turned on for fast-changing stuff because there
\r
25 is an apparanet memory leak in irrlicht when using it (not sure)
\r
26 - It is not a memory leak but some kind of a buffer.
\r
28 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
29 NOTE: Global locale is now set at initialization
\r
31 SUGG: Fix address to be ipv6 compatible
\r
33 NOTE: When a new sector is generated, it may change the ground level
\r
34 of it's and it's neighbors border that two blocks that are
\r
35 above and below each other and that are generated before and
\r
36 after the sector heightmap generation (order doesn't matter),
\r
37 can have a small gap between each other at the border.
\r
38 SUGG: Use same technique for sector heightmaps as what we're
\r
39 using for UnlimitedHeightmap? (getting all neighbors
\r
42 SUGG: Transfer more blocks in a single packet
\r
43 SUGG: A blockdata combiner class, to which blocks are added and at
\r
44 destruction it sends all the stuff in as few packets as possible.
\r
46 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
47 SUGG: Fetch stuff mainly from the viewing direction
\r
49 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
50 - This enables saving many packets and making a faster connection
\r
51 - This also enables server to check if client has received the
\r
52 most recent block sent, for example.
\r
53 SUGG: Add a sane bandwidth throttling system to Connection
\r
55 SUGG: More fine-grained control of client's dumping of blocks from
\r
57 - ...What does this mean in the first place?
\r
59 SUGG: A map editing mode (similar to dedicated server mode)
\r
61 SUGG: Add a time value to the param of footstepped grass and check it
\r
62 against a global timer when a block is accessed, to make old
\r
65 SUGG: Make a copy of close-range environment on client for showing
\r
66 on screen, with minimal mutexes to slow down the main loop
\r
68 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
69 it by sending more stuff in a single packet.
\r
70 - Add a packet queue to RemoteClient, from which packets will be
\r
71 combined with object data packets
\r
72 - This is not exactly trivial: the object data packets are
\r
73 sometimes very big by themselves
\r
75 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
76 - This will allow saving ages of rats on disk but not sending
\r
79 SUGG: MovingObject::move and Player::move are basically the same.
\r
81 - NOTE: Player::move is more up-to-date.
\r
83 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
84 - This is not doable because it is currently hand-made and not
\r
85 based on some mathematical function.
\r
86 - Note: This has been changing lately
\r
88 SUGG: A version number to blocks, which increments when the block is
\r
89 modified (node add/remove, water update, lighting update)
\r
90 - This can then be used to make sure the most recent version of
\r
91 a block has been sent to client
\r
93 SUGG: Make the amount of blocks sending to client and the total
\r
94 amount of blocks dynamically limited. Transferring blocks is the
\r
95 main network eater of this system, so it is the one that has
\r
96 to be throttled so that RTTs stay low.
\r
98 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
99 different directions and then only those drawn that need to be
\r
101 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
107 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
109 - The player could go faster by a crafting a boat, or riding an animal
\r
111 - Random NPC traders. what else?
\r
116 Build system / running:
\r
117 -----------------------
\r
119 FIXME: Some network errors on Windows that cause local game to not work
\r
120 - See siggjen's emails.
\r
121 - Is this the famous "windows 7 problem"?
\r
122 - Apparently there might be other errors too
\r
124 Networking and serialization:
\r
125 -----------------------------
\r
127 TODO: Get rid of GotSplitPacketException
\r
132 TODO: Add gui option to remove map
\r
134 TODO: Configuration menu, at least for keys
\r
139 TODO: Optimize day/night mesh updating somehow
\r
140 - create copies of all textures for all lighting values and only
\r
141 change texture for material?
\r
142 - Umm... the collecting of the faces is the slow part
\r
143 -> what about just changing the color values of the existing
\r
144 meshbuffers? It should go quite fast.
\r
145 - This is not easy; There'd need to be a buffer somewhere
\r
146 that would contain the night and day lighting values.
\r
147 - Actually if FastFaces would be stored, they could
\r
150 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
152 - That is >500 vertices
\r
153 - This is not easy; all the MapBlocks close to the player would
\r
154 still need to be drawn separately and combining the blocks
\r
155 would have to happen in a background thread
\r
157 TODO: Make fetching sector's blocks more efficient when rendering
\r
158 sectors that have very large amounts of blocks (on client)
\r
159 - Is this necessary at all?
\r
161 TODO: Flowing water animation
\r
169 TODO: Untie client network operations from framerate
\r
170 - Needs some input queues or something
\r
172 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
174 SUGG: Don't update all meshes always on single node changes, but
\r
175 check which ones should be updated
\r
176 - implement Map::updateNodeMeshes()
\r
178 TODO: Remove IrrlichtWrapper
\r
183 TODO: When player dies, throw items on map
\r
185 TODO: Make an option to the server to disable building and digging near
\r
186 the starting position
\r
188 TODO: Copy the text of the last picked sign to inventory in creative
\r
191 TODO: Check what goes wrong with caching map to disk (Kray)
\r
194 TODO: When server sees that client is removing an inexistent block in
\r
195 an existent position, resend the MapBlock.
\r
197 FIXME: Server went into some infinite PeerNotFoundException loop
\r
202 TODO: There has to be some better way to handle static objects than to
\r
203 send them all the time. This affects signs and item objects.
\r
204 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
205 need an additional metadata field for the texts
\r
206 - This is also needed for item container chests
\r
208 Block object server side:
\r
209 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
210 - For all blocks in the buffer, objects are stepped(). This
\r
211 means they are active.
\r
212 - TODO: A global active buffer is needed for the server
\r
213 - TODO: A timestamp to blocks
\r
214 - TODO: All blocks going in and out of the buffer are recorded.
\r
215 - TODO: For outgoing blocks, timestamp is written.
\r
216 - TODO: For incoming blocks, time difference is calculated and
\r
217 objects are stepped according to it.
\r
219 - When an active object goes far from a player, either delete
\r
220 it or store it statically.
\r
221 - When a statically stored active object comes near a player,
\r
222 recreate the active object
\r
227 TODO: Mineral and ground material properties
\r
228 - This way mineral ground toughness can be calculated with just
\r
229 some formula, as well as tool strengths
\r
231 TODO: Flowing water to actually contain flow direction information
\r
233 TODO: Remove duplicate lighting implementation from Map (leave
\r
234 VoxelManipulator, which is faster)
\r
236 FEATURE: Create a system that allows a huge amount of different "map
\r
237 generator modules/filters"
\r
239 FEATURE: Erosion simulation at map generation time
\r
240 - Simulate water flows, which would carve out dirt fast and
\r
241 then turn stone into gravel and sand and relocate it.
\r
242 - How about relocating minerals, too? Coal and gold in
\r
243 downstream sand and gravel would be kind of cool
\r
244 - This would need a better way of handling minerals, mainly
\r
245 to have mineral content as a separate field. the first
\r
246 parameter field is free for this.
\r
247 - Simulate rock falling from cliffs when water has removed
\r
248 enough solid rock from the bottom
\r
250 Doing now (most important at the top):
\r
251 --------------------------------------
\r
256 * Continue making the scripting system:
\r
257 * Make updateNodeMesh for a less verbose mesh update on add/removenode
\r
258 * Switch to using a safe way for the self and env pointers
\r
259 * Make some global environment hooks, like node placed and general
\r
263 * Check the fixmes in the list above
\r
264 * Make server find the spawning place from the real map data, not from
\r
266 - But the changing borders of chunk have to be avoided, because
\r
267 there is time to generate only one chunk.
\r
268 * Make the generator to run in background and not blocking block
\r
269 placement and transfer
\r
270 * only_from_disk might not work anymore - check and fix it.
\r
272 === Making it more portable
\r
273 * Some MSVC: std::sto* are defined without a namespace and collide
\r
274 with the ones in utility.h
\r
277 * Map should make the appropriate MapEditEvents
\r
278 * Add a global Lua spawn handler and such
\r
279 * Get rid of MapBlockObjects
\r
280 * Other players could be sent to clients as LuaCAOs
\r
281 * Add mud underground
\r
282 * Make an "environment metafile" to store at least time of day
\r
283 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
284 - Or maybe move content_features to material.{h,cpp}?
\r
285 * Add some kind of erosion and other stuff that now is possible
\r
286 * Make client to fetch stuff asynchronously
\r
287 - Needs method SyncProcessData
\r
288 * Better water generation (spread it to underwater caverns but don't
\r
289 fill dungeons that don't touch big water masses)
\r
290 * When generating a chunk and the neighboring chunk doesn't have mud
\r
291 and stuff yet and the ground is fairly flat, the mud will flow to
\r
292 the other chunk making nasty straight walls when the other chunk
\r
293 is generated. Fix it.
\r
294 * Fix the problem with the server constantly saving one or a few
\r
295 blocks? List the first saved block, maybe it explains.
\r
296 - It is probably caused by oscillating water
\r
297 * Make a small history check to transformLiquids to detect and log
\r
298 continuous oscillations, in such detail that they can be fixed.
\r
299 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
301 ======================================================================
\r
306 Setting this to 1 enables a special camera mode that forces
\r
307 the renderers to think that the camera statically points from
\r
308 the starting place to a static direction.
\r
310 This allows one to move around with the player and see what
\r
311 is actually drawn behind solid things and behind the player.
\r
313 #define FIELD_OF_VIEW_TEST 0
\r
317 #pragma message ("Disabling unit tests")
\r
319 #warning "Disabling unit tests"
\r
321 // Disable unit tests
\r
322 #define ENABLE_TESTS 0
\r
324 // Enable unit tests
\r
325 #define ENABLE_TESTS 1
\r
329 #pragma comment(lib, "Irrlicht.lib")
\r
330 //#pragma comment(lib, "jthread.lib")
\r
331 #pragma comment(lib, "zlibwapi.lib")
\r
332 #pragma comment(lib, "Shell32.lib")
\r
333 // This would get rid of the console window
\r
334 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
337 #include <iostream>
\r
339 #include <jmutexautolock.h>
\r
340 #include <locale.h>
\r
342 #include "common_irrlicht.h"
\r
345 #include "player.h"
\r
347 //#include "environment.h"
\r
348 #include "server.h"
\r
349 #include "client.h"
\r
350 //#include "serialization.h"
\r
351 #include "constants.h"
\r
352 //#include "strfnd.h"
\r
353 #include "porting.h"
\r
354 #include "irrlichtwrapper.h"
\r
355 #include "gettime.h"
\r
356 #include "porting.h"
\r
357 #include "guiPauseMenu.h"
\r
358 #include "guiInventoryMenu.h"
\r
359 #include "guiTextInputMenu.h"
\r
360 #include "materials.h"
\r
361 #include "guiMessageMenu.h"
\r
362 #include "filesys.h"
\r
363 #include "config.h"
\r
364 #include "guiMainMenu.h"
\r
365 #include "mineral.h"
\r
369 // TODO: Remove this
\r
370 IrrlichtWrapper *g_irrlicht = NULL;
\r
372 // This makes textures
\r
373 ITextureSource *g_texturesource = NULL;
\r
375 MapDrawControl draw_control;
\r
379 These are loaded from the config file.
\r
382 Settings g_settings;
\r
384 extern void set_default_settings();
\r
390 IrrlichtDevice *g_device = NULL;
\r
391 Client *g_client = NULL;
\r
393 /*const s16 quickinv_size = 40;
\r
394 const s16 quickinv_padding = 8;
\r
395 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
396 const s16 quickinv_outer_padding = 4;
\r
397 const s16 quickinv_itemcount = 8;*/
\r
399 const s32 hotbar_itemcount = 8;
\r
400 const s32 hotbar_imagesize = 36;
\r
406 gui::IGUIEnvironment* guienv = NULL;
\r
407 gui::IGUIStaticText *guiroot = NULL;
\r
409 class MainMenuManager : public IMenuManager
\r
412 virtual void createdMenu(GUIModalMenu *menu)
\r
414 for(core::list<GUIModalMenu*>::Iterator
\r
415 i = m_stack.begin();
\r
416 i != m_stack.end(); i++)
\r
418 assert(*i != menu);
\r
421 if(m_stack.size() != 0)
\r
422 (*m_stack.getLast())->setVisible(false);
\r
423 m_stack.push_back(menu);
\r
426 virtual void deletingMenu(GUIModalMenu *menu)
\r
428 // Remove all entries if there are duplicates
\r
429 bool removed_entry;
\r
431 removed_entry = false;
\r
432 for(core::list<GUIModalMenu*>::Iterator
\r
433 i = m_stack.begin();
\r
434 i != m_stack.end(); i++)
\r
439 removed_entry = true;
\r
443 }while(removed_entry);
\r
445 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
446 assert(*i == menu);
\r
447 m_stack.erase(i);*/
\r
449 if(m_stack.size() != 0)
\r
450 (*m_stack.getLast())->setVisible(true);
\r
455 return m_stack.size();
\r
458 core::list<GUIModalMenu*> m_stack;
\r
461 MainMenuManager g_menumgr;
\r
463 bool noMenuActive()
\r
465 return (g_menumgr.menuCount() == 0);
\r
468 bool g_disconnect_requested = false;
\r
470 class MainGameCallback : public IGameCallback
\r
473 virtual void exitToOS()
\r
475 g_device->closeDevice();
\r
478 virtual void disconnect()
\r
480 g_disconnect_requested = true;
\r
484 MainGameCallback g_gamecallback;
\r
486 // Inventory actions from the menu are buffered here before sending
\r
487 Queue<InventoryAction*> inventory_action_queue;
\r
488 // This is a copy of the inventory that the client's environment has
\r
489 Inventory local_inventory;
\r
491 u16 g_selected_item = 0;
\r
498 std::ostream *dout_con_ptr = &dummyout;
\r
499 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
500 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
501 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
502 //std::ostream *dout_con_ptr = &dstream;
\r
503 //std::ostream *derr_con_ptr = &dstream;
\r
506 std::ostream *dout_server_ptr = &dstream;
\r
507 std::ostream *derr_server_ptr = &dstream;
\r
510 std::ostream *dout_client_ptr = &dstream;
\r
511 std::ostream *derr_client_ptr = &dstream;
\r
514 gettime.h implementation
\r
520 Use irrlicht because it is more precise than porting.h's
\r
523 if(g_irrlicht == NULL)
\r
525 return g_irrlicht->getTime();
\r
532 struct TextDestSign : public TextDest
\r
534 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
536 m_blockpos = blockpos;
\r
540 void gotText(std::wstring text)
\r
542 std::string ntext = wide_to_narrow(text);
\r
543 dstream<<"Changing text of a sign object: "
\r
544 <<ntext<<std::endl;
\r
545 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
553 struct TextDestChat : public TextDest
\r
555 TextDestChat(Client *client)
\r
559 void gotText(std::wstring text)
\r
561 // Discard empty line
\r
565 // Parse command (server command starts with "/#")
\r
566 if(text[0] == L'/' && text[1] != L'#')
\r
568 std::wstring reply = L"Local: ";
\r
570 reply += L"Local commands not yet supported. "
\r
571 L"Server prefix is \"/#\".";
\r
573 m_client->addChatMessage(reply);
\r
578 m_client->sendChatMessage(text);
\r
580 m_client->addChatMessage(text);
\r
586 class MyEventReceiver : public IEventReceiver
\r
589 // This is the one method that we have to implement
\r
590 virtual bool OnEvent(const SEvent& event)
\r
593 React to nothing here if a menu is active
\r
595 if(noMenuActive() == false)
\r
601 // Remember whether each key is down or up
\r
602 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
604 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
606 if(event.KeyInput.PressedDown)
\r
608 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
614 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
616 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
618 dstream<<DTIME<<"MyEventReceiver: "
\r
619 <<"Launching pause menu"<<std::endl;
\r
620 // It will delete itself by itself
\r
621 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
622 &g_menumgr))->drop();
\r
625 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
627 dstream<<DTIME<<"MyEventReceiver: "
\r
628 <<"Launching inventory"<<std::endl;
\r
629 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
630 &local_inventory, &inventory_action_queue,
\r
631 &g_menumgr))->drop();
\r
634 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
636 TextDest *dest = new TextDestChat(g_client);
\r
638 (new GUITextInputMenu(guienv, guiroot, -1,
\r
645 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
646 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
648 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
649 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
651 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
652 g_selected_item = s1-1;
\r
653 dstream<<DTIME<<"Selected item: "
\r
654 <<g_selected_item<<std::endl;
\r
657 // Viewing range selection
\r
658 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
660 if(draw_control.range_all)
\r
662 draw_control.range_all = false;
\r
663 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
667 draw_control.range_all = true;
\r
668 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
672 // Print debug stacks
\r
673 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
675 dstream<<"-----------------------------------------"
\r
677 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
678 dstream<<"-----------------------------------------"
\r
680 debug_stacks_print();
\r
685 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
687 if(noMenuActive() == false)
\r
689 left_active = false;
\r
690 middle_active = false;
\r
691 right_active = false;
\r
695 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
696 left_active = event.MouseInput.isLeftPressed();
\r
697 middle_active = event.MouseInput.isMiddlePressed();
\r
698 right_active = event.MouseInput.isRightPressed();
\r
700 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
702 leftclicked = true;
\r
704 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
706 rightclicked = true;
\r
708 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
710 leftreleased = true;
\r
712 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
714 rightreleased = true;
\r
716 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
718 /*dstream<<"event.MouseInput.Wheel="
\r
719 <<event.MouseInput.Wheel<<std::endl;*/
\r
721 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
722 hotbar_itemcount-1);
\r
723 if(event.MouseInput.Wheel < 0)
\r
725 if(g_selected_item < max_item)
\r
728 g_selected_item = 0;
\r
730 else if(event.MouseInput.Wheel > 0)
\r
732 if(g_selected_item > 0)
\r
735 g_selected_item = max_item;
\r
744 // This is used to check whether a key is being held down
\r
745 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
747 return keyIsDown[keyCode];
\r
752 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
753 keyIsDown[i] = false;
\r
755 leftclicked = false;
\r
756 rightclicked = false;
\r
757 leftreleased = false;
\r
758 rightreleased = false;
\r
760 left_active = false;
\r
761 middle_active = false;
\r
762 right_active = false;
\r
773 bool rightreleased;
\r
776 bool middle_active;
\r
780 // We use this array to store the current state of each key
\r
781 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
784 IrrlichtDevice *m_device;
\r
793 virtual ~InputHandler()
\r
797 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
799 virtual v2s32 getMousePos() = 0;
\r
800 virtual void setMousePos(s32 x, s32 y) = 0;
\r
802 virtual bool getLeftState() = 0;
\r
803 virtual bool getRightState() = 0;
\r
805 virtual bool getLeftClicked() = 0;
\r
806 virtual bool getRightClicked() = 0;
\r
807 virtual void resetLeftClicked() = 0;
\r
808 virtual void resetRightClicked() = 0;
\r
810 virtual bool getLeftReleased() = 0;
\r
811 virtual bool getRightReleased() = 0;
\r
812 virtual void resetLeftReleased() = 0;
\r
813 virtual void resetRightReleased() = 0;
\r
815 virtual void step(float dtime) {};
\r
817 virtual void clear() {};
\r
820 InputHandler *g_input = NULL;
\r
822 class RealInputHandler : public InputHandler
\r
825 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
827 m_receiver(receiver)
\r
830 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
832 return m_receiver->IsKeyDown(keyCode);
\r
834 virtual v2s32 getMousePos()
\r
836 return m_device->getCursorControl()->getPosition();
\r
838 virtual void setMousePos(s32 x, s32 y)
\r
840 m_device->getCursorControl()->setPosition(x, y);
\r
843 virtual bool getLeftState()
\r
845 return m_receiver->left_active;
\r
847 virtual bool getRightState()
\r
849 return m_receiver->right_active;
\r
852 virtual bool getLeftClicked()
\r
854 return m_receiver->leftclicked;
\r
856 virtual bool getRightClicked()
\r
858 return m_receiver->rightclicked;
\r
860 virtual void resetLeftClicked()
\r
862 m_receiver->leftclicked = false;
\r
864 virtual void resetRightClicked()
\r
866 m_receiver->rightclicked = false;
\r
869 virtual bool getLeftReleased()
\r
871 return m_receiver->leftreleased;
\r
873 virtual bool getRightReleased()
\r
875 return m_receiver->rightreleased;
\r
877 virtual void resetLeftReleased()
\r
879 m_receiver->leftreleased = false;
\r
881 virtual void resetRightReleased()
\r
883 m_receiver->rightreleased = false;
\r
888 resetRightClicked();
\r
889 resetLeftClicked();
\r
892 IrrlichtDevice *m_device;
\r
893 MyEventReceiver *m_receiver;
\r
896 class RandomInputHandler : public InputHandler
\r
899 RandomInputHandler()
\r
903 leftclicked = false;
\r
904 rightclicked = false;
\r
905 leftreleased = false;
\r
906 rightreleased = false;
\r
907 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
908 keydown[i] = false;
\r
910 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
912 return keydown[keyCode];
\r
914 virtual v2s32 getMousePos()
\r
918 virtual void setMousePos(s32 x, s32 y)
\r
920 mousepos = v2s32(x,y);
\r
923 virtual bool getLeftState()
\r
927 virtual bool getRightState()
\r
932 virtual bool getLeftClicked()
\r
934 return leftclicked;
\r
936 virtual bool getRightClicked()
\r
938 return rightclicked;
\r
940 virtual void resetLeftClicked()
\r
942 leftclicked = false;
\r
944 virtual void resetRightClicked()
\r
946 rightclicked = false;
\r
949 virtual bool getLeftReleased()
\r
951 return leftreleased;
\r
953 virtual bool getRightReleased()
\r
955 return rightreleased;
\r
957 virtual void resetLeftReleased()
\r
959 leftreleased = false;
\r
961 virtual void resetRightReleased()
\r
963 rightreleased = false;
\r
966 virtual void step(float dtime)
\r
969 static float counter1 = 0;
\r
973 counter1 = 0.1*Rand(1, 40);
\r
974 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
978 static float counter1 = 0;
\r
982 counter1 = 0.1*Rand(1, 40);
\r
983 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
987 static float counter1 = 0;
\r
991 counter1 = 0.1*Rand(1, 40);
\r
992 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
996 static float counter1 = 0;
\r
1000 counter1 = 0.1*Rand(1, 40);
\r
1001 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1005 static float counter1 = 0;
\r
1006 counter1 -= dtime;
\r
1007 if(counter1 < 0.0)
\r
1009 counter1 = 0.1*Rand(1, 20);
\r
1010 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1014 static float counter1 = 0;
\r
1015 counter1 -= dtime;
\r
1016 if(counter1 < 0.0)
\r
1018 counter1 = 0.1*Rand(1, 30);
\r
1019 leftdown = !leftdown;
\r
1021 leftclicked = true;
\r
1023 leftreleased = true;
\r
1027 static float counter1 = 0;
\r
1028 counter1 -= dtime;
\r
1029 if(counter1 < 0.0)
\r
1031 counter1 = 0.1*Rand(1, 15);
\r
1032 rightdown = !rightdown;
\r
1034 rightclicked = true;
\r
1036 rightreleased = true;
\r
1039 mousepos += mousespeed;
\r
1042 s32 Rand(s32 min, s32 max)
\r
1044 return (myrand()%(max-min+1))+min;
\r
1047 bool keydown[KEY_KEY_CODES_COUNT];
\r
1053 bool rightclicked;
\r
1054 bool leftreleased;
\r
1055 bool rightreleased;
\r
1058 void updateViewingRange(f32 frametime_in, Client *client)
\r
1060 if(draw_control.range_all == true)
\r
1063 static f32 added_frametime = 0;
\r
1064 static s16 added_frames = 0;
\r
1066 added_frametime += frametime_in;
\r
1067 added_frames += 1;
\r
1069 // Actually this counter kind of sucks because frametime is busytime
\r
1070 static f32 counter = 0;
\r
1071 counter -= frametime_in;
\r
1077 /*dstream<<__FUNCTION_NAME
\r
1078 <<": Collected "<<added_frames<<" frames, total of "
\r
1079 <<added_frametime<<"s."<<std::endl;*/
\r
1081 /*dstream<<"draw_control.blocks_drawn="
\r
1082 <<draw_control.blocks_drawn
\r
1083 <<", draw_control.blocks_would_have_drawn="
\r
1084 <<draw_control.blocks_would_have_drawn
\r
1087 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1088 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1090 draw_control.wanted_min_range = range_min;
\r
1091 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1093 float block_draw_ratio = 1.0;
\r
1094 if(draw_control.blocks_would_have_drawn != 0)
\r
1096 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1097 / (float)draw_control.blocks_would_have_drawn;
\r
1100 // Calculate the average frametime in the case that all wanted
\r
1101 // blocks had been drawn
\r
1102 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1104 added_frametime = 0.0;
\r
1107 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1108 float wanted_frametime = 1.0 / wanted_fps;
\r
1110 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1111 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1113 // If needed frametime change is very small, just return
\r
1114 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1116 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1120 float range = draw_control.wanted_range;
\r
1121 float new_range = range;
\r
1123 static s16 range_old = 0;
\r
1124 static f32 frametime_old = 0;
\r
1126 float d_range = range - range_old;
\r
1127 f32 d_frametime = frametime - frametime_old;
\r
1128 // A sane default of 30ms per 50 nodes of range
\r
1129 static f32 time_per_range = 30. / 50;
\r
1132 time_per_range = d_frametime / d_range;
\r
1135 // The minimum allowed calculated frametime-range derivative:
\r
1136 // Practically this sets the maximum speed of changing the range.
\r
1137 // The lower this value, the higher the maximum changing speed.
\r
1138 // A low value here results in wobbly range (0.001)
\r
1139 // A high value here results in slow changing range (0.0025)
\r
1140 // SUGG: This could be dynamically adjusted so that when
\r
1141 // the camera is turning, this is lower
\r
1142 //float min_time_per_range = 0.0015;
\r
1143 float min_time_per_range = 0.0010;
\r
1144 //float min_time_per_range = 0.05 / range;
\r
1145 if(time_per_range < min_time_per_range)
\r
1147 time_per_range = min_time_per_range;
\r
1148 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1152 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1155 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1156 // Dampen the change a bit to kill oscillations
\r
1157 //wanted_range_change *= 0.9;
\r
1158 //wanted_range_change *= 0.75;
\r
1159 wanted_range_change *= 0.5;
\r
1160 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1162 // If needed range change is very small, just return
\r
1163 if(fabs(wanted_range_change) < 0.001)
\r
1165 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1169 new_range += wanted_range_change;
\r
1170 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1172 //float new_range_unclamped = new_range;
\r
1173 if(new_range < range_min)
\r
1174 new_range = range_min;
\r
1175 if(new_range > range_max)
\r
1176 new_range = range_max;
\r
1178 /*if(new_range != new_range_unclamped)
\r
1179 dstream<<", clamped to "<<new_range<<std::endl;
\r
1181 dstream<<std::endl;*/
\r
1183 draw_control.wanted_range = new_range;
\r
1185 range_old = new_range;
\r
1186 frametime_old = frametime;
\r
1189 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1190 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1191 Inventory *inventory)
\r
1193 InventoryList *mainlist = inventory->getList("main");
\r
1194 if(mainlist == NULL)
\r
1196 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1200 s32 padding = imgsize/12;
\r
1201 //s32 height = imgsize + padding*2;
\r
1202 s32 width = itemcount*(imgsize+padding*2);
\r
1204 // Position of upper left corner of bar
\r
1205 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1207 // Draw background color
\r
1208 /*core::rect<s32> barrect(0,0,width,height);
\r
1210 video::SColor bgcolor(255,128,128,128);
\r
1211 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1213 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1215 for(s32 i=0; i<itemcount; i++)
\r
1217 InventoryItem *item = mainlist->getItem(i);
\r
1219 core::rect<s32> rect = imgrect + pos
\r
1220 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1222 if(g_selected_item == i)
\r
1224 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1225 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1226 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1231 video::SColor bgcolor2(128,0,0,0);
\r
1232 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1237 drawInventoryItem(driver, font, item, rect, NULL);
\r
1249 ChatLine(const std::wstring &a_text):
\r
1255 std::wstring text;
\r
1258 // These are defined global so that they're not optimized too much.
\r
1259 // Can't change them to volatile.
\r
1264 std::string tempstring;
\r
1265 std::string tempstring2;
\r
1270 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1271 TimeTaker timer("Testing std::string speed");
\r
1272 const u32 jj = 10000;
\r
1273 for(u32 j=0; j<jj; j++)
\r
1277 const u32 ii = 10;
\r
1278 for(u32 i=0; i<ii; i++){
\r
1279 tempstring2 += "asd";
\r
1281 for(u32 i=0; i<ii+1; i++){
\r
1282 tempstring += "asd";
\r
1283 if(tempstring == tempstring2)
\r
1289 dstream<<"All of the following tests should take around 100ms each."
\r
1293 TimeTaker timer("Testing floating-point conversion speed");
\r
1295 for(u32 i=0; i<4000000; i++){
\r
1302 TimeTaker timer("Testing floating-point vector speed");
\r
1304 tempv3f1 = v3f(1,2,3);
\r
1305 tempv3f2 = v3f(4,5,6);
\r
1306 for(u32 i=0; i<10000000; i++){
\r
1307 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1308 tempv3f2 += v3f(7,8,9);
\r
1313 TimeTaker timer("Testing core::map speed");
\r
1315 core::map<v2s16, f32> map1;
\r
1318 for(s16 y=0; y<ii; y++){
\r
1319 for(s16 x=0; x<ii; x++){
\r
1320 map1.insert(v2s16(x,y), tempf);
\r
1324 for(s16 y=ii-1; y>=0; y--){
\r
1325 for(s16 x=0; x<ii; x++){
\r
1326 tempf = map1[v2s16(x,y)];
\r
1332 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1333 TimeTaker timer("Testing mutex speed");
\r
1346 // Do at least 10ms
\r
1347 while(timer.getTime() < 10);
\r
1349 u32 dtime = timer.stop();
\r
1350 u32 per_ms = n / dtime;
\r
1351 std::cout<<"Done. "<<dtime<<"ms, "
\r
1352 <<per_ms<<"/ms"<<std::endl;
\r
1356 int main(int argc, char *argv[])
\r
1359 Parse command line
\r
1362 // List all allowed options
\r
1363 core::map<std::string, ValueSpec> allowed_options;
\r
1364 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1365 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1366 "Run server directly"));
\r
1367 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1368 "Load configuration from specified file"));
\r
1369 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1370 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1371 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1372 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1373 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1374 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1376 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1378 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1380 Settings cmd_args;
\r
1382 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1384 if(ret == false || cmd_args.getFlag("help"))
\r
1386 dstream<<"Allowed options:"<<std::endl;
\r
1387 for(core::map<std::string, ValueSpec>::Iterator
\r
1388 i = allowed_options.getIterator();
\r
1389 i.atEnd() == false; i++)
\r
1391 dstream<<" --"<<i.getNode()->getKey();
\r
1392 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1397 dstream<<" <value>";
\r
1399 dstream<<std::endl;
\r
1401 if(i.getNode()->getValue().help != NULL)
\r
1403 dstream<<" "<<i.getNode()->getValue().help
\r
1408 return cmd_args.getFlag("help") ? 0 : 1;
\r
1412 Low-level initialization
\r
1415 bool disable_stderr = false;
\r
1417 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1418 disable_stderr = true;
\r
1421 // Initialize debug streams
\r
1422 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1423 // Initialize debug stacks
\r
1424 debug_stacks_init();
\r
1426 DSTACK(__FUNCTION_NAME);
\r
1428 porting::signal_handler_init();
\r
1429 bool &kill = *porting::signal_handler_killstatus();
\r
1431 porting::initializePaths();
\r
1432 // Create user data directory
\r
1433 fs::CreateDir(porting::path_userdata);
\r
1435 // C-style stuff initialization
\r
1436 initializeMaterialProperties();
\r
1439 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1441 // Print startup message
\r
1442 dstream<<DTIME<<"minetest-c55"
\r
1443 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1444 <<", "<<BUILD_INFO
\r
1448 Basic initialization
\r
1451 // Initialize default settings
\r
1452 set_default_settings();
\r
1454 // Set locale. This is for forcing '.' as the decimal point.
\r
1455 std::locale::global(std::locale("C"));
\r
1456 // This enables printing all characters in bitmap font
\r
1457 setlocale(LC_CTYPE, "en_US");
\r
1459 // Initialize sockets
\r
1461 atexit(sockets_cleanup);
\r
1471 // Path of configuration file in use
\r
1472 std::string configpath = "";
\r
1474 if(cmd_args.exists("config"))
\r
1476 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1479 dstream<<"Could not read configuration from \""
\r
1480 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1483 configpath = cmd_args.get("config");
\r
1487 core::array<std::string> filenames;
\r
1488 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1489 #ifdef RUN_IN_PLACE
\r
1490 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1493 for(u32 i=0; i<filenames.size(); i++)
\r
1495 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1498 configpath = filenames[i];
\r
1503 // If no path found, use the first one (menu creates the file)
\r
1504 if(configpath == "")
\r
1505 configpath = filenames[0];
\r
1508 // Initialize random seed
\r
1513 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1515 These are needed for unit tests at least.
\r
1518 // Initial call with g_texturesource not set.
\r
1525 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1526 || cmd_args.getFlag("enable-unittests") == true)
\r
1531 /*for(s16 y=-100; y<100; y++)
\r
1532 for(s16 x=-100; x<100; x++)
\r
1534 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1544 if(cmd_args.exists("port"))
\r
1545 port = cmd_args.getU16("port");
\r
1546 else if(cmd_args.exists("port"))
\r
1547 port = g_settings.getU16("port");
\r
1550 std::string map_dir = porting::path_userdata+"/map";
\r
1551 if(cmd_args.exists("map-dir"))
\r
1552 map_dir = cmd_args.get("map-dir");
\r
1553 else if(g_settings.exists("map-dir"))
\r
1554 map_dir = g_settings.get("map-dir");
\r
1556 // Run dedicated server if asked to
\r
1557 if(cmd_args.getFlag("server"))
\r
1559 DSTACK("Dedicated server branch");
\r
1562 Server server(map_dir.c_str());
\r
1563 server.start(port);
\r
1566 dedicated_server_loop(server, kill);
\r
1575 // Address to connect to
\r
1576 std::string address = "";
\r
1578 if(cmd_args.exists("address"))
\r
1580 address = cmd_args.get("address");
\r
1584 address = g_settings.get("address");
\r
1587 std::string playername = g_settings.get("name");
\r
1589 // Resolution selection
\r
1591 bool fullscreen = false;
\r
1592 u16 screenW = g_settings.getU16("screenW");
\r
1593 u16 screenH = g_settings.getU16("screenH");
\r
1595 // Determine driver
\r
1597 video::E_DRIVER_TYPE driverType;
\r
1599 std::string driverstring = g_settings.get("video_driver");
\r
1601 if(driverstring == "null")
\r
1602 driverType = video::EDT_NULL;
\r
1603 else if(driverstring == "software")
\r
1604 driverType = video::EDT_SOFTWARE;
\r
1605 else if(driverstring == "burningsvideo")
\r
1606 driverType = video::EDT_BURNINGSVIDEO;
\r
1607 else if(driverstring == "direct3d8")
\r
1608 driverType = video::EDT_DIRECT3D8;
\r
1609 else if(driverstring == "direct3d9")
\r
1610 driverType = video::EDT_DIRECT3D9;
\r
1611 else if(driverstring == "opengl")
\r
1612 driverType = video::EDT_OPENGL;
\r
1615 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1616 "to opengl"<<std::endl;
\r
1617 driverType = video::EDT_OPENGL;
\r
1620 // create device and exit if creation failed
\r
1622 MyEventReceiver receiver;
\r
1624 IrrlichtDevice *device;
\r
1625 device = createDevice(driverType,
\r
1626 core::dimension2d<u32>(screenW, screenH),
\r
1627 16, fullscreen, false, false, &receiver);
\r
1630 return 1; // could not create selected driver.
\r
1632 g_device = device;
\r
1633 g_irrlicht = new IrrlichtWrapper(device);
\r
1634 TextureSource *texturesource = new TextureSource(device);
\r
1635 g_texturesource = texturesource;
\r
1638 Speed tests (done after irrlicht is loaded to get timer)
\r
1640 if(cmd_args.getFlag("speedtests"))
\r
1642 dstream<<"Running speed tests"<<std::endl;
\r
1647 device->setResizable(true);
\r
1649 bool random_input = g_settings.getBool("random_input")
\r
1650 || cmd_args.getFlag("random-input");
\r
1652 g_input = new RandomInputHandler();
\r
1654 g_input = new RealInputHandler(device, &receiver);
\r
1657 Continue initialization
\r
1660 video::IVideoDriver* driver = device->getVideoDriver();
\r
1663 This changes the minimum allowed number of vertices in a VBO.
\r
1666 //driver->setMinHardwareBufferVertexCount(50);
\r
1668 scene::ISceneManager* smgr = device->getSceneManager();
\r
1670 guienv = device->getGUIEnvironment();
\r
1671 gui::IGUISkin* skin = guienv->getSkin();
\r
1672 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1674 skin->setFont(font);
\r
1676 dstream<<"WARNING: Font file was not found."
\r
1677 " Using default font."<<std::endl;
\r
1678 // If font was not found, this will get us one
\r
1679 font = skin->getFont();
\r
1682 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1683 dstream<<"text_height="<<text_height<<std::endl;
\r
1685 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1686 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1687 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1688 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1689 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1690 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1693 Preload some textures and stuff
\r
1696 init_content_inventory_texture_paths();
\r
1697 init_mapnode(); // Second call with g_texturesource set
\r
1705 We need some kind of a root node to be able to add
\r
1706 custom gui elements directly on the screen.
\r
1707 Otherwise they won't be automatically drawn.
\r
1709 guiroot = guienv->addStaticText(L"",
\r
1710 core::rect<s32>(0, 0, 10000, 10000));
\r
1712 // First line of debug text
\r
1713 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1715 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1717 // Second line of debug text
\r
1718 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1720 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1723 // At the middle of the screen
\r
1724 // Object infos are shown in this
\r
1725 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1727 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1731 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1733 core::rect<s32>(0,0,0,0),
\r
1734 false, false); // Disable word wrap as of now
\r
1736 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1737 core::list<ChatLine> chat_lines;
\r
1740 If an error occurs, this is set to something and the
\r
1741 menu-game loop is restarted. It is then displayed before
\r
1744 std::wstring error_message = L"";
\r
1749 while(g_device->run() && kill == false)
\r
1752 // This is used for catching disconnects
\r
1757 Out-of-game menu loop.
\r
1759 Loop quits when menu returns proper parameters.
\r
1761 while(kill == false)
\r
1763 // Cursor can be non-visible when coming from the game
\r
1764 device->getCursorControl()->setVisible(true);
\r
1765 // Some stuff are left to scene manager when coming from the game
\r
1766 // (map at least?)
\r
1768 // Reset or hide the debug gui texts
\r
1769 guitext->setText(L"Minetest-c55");
\r
1770 guitext2->setVisible(false);
\r
1771 guitext_info->setVisible(false);
\r
1772 guitext_chat->setVisible(false);
\r
1774 // Initialize menu data
\r
1775 MainMenuData menudata;
\r
1776 menudata.address = narrow_to_wide(address);
\r
1777 menudata.name = narrow_to_wide(playername);
\r
1778 menudata.port = narrow_to_wide(itos(port));
\r
1779 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1781 GUIMainMenu *menu =
\r
1782 new GUIMainMenu(guienv, guiroot, -1,
\r
1783 &g_menumgr, &menudata, &g_gamecallback);
\r
1784 menu->allowFocusRemoval(true);
\r
1786 if(error_message != L"")
\r
1788 GUIMessageMenu *menu2 =
\r
1789 new GUIMessageMenu(guienv, guiroot, -1,
\r
1790 &g_menumgr, error_message.c_str());
\r
1792 error_message = L"";
\r
1795 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1797 dstream<<"Created main menu"<<std::endl;
\r
1799 while(g_device->run() && kill == false)
\r
1801 // Run global IrrlichtWrapper's main thread processing stuff
\r
1802 g_irrlicht->Run();
\r
1804 if(menu->getStatus() == true)
\r
1807 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1808 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1809 guienv->drawAll();
\r
1810 driver->endScene();
\r
1813 // Break out of menu-game loop to shut down cleanly
\r
1814 if(g_device->run() == false || kill == true)
\r
1817 dstream<<"Dropping main menu"<<std::endl;
\r
1821 // Delete map if requested
\r
1822 if(menudata.delete_map)
\r
1824 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1826 error_message = L"Delete failed";
\r
1830 playername = wide_to_narrow(menudata.name);
\r
1831 address = wide_to_narrow(menudata.address);
\r
1832 port = stoi(wide_to_narrow(menudata.port));
\r
1833 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1835 // Check for valid parameters, restart menu if invalid.
\r
1836 if(playername == "")
\r
1838 error_message = L"Name required.";
\r
1843 g_settings.set("name", playername);
\r
1844 g_settings.set("address", address);
\r
1845 g_settings.set("port", itos(port));
\r
1846 // Update configuration file
\r
1847 if(configpath != "")
\r
1848 g_settings.updateConfigFile(configpath.c_str());
\r
1850 // Continue to game
\r
1854 // Break out of menu-game loop to shut down cleanly
\r
1855 if(g_device->run() == false)
\r
1859 Make a scope here so that the client and the server and other
\r
1860 stuff gets removed when disconnected or the irrlicht device
\r
1865 // This is set to true at the end of the scope
\r
1866 g_irrlicht->Shutdown(false);
\r
1869 Draw "Loading" screen
\r
1871 const wchar_t *text = L"Loading and connecting...";
\r
1872 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1873 core::vector2d<s32> textsize(300, text_height);
\r
1874 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1876 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1877 text, textrect, false, false);
\r
1878 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1880 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1881 guienv->drawAll();
\r
1882 driver->endScene();
\r
1884 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1888 SharedPtr will delete it when it goes out of scope.
\r
1890 SharedPtr<Server> server;
\r
1891 if(address == ""){
\r
1892 server = new Server(map_dir);
\r
1893 server->start(port);
\r
1900 Client client(device, playername.c_str(), draw_control);
\r
1902 g_client = &client;
\r
1904 Address connect_address(0,0,0,0, port);
\r
1907 //connect_address.Resolve("localhost");
\r
1908 connect_address.setAddress(127,0,0,1);
\r
1910 connect_address.Resolve(address.c_str());
\r
1912 catch(ResolveError &e)
\r
1914 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1916 error_message = L"Couldn't resolve address";
\r
1917 gui_loadingtext->remove();
\r
1921 dstream<<DTIME<<"Connecting to server at ";
\r
1922 connect_address.print(&dstream);
\r
1923 dstream<<std::endl;
\r
1924 client.connect(connect_address);
\r
1927 while(client.connectedAndInitialized() == false)
\r
1930 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1931 guienv->drawAll();
\r
1932 driver->endScene();
\r
1934 // Update client and server
\r
1938 if(server != NULL)
\r
1939 server->step(0.1);
\r
1945 catch(con::PeerNotFoundException &e)
\r
1947 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1949 error_message = L"Connection timed out.";
\r
1950 gui_loadingtext->remove();
\r
1957 /*scene::ISceneNode* skybox;
\r
1958 skybox = smgr->addSkyBoxSceneNode(
\r
1959 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1960 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1961 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1962 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1963 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1964 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1967 Create the camera node
\r
1970 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1971 0, // Camera parent
\r
1972 v3f(BS*100, BS*2, BS*100), // Look from
\r
1973 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1977 if(camera == NULL)
\r
1980 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
1981 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
1982 video::SColor skycolor = video::SColor(255,120,185,244);
\r
1984 camera->setFOV(FOV_ANGLE);
\r
1986 // Just so big a value that everything rendered is visible
\r
1987 camera->setFarValue(100000*BS);
\r
1990 Lighting test code. Doesn't quite work this way.
\r
1991 The CPU-computed lighting is good.
\r
1995 smgr->addLightSceneNode(NULL,
\r
1996 v3f(0, BS*1000000, 0),
\r
1997 video::SColorf(0.3,0.3,0.3),
\r
2000 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2002 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2003 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2006 f32 camera_yaw = 0; // "right/left"
\r
2007 f32 camera_pitch = 0; // "up/down"
\r
2013 gui_loadingtext->remove();
\r
2016 Add some gui stuff
\r
2019 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2020 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2021 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2022 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2024 // Test the text input system
\r
2025 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2027 /*GUIMessageMenu *menu =
\r
2028 new GUIMessageMenu(guienv, guiroot, -1,
\r
2033 // Launch pause menu
\r
2034 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2035 &g_menumgr))->drop();
\r
2038 guitext2->setVisible(true);
\r
2039 guitext_info->setVisible(true);
\r
2040 guitext_chat->setVisible(true);
\r
2042 //s32 guitext_chat_pad_bottom = 70;
\r
2044 v2u32 screensize(0,0);
\r
2045 v2u32 last_screensize(0,0);
\r
2048 Some statistics are collected in these
\r
2051 u32 beginscenetime = 0;
\r
2052 u32 scenetime = 0;
\r
2053 u32 endscenetime = 0;
\r
2056 //throw con::PeerNotFoundException("lol");
\r
2058 core::list<float> frametime_log;
\r
2064 bool first_loop_after_window_activation = true;
\r
2066 // Time is in milliseconds
\r
2067 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2068 // NOTE: So we have to use getTime() and call run()s between them
\r
2069 u32 lasttime = device->getTimer()->getTime();
\r
2071 while(device->run() && kill == false)
\r
2073 if(g_disconnect_requested)
\r
2075 g_disconnect_requested = false;
\r
2080 Run global IrrlichtWrapper's main thread processing stuff
\r
2082 g_irrlicht->Run();
\r
2085 Process TextureSource's queue
\r
2087 texturesource->processQueue();
\r
2090 Random calculations
\r
2092 last_screensize = screensize;
\r
2093 screensize = driver->getScreenSize();
\r
2094 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2095 //bool screensize_changed = screensize != last_screensize;
\r
2097 // Hilight boxes collected during the loop and displayed
\r
2098 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2101 std::wstring infotext;
\r
2103 // When screen size changes, update positions and sizes of stuff
\r
2104 /*if(screensize_changed)
\r
2106 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2107 quick_inventory->updatePosition(pos);
\r
2110 //TimeTaker //timer1("//timer1");
\r
2112 // Time of frame without fps limit
\r
2116 // not using getRealTime is necessary for wine
\r
2117 u32 time = device->getTimer()->getTime();
\r
2118 if(time > lasttime)
\r
2119 busytime_u32 = time - lasttime;
\r
2122 busytime = busytime_u32 / 1000.0;
\r
2125 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2127 // Absolutelu necessary for wine!
\r
2134 updateViewingRange(busytime, &client);
\r
2141 float fps_max = g_settings.getFloat("fps_max");
\r
2142 u32 frametime_min = 1000./fps_max;
\r
2144 if(busytime_u32 < frametime_min)
\r
2146 u32 sleeptime = frametime_min - busytime_u32;
\r
2147 device->sleep(sleeptime);
\r
2151 // Absolutelu necessary for wine!
\r
2155 Time difference calculation
\r
2157 f32 dtime; // in seconds
\r
2159 u32 time = device->getTimer()->getTime();
\r
2160 if(time > lasttime)
\r
2161 dtime = (time - lasttime) / 1000.0;
\r
2167 Log frametime for visualization
\r
2169 frametime_log.push_back(dtime);
\r
2170 if(frametime_log.size() > 100)
\r
2172 core::list<float>::Iterator i = frametime_log.begin();
\r
2173 frametime_log.erase(i);
\r
2177 Visualize frametime in terminal
\r
2179 /*for(u32 i=0; i<dtime*400; i++)
\r
2181 std::cout<<std::endl;*/
\r
2184 Time average and jitter calculation
\r
2187 static f32 dtime_avg1 = 0.0;
\r
2188 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2189 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2191 static f32 dtime_jitter1_max_sample = 0.0;
\r
2192 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2194 static f32 jitter1_max = 0.0;
\r
2195 static f32 counter = 0.0;
\r
2196 if(dtime_jitter1 > jitter1_max)
\r
2197 jitter1_max = dtime_jitter1;
\r
2202 dtime_jitter1_max_sample = jitter1_max;
\r
2203 dtime_jitter1_max_fraction
\r
2204 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2205 jitter1_max = 0.0;
\r
2210 Busytime average and jitter calculation
\r
2213 static f32 busytime_avg1 = 0.0;
\r
2214 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2215 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2217 static f32 busytime_jitter1_max_sample = 0.0;
\r
2218 static f32 busytime_jitter1_min_sample = 0.0;
\r
2220 static f32 jitter1_max = 0.0;
\r
2221 static f32 jitter1_min = 0.0;
\r
2222 static f32 counter = 0.0;
\r
2223 if(busytime_jitter1 > jitter1_max)
\r
2224 jitter1_max = busytime_jitter1;
\r
2225 if(busytime_jitter1 < jitter1_min)
\r
2226 jitter1_min = busytime_jitter1;
\r
2228 if(counter > 0.0){
\r
2230 busytime_jitter1_max_sample = jitter1_max;
\r
2231 busytime_jitter1_min_sample = jitter1_min;
\r
2232 jitter1_max = 0.0;
\r
2233 jitter1_min = 0.0;
\r
2238 Debug info for client
\r
2241 static float counter = 0.0;
\r
2246 client.printDebugInfo(std::cout);
\r
2251 Input handler step()
\r
2253 g_input->step(dtime);
\r
2256 Player speed control
\r
2265 bool a_superspeed,
\r
2269 PlayerControl control(
\r
2270 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2271 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2272 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2273 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2274 g_input->isKeyDown(irr::KEY_SPACE),
\r
2275 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2276 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2277 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2281 client.setPlayerControl(control);
\r
2285 Process environment
\r
2289 //TimeTaker timer("client.step(dtime)");
\r
2290 client.step(dtime);
\r
2291 //client.step(dtime_avg1);
\r
2294 if(server != NULL)
\r
2296 //TimeTaker timer("server->step(dtime)");
\r
2297 server->step(dtime);
\r
2300 v3f player_position = client.getPlayerPosition();
\r
2302 //TimeTaker //timer2("//timer2");
\r
2305 Mouse and camera control
\r
2308 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2311 device->getCursorControl()->setVisible(false);
\r
2313 if(first_loop_after_window_activation){
\r
2314 //std::cout<<"window active, first loop"<<std::endl;
\r
2315 first_loop_after_window_activation = false;
\r
2318 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2319 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2320 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2321 camera_yaw -= dx*0.2;
\r
2322 camera_pitch += dy*0.2;
\r
2323 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2324 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2326 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2329 device->getCursorControl()->setVisible(true);
\r
2331 //std::cout<<"window inactive"<<std::endl;
\r
2332 first_loop_after_window_activation = true;
\r
2335 camera_yaw = wrapDegrees(camera_yaw);
\r
2336 camera_pitch = wrapDegrees(camera_pitch);
\r
2338 v3f camera_direction = v3f(0,0,1);
\r
2339 camera_direction.rotateYZBy(camera_pitch);
\r
2340 camera_direction.rotateXZBy(camera_yaw);
\r
2342 // This is at the height of the eyes of the current figure
\r
2343 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2344 // This is more like in minecraft
\r
2345 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2347 camera->setPosition(camera_position);
\r
2348 // *100.0 helps in large map coordinates
\r
2349 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2351 if(FIELD_OF_VIEW_TEST){
\r
2352 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2355 //TimeTaker timer("client.updateCamera");
\r
2356 client.updateCamera(camera_position, camera_direction);
\r
2360 //TimeTaker //timer3("//timer3");
\r
2363 Calculate what block is the crosshair pointing to
\r
2366 //u32 t1 = device->getTimer()->getRealTime();
\r
2368 //f32 d = 4; // max. distance
\r
2369 f32 d = 4; // max. distance
\r
2370 core::line3d<f32> shootline(camera_position,
\r
2371 camera_position + camera_direction * BS * (d+1));
\r
2373 MapBlockObject *selected_object = client.getSelectedObject
\r
2374 (d*BS, camera_position, shootline);
\r
2377 If it's pointing to a MapBlockObject
\r
2380 if(selected_object != NULL)
\r
2382 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2384 core::aabbox3d<f32> box_on_map
\r
2385 = selected_object->getSelectionBoxOnMap();
\r
2387 hilightboxes.push_back(box_on_map);
\r
2389 infotext = narrow_to_wide(selected_object->infoText());
\r
2391 if(g_input->getLeftClicked())
\r
2393 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2394 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2395 selected_object->getId(), g_selected_item);
\r
2397 else if(g_input->getRightClicked())
\r
2399 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2401 Check if we want to modify the object ourselves
\r
2403 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2405 dstream<<"Sign object right-clicked"<<std::endl;
\r
2407 if(random_input == false)
\r
2409 // Get a new text for it
\r
2411 TextDest *dest = new TextDestSign(
\r
2412 selected_object->getBlock()->getPos(),
\r
2413 selected_object->getId(),
\r
2416 SignObject *sign_object = (SignObject*)selected_object;
\r
2418 std::wstring wtext =
\r
2419 narrow_to_wide(sign_object->getText());
\r
2421 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2427 Otherwise pass the event to the server as-is
\r
2431 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2432 selected_object->getId(), g_selected_item);
\r
2436 else // selected_object == NULL
\r
2440 Find out which node we are pointing at
\r
2443 bool nodefound = false;
\r
2445 v3s16 neighbourpos;
\r
2446 core::aabbox3d<f32> nodehilightbox;
\r
2447 f32 mindistance = BS * 1001;
\r
2449 v3s16 pos_i = floatToInt(player_position, BS);
\r
2451 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2455 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2456 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2457 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2458 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2459 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2460 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2462 for(s16 y = ystart; y <= yend; y++)
\r
2463 for(s16 z = zstart; z <= zend; z++)
\r
2464 for(s16 x = xstart; x <= xend; x++)
\r
2469 n = client.getNode(v3s16(x,y,z));
\r
2470 if(content_pointable(n.d) == false)
\r
2473 catch(InvalidPositionException &e)
\r
2479 v3f npf = intToFloat(np, BS);
\r
2484 v3s16(0,0,1), // back
\r
2485 v3s16(0,1,0), // top
\r
2486 v3s16(1,0,0), // right
\r
2487 v3s16(0,0,-1), // front
\r
2488 v3s16(0,-1,0), // bottom
\r
2489 v3s16(-1,0,0), // left
\r
2495 if(n.d == CONTENT_TORCH)
\r
2497 v3s16 dir = unpackDir(n.dir);
\r
2498 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2499 dir_f *= BS/2 - BS/6 - BS/20;
\r
2500 v3f cpf = npf + dir_f;
\r
2501 f32 distance = (cpf - camera_position).getLength();
\r
2503 core::aabbox3d<f32> box;
\r
2506 if(dir == v3s16(0,-1,0))
\r
2508 box = core::aabbox3d<f32>(
\r
2509 npf - v3f(BS/6, BS/2, BS/6),
\r
2510 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2514 else if(dir == v3s16(0,1,0))
\r
2516 box = core::aabbox3d<f32>(
\r
2517 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2518 npf + v3f(BS/6, BS/2, BS/6)
\r
2524 box = core::aabbox3d<f32>(
\r
2525 cpf - v3f(BS/6, BS/3, BS/6),
\r
2526 cpf + v3f(BS/6, BS/3, BS/6)
\r
2530 if(distance < mindistance)
\r
2532 if(box.intersectsWithLine(shootline))
\r
2536 neighbourpos = np;
\r
2537 mindistance = distance;
\r
2538 nodehilightbox = box;
\r
2547 for(u16 i=0; i<6; i++)
\r
2549 v3f dir_f = v3f(dirs[i].X,
\r
2550 dirs[i].Y, dirs[i].Z);
\r
2551 v3f centerpoint = npf + dir_f * BS/2;
\r
2553 (centerpoint - camera_position).getLength();
\r
2555 if(distance < mindistance)
\r
2557 core::CMatrix4<f32> m;
\r
2558 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2560 // This is the back face
\r
2561 v3f corners[2] = {
\r
2562 v3f(BS/2, BS/2, BS/2),
\r
2563 v3f(-BS/2, -BS/2, BS/2+d)
\r
2566 for(u16 j=0; j<2; j++)
\r
2568 m.rotateVect(corners[j]);
\r
2569 corners[j] += npf;
\r
2572 core::aabbox3d<f32> facebox(corners[0]);
\r
2573 facebox.addInternalPoint(corners[1]);
\r
2575 if(facebox.intersectsWithLine(shootline))
\r
2579 neighbourpos = np + dirs[i];
\r
2580 mindistance = distance;
\r
2582 //nodehilightbox = facebox;
\r
2584 const float d = 0.502;
\r
2585 core::aabbox3d<f32> nodebox
\r
2586 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2587 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2588 nodebox.MinEdge += nodepos_f;
\r
2589 nodebox.MaxEdge += nodepos_f;
\r
2590 nodehilightbox = nodebox;
\r
2592 } // if distance < mindistance
\r
2594 } // regular block
\r
2597 static float nodig_delay_counter = 0.0;
\r
2601 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2603 static float dig_time = 0.0;
\r
2604 static u16 dig_index = 0;
\r
2606 // Visualize selection
\r
2608 hilightboxes.push_back(nodehilightbox);
\r
2612 if(g_input->getLeftReleased())
\r
2614 client.clearTempMod(nodepos);
\r
2618 if(nodig_delay_counter > 0.0)
\r
2620 nodig_delay_counter -= dtime;
\r
2624 if(nodepos != nodepos_old)
\r
2626 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2627 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2629 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2631 client.clearTempMod(nodepos_old);
\r
2636 if(g_input->getLeftClicked() ||
\r
2637 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2639 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2640 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2642 if(g_input->getLeftClicked())
\r
2644 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2646 if(g_input->getLeftState())
\r
2648 MapNode n = client.getNode(nodepos);
\r
2650 // Get tool name. Default is "" = bare hands
\r
2651 std::string toolname = "";
\r
2652 InventoryList *mlist = local_inventory.getList("main");
\r
2655 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2656 if(item && (std::string)item->getName() == "ToolItem")
\r
2658 ToolItem *titem = (ToolItem*)item;
\r
2659 toolname = titem->getToolName();
\r
2663 // Get digging properties for material and tool
\r
2664 u8 material = n.d;
\r
2665 DiggingProperties prop =
\r
2666 getDiggingProperties(material, toolname);
\r
2668 float dig_time_complete = 0.0;
\r
2670 if(prop.diggable == false)
\r
2672 /*dstream<<"Material "<<(int)material
\r
2673 <<" not diggable with \""
\r
2674 <<toolname<<"\""<<std::endl;*/
\r
2675 // I guess nobody will wait for this long
\r
2676 dig_time_complete = 10000000.0;
\r
2680 dig_time_complete = prop.time;
\r
2683 if(dig_time_complete >= 0.001)
\r
2685 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2686 * dig_time/dig_time_complete);
\r
2688 // This is for torches
\r
2691 dig_index = CRACK_ANIMATION_LENGTH;
\r
2694 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2696 //TimeTaker timer("client.setTempMod");
\r
2697 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2698 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2702 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2703 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2704 client.clearTempMod(nodepos);
\r
2705 client.removeNode(nodepos);
\r
2709 nodig_delay_counter = dig_time_complete
\r
2710 / (float)CRACK_ANIMATION_LENGTH;
\r
2712 // We don't want a corresponding delay to
\r
2713 // very time consuming nodes
\r
2714 if(nodig_delay_counter > 0.5)
\r
2716 nodig_delay_counter = 0.5;
\r
2718 // We want a slight delay to very little
\r
2719 // time consuming nodes
\r
2720 float mindelay = 0.15;
\r
2721 if(nodig_delay_counter < mindelay)
\r
2723 nodig_delay_counter = mindelay;
\r
2727 dig_time += dtime;
\r
2731 if(g_input->getRightClicked())
\r
2733 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2734 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2737 nodepos_old = nodepos;
\r
2742 } // selected_object == NULL
\r
2744 g_input->resetLeftClicked();
\r
2745 g_input->resetRightClicked();
\r
2747 if(g_input->getLeftReleased())
\r
2749 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2751 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2753 if(g_input->getRightReleased())
\r
2755 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2759 g_input->resetLeftReleased();
\r
2760 g_input->resetRightReleased();
\r
2763 Calculate stuff for drawing
\r
2766 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2768 u32 daynight_ratio = client.getDayNightRatio();
\r
2769 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2770 video::SColor bgcolor = video::SColor(
\r
2772 skycolor.getRed() * l / 255,
\r
2773 skycolor.getGreen() * l / 255,
\r
2774 skycolor.getBlue() * l / 255);
\r
2780 if(g_settings.getBool("enable_fog") == true)
\r
2782 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2783 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2784 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2785 if(draw_control.range_all)
\r
2786 range = 100000*BS;
\r
2790 video::EFT_FOG_LINEAR,
\r
2794 false, // pixel fog
\r
2795 false // range fog
\r
2802 video::EFT_FOG_LINEAR,
\r
2806 false, // pixel fog
\r
2807 false // range fog
\r
2813 Update gui stuff (0ms)
\r
2816 //TimeTaker guiupdatetimer("Gui updating");
\r
2819 static float drawtime_avg = 0;
\r
2820 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2821 static float beginscenetime_avg = 0;
\r
2822 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2823 static float scenetime_avg = 0;
\r
2824 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2825 static float endscenetime_avg = 0;
\r
2826 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2828 char temptext[300];
\r
2829 snprintf(temptext, 300, "Minetest-c55 ("
\r
2831 ", R: range_all=%i"
\r
2833 " drawtime=%.0f, beginscenetime=%.0f"
\r
2834 ", scenetime=%.0f, endscenetime=%.0f",
\r
2836 draw_control.range_all,
\r
2838 beginscenetime_avg,
\r
2843 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2847 char temptext[300];
\r
2848 snprintf(temptext, 300,
\r
2849 "(% .1f, % .1f, % .1f)"
\r
2850 " (% .3f < btime_jitter < % .3f"
\r
2851 ", dtime_jitter = % .1f %%"
\r
2852 ", v_range = %.1f)",
\r
2853 player_position.X/BS,
\r
2854 player_position.Y/BS,
\r
2855 player_position.Z/BS,
\r
2856 busytime_jitter1_min_sample,
\r
2857 busytime_jitter1_max_sample,
\r
2858 dtime_jitter1_max_fraction * 100.0,
\r
2859 draw_control.wanted_range
\r
2862 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2866 guitext_info->setText(infotext.c_str());
\r
2870 Get chat messages from client
\r
2873 // Get new messages
\r
2874 std::wstring message;
\r
2875 while(client.getChatMessage(message))
\r
2877 chat_lines.push_back(ChatLine(message));
\r
2878 /*if(chat_lines.size() > 6)
\r
2880 core::list<ChatLine>::Iterator
\r
2881 i = chat_lines.begin();
\r
2882 chat_lines.erase(i);
\r
2885 // Append them to form the whole static text and throw
\r
2886 // it to the gui element
\r
2887 std::wstring whole;
\r
2888 // This will correspond to the line number counted from
\r
2889 // top to bottom, from size-1 to 0
\r
2890 s16 line_number = chat_lines.size();
\r
2891 // Count of messages to be removed from the top
\r
2892 u16 to_be_removed_count = 0;
\r
2893 for(core::list<ChatLine>::Iterator
\r
2894 i = chat_lines.begin();
\r
2895 i != chat_lines.end(); i++)
\r
2897 // After this, line number is valid for this loop
\r
2900 (*i).age += dtime;
\r
2902 This results in a maximum age of 60*6 to the
\r
2903 lowermost line and a maximum of 6 lines
\r
2905 float allowed_age = (6-line_number) * 60.0;
\r
2907 if((*i).age > allowed_age)
\r
2909 to_be_removed_count++;
\r
2912 whole += (*i).text + L'\n';
\r
2914 for(u16 i=0; i<to_be_removed_count; i++)
\r
2916 core::list<ChatLine>::Iterator
\r
2917 it = chat_lines.begin();
\r
2918 chat_lines.erase(it);
\r
2920 guitext_chat->setText(whole.c_str());
\r
2922 // Update gui element size and position
\r
2924 /*core::rect<s32> rect(
\r
2926 screensize.Y - guitext_chat_pad_bottom
\r
2927 - text_height*chat_lines.size(),
\r
2928 screensize.X - 10,
\r
2929 screensize.Y - guitext_chat_pad_bottom
\r
2931 core::rect<s32> rect(
\r
2934 screensize.X - 10,
\r
2935 50 + text_height*chat_lines.size()
\r
2938 guitext_chat->setRelativePosition(rect);
\r
2940 if(chat_lines.size() == 0)
\r
2941 guitext_chat->setVisible(false);
\r
2943 guitext_chat->setVisible(true);
\r
2950 static u16 old_selected_item = 65535;
\r
2951 if(client.getLocalInventoryUpdated()
\r
2952 || g_selected_item != old_selected_item)
\r
2954 old_selected_item = g_selected_item;
\r
2955 //std::cout<<"Updating local inventory"<<std::endl;
\r
2956 client.getLocalInventory(local_inventory);
\r
2957 /*quick_inventory->setSelection(g_selected_item);
\r
2958 quick_inventory->update();*/
\r
2962 Send actions returned by the inventory menu
\r
2964 while(inventory_action_queue.size() != 0)
\r
2966 InventoryAction *a = inventory_action_queue.pop_front();
\r
2968 client.sendInventoryAction(a);
\r
2977 TimeTaker drawtimer("Drawing");
\r
2981 TimeTaker timer("beginScene");
\r
2982 driver->beginScene(true, true, bgcolor);
\r
2983 //driver->beginScene(false, true, bgcolor);
\r
2984 beginscenetime = timer.stop(true);
\r
2989 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2992 TimeTaker timer("smgr");
\r
2994 scenetime = timer.stop(true);
\r
2998 //TimeTaker timer9("auxiliary drawings");
\r
3002 //TimeTaker //timer10("//timer10");
\r
3004 video::SMaterial m;
\r
3005 //m.Thickness = 10;
\r
3007 m.Lighting = false;
\r
3008 driver->setMaterial(m);
\r
3010 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3012 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3013 i != hilightboxes.end(); i++)
\r
3015 /*std::cout<<"hilightbox min="
\r
3016 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3018 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3020 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3026 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3027 displaycenter + core::vector2d<s32>(10,0),
\r
3028 video::SColor(255,255,255,255));
\r
3029 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3030 displaycenter + core::vector2d<s32>(0,10),
\r
3031 video::SColor(255,255,255,255));
\r
3036 if(g_settings.getBool("frametime_graph") == true)
\r
3039 for(core::list<float>::Iterator
\r
3040 i = frametime_log.begin();
\r
3041 i != frametime_log.end();
\r
3044 driver->draw2DLine(v2s32(x,50),
\r
3045 v2s32(x,50+(*i)*1000),
\r
3046 video::SColor(255,255,255,255));
\r
3054 //TimeTaker //timer11("//timer11");
\r
3060 guienv->drawAll();
\r
3066 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3067 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3072 TimeTaker timer("endScene");
\r
3073 driver->endScene();
\r
3074 endscenetime = timer.stop(true);
\r
3077 drawtime = drawtimer.stop(true);
\r
3083 static s16 lastFPS = 0;
\r
3084 //u16 fps = driver->getFPS();
\r
3085 u16 fps = (1.0/dtime_avg1);
\r
3087 if (lastFPS != fps)
\r
3089 core::stringw str = L"Minetest [";
\r
3090 str += driver->getName();
\r
3094 device->setWindowCaption(str.c_str());
\r
3100 device->yield();*/
\r
3103 //delete quick_inventory;
\r
3106 Disable texture fetches and other stuff that is queued
\r
3107 to be processed by the main loop.
\r
3109 This has to be done before client goes out of scope.
\r
3111 g_irrlicht->Shutdown(true);
\r
3113 } // client and server are deleted at this point
\r
3116 catch(con::PeerNotFoundException &e)
\r
3118 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3119 error_message = L"Connection timed out.";
\r
3122 } // Menu-game loop
\r
3127 In the end, delete the Irrlicht device.
\r
3132 Update configuration file
\r
3134 /*if(configpath != "")
\r
3136 g_settings.updateConfigFile(configpath.c_str());
\r
3139 END_DEBUG_EXCEPTION_HANDLER
\r
3141 debugstreams_deinit();
\r