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
82 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
83 if something is already in it)
\r
84 - Use it in active block queue in water flowing
\r
86 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
87 - This is not doable because it is currently hand-made and not
\r
88 based on some mathematical function.
\r
90 SUGG: A version number to blocks, which increments when the block is
\r
91 modified (node add/remove, water update, lighting update)
\r
92 - This can then be used to make sure the most recent version of
\r
93 a block has been sent to client
\r
95 SUGG: Make the amount of blocks sending to client and the total
\r
96 amount of blocks dynamically limited. Transferring blocks is the
\r
97 main network eater of this system, so it is the one that has
\r
98 to be throttled so that RTTs stay low.
\r
100 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
101 different directions and then only those drawn that need to be
\r
102 - Also an 1-dimensional tile map would be nice probably
\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 NOTE: The following fixme is not apparently valid, and it does work.
\r
120 FIXME: Graphical mode seems to segfault with Irrlicht 1.7.1 on 64-bit
\r
122 - http://pastebin.no/32bo
\r
123 - Might be just a bad build, too
\r
124 - Doesn't affect Irrlicht 1.7.2 or 32-bit 1.7.1. (Arch/Debian)
\r
125 - A similar error occurs when getTexture is called from a thread
\r
126 when the texture has not been already loaded from disk:
\r
127 http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=68830
\r
129 FIXME: Some network errors on Windows that cause local game to not work
\r
130 - See siggjen's emails.
\r
132 Networking and serialization:
\r
133 -----------------------------
\r
135 TODO: Get rid of GotSplitPacketException
\r
140 TODO: Add gui option to remove map
\r
142 TODO: Configuration menu, at least for keys
\r
147 TODO: Optimize day/night mesh updating somehow
\r
148 - create copies of all textures for all lighting values and only
\r
149 change texture for material?
\r
150 - Umm... the collecting of the faces is the slow part
\r
151 -> what about just changing the color values of the existing
\r
152 meshbuffers? It should go quite fast.
\r
153 - This is not easy; There'd need to be a buffer somewhere
\r
154 that would contain the night and day lighting values.
\r
155 - Actually if FastFaces would be stored, they could
\r
158 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
160 - That is >500 vertices
\r
161 - This is not easy; all the MapBlocks close to the player would
\r
162 still need to be drawn separately and combining the blocks
\r
163 would have to happen in a background thread
\r
165 TODO: Make fetching sector's blocks more efficient when rendering
\r
166 sectors that have very large amounts of blocks (on client)
\r
167 - Is this necessary at all?
\r
169 TODO: Flowing water animation
\r
171 FIXME(FIXED): The new texture stuff is slow on wine
\r
172 - A basic grassy ground block takes 20-40ms
\r
173 - A bit more complicated block can take 270ms
\r
174 - On linux, a similar one doesn't take long at all (14ms)
\r
175 - It is NOT a bad std::string implementation of MSVC.
\r
176 - Can take up to 200ms? Is it when loading textures or always?
\r
177 - Updating excess amount of meshes when making footprints is too
\r
178 slow. It has to be fixed.
\r
179 -> implement Map::updateNodeMeshes()
\r
181 * Optimize TileSpec to only contain a reference number that
\r
182 is fast to compare, which refers to a cached string, or
\r
183 * Make TextureSpec for using instead of strings
\r
185 FIXME(FIXED): A lock condition is possible:
\r
186 1) MapBlock::updateMesh() is called from client asynchronously:
\r
187 - AsyncProcessData() -> Map::updateMeshes()
\r
188 2) Asynchronous locks m_temp_mods_mutex
\r
189 3) MapBlock::updateMesh() is called from client synchronously:
\r
190 - Client::step() -> Environment::step()
\r
191 4) Synchronous starts waiting for m_temp_mods_mutex
\r
192 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
197 TODO: Make the video backend selectable
\r
202 TODO: Untie client network operations from framerate
\r
203 - Needs some input queues or something
\r
204 - Not really necessary?
\r
206 TODO: Make morning and evening shorter
\r
208 TODO: Don't update all meshes always on single node changes, but
\r
209 check which ones should be updated
\r
210 - implement Map::updateNodeMeshes()
\r
215 TODO: When player dies, throw items on map
\r
217 TODO: Make an option to the server to disable building and digging near
\r
218 the starting position
\r
220 TODO: Save players with inventories to disk
\r
221 TODO: Players to be saved as text in map/players/<name>
\r
223 TODO: Copy the text of the last picked sign to inventory in creative
\r
226 TODO: Check what goes wrong with caching map to disk (Kray)
\r
229 TODO: When server sees that client is removing an inexistent block or
\r
230 adding a block to an existent position, resend the MapBlock.
\r
235 TODO: Better handling of objects and mobs
\r
237 - There has to be some way to do it with less messy code
\r
238 - Make separate classes for client and server
\r
239 - Client should not discriminate between blocks, server should
\r
240 - Make other players utilize the same framework
\r
241 - This is also needed for objects that don't get sent to client
\r
242 but are used for triggers etc
\r
244 TODO: There has to be some better way to handle static objects than to
\r
245 send them all the time. This affects signs and item objects.
\r
246 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
247 need an additional metadata field for the texts
\r
248 - This is also needed for item container chests
\r
250 Block object server side:
\r
251 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
252 - For all blocks in the buffer, objects are stepped(). This
\r
253 means they are active.
\r
254 - TODO: A global active buffer is needed for the server
\r
255 - TODO: A timestamp to blocks
\r
256 - TODO: All blocks going in and out of the buffer are recorded.
\r
257 - TODO: For outgoing blocks, timestamp is written.
\r
258 - TODO: For incoming blocks, time difference is calculated and
\r
259 objects are stepped according to it.
\r
264 NOTE: There are some lighting-related todos and fixmes in
\r
265 ServerMap::emergeBlock. And there always will be. 8)
\r
267 TODO: Mineral and ground material properties
\r
268 - This way mineral ground toughness can be calculated with just
\r
269 some formula, as well as tool strengths
\r
271 TODO: Change AttributeList to split the area into smaller sections so
\r
272 that searching won't be as heavy.
\r
274 TODO: Remove HMParams
\r
276 TODO: Flowing water to actually contain flow direction information
\r
278 TODO: Remove duplicate lighting implementation from Map (leave
\r
279 VoxelManipulator, which is faster)
\r
281 FEATURE: Map generator version 2
\r
282 - Create surface areas based on central points; a given point's
\r
283 area type is given by the nearest central point
\r
284 - Separate points for heightmap, caves, plants and minerals?
\r
285 - Flat land, mountains, forest, jungle
\r
287 - There could be a certain height (to which mountains only reach)
\r
288 where some minerals are found
\r
289 - Create a system that allows a huge amount of different "map
\r
290 generator modules/filters"
\r
292 FEATURE: The map could be generated procedually:
\r
293 - This would need the map to be generated in larger pieces
\r
294 - How large? How do they connect to each other?
\r
295 - It has to be split vertically also
\r
296 - Lighting would not have to be necessarily calculated until
\r
297 the blocks are actually needed - it would be quite fast
\r
298 - Something like 64*64*16 MapBlocks?
\r
299 - TODO: Separate lighting and block generation
\r
300 * Make the stone level with a heightmap
\r
301 * Carve out stuff in the stone
\r
302 * Dump dirt all around, and simulate it falling off steep
\r
304 * Erosion simulation at map generation time
\r
305 - Simulate water flows, which would carve out dirt fast and
\r
306 then turn stone into gravel and sand and relocate it.
\r
307 - How about relocating minerals, too? Coal and gold in
\r
308 downstream sand and gravel would be kind of cool
\r
309 - This would need a better way of handling minerals, mainly
\r
310 to have mineral content as a separate field. the first
\r
311 parameter field is free for this.
\r
312 - Simulate rock falling from cliffs when water has removed
\r
313 enough solid rock from the bottom
\r
314 TODO: Lazy lighting updates:
\r
315 - Set updateLighting to ignore MapBlocks with expired lighting,
\r
316 except the blocks specified to it
\r
317 - When a MapBlock is generated, lighting expires in all blocks
\r
318 touching it (26 blocks + self)
\r
319 - When a lighting-wise valid MapBlock is needed and lighting of it
\r
320 has expired, what to do?
\r
325 ======================================================================
\r
330 Setting this to 1 enables a special camera mode that forces
\r
331 the renderers to think that the camera statically points from
\r
332 the starting place to a static direction.
\r
334 This allows one to move around with the player and see what
\r
335 is actually drawn behind solid things and behind the player.
\r
337 #define FIELD_OF_VIEW_TEST 0
\r
341 #pragma message ("Disabling unit tests")
\r
343 #warning "Disabling unit tests"
\r
345 // Disable unit tests
\r
346 #define ENABLE_TESTS 0
\r
348 // Enable unit tests
\r
349 #define ENABLE_TESTS 1
\r
353 #pragma comment(lib, "Irrlicht.lib")
\r
354 //#pragma comment(lib, "jthread.lib")
\r
355 #pragma comment(lib, "zlibwapi.lib")
\r
356 #pragma comment(lib, "Shell32.lib")
\r
357 // This would get rid of the console window
\r
358 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
361 #include <iostream>
\r
363 #include <jmutexautolock.h>
\r
364 #include <locale.h>
\r
365 #include "common_irrlicht.h"
\r
368 #include "player.h"
\r
371 #include "environment.h"
\r
372 #include "server.h"
\r
373 #include "client.h"
\r
374 #include "serialization.h"
\r
375 #include "constants.h"
\r
376 #include "strfnd.h"
\r
377 #include "porting.h"
\r
378 #include "irrlichtwrapper.h"
\r
379 #include "gettime.h"
\r
380 #include "porting.h"
\r
381 #include "guiPauseMenu.h"
\r
382 #include "guiInventoryMenu.h"
\r
383 #include "guiTextInputMenu.h"
\r
384 #include "materials.h"
\r
385 #include "guiMessageMenu.h"
\r
386 #include "filesys.h"
\r
387 #include "config.h"
\r
388 #include "guiMainMenu.h"
\r
389 #include "mineral.h"
\r
391 IrrlichtWrapper *g_irrlicht;
\r
393 MapDrawControl draw_control;
\r
397 These are loaded from the config file.
\r
400 Settings g_settings;
\r
402 extern void set_default_settings();
\r
408 IrrlichtDevice *g_device = NULL;
\r
409 Client *g_client = NULL;
\r
415 gui::IGUIEnvironment* guienv = NULL;
\r
416 gui::IGUIStaticText *guiroot = NULL;
\r
418 class MainMenuManager : public IMenuManager
\r
421 virtual void createdMenu(GUIModalMenu *menu)
\r
423 for(core::list<GUIModalMenu*>::Iterator
\r
424 i = m_stack.begin();
\r
425 i != m_stack.end(); i++)
\r
427 assert(*i != menu);
\r
430 if(m_stack.size() != 0)
\r
431 (*m_stack.getLast())->setVisible(false);
\r
432 m_stack.push_back(menu);
\r
435 virtual void deletingMenu(GUIModalMenu *menu)
\r
437 // Remove all entries if there are duplicates
\r
438 bool removed_entry;
\r
440 removed_entry = false;
\r
441 for(core::list<GUIModalMenu*>::Iterator
\r
442 i = m_stack.begin();
\r
443 i != m_stack.end(); i++)
\r
448 removed_entry = true;
\r
452 }while(removed_entry);
\r
454 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
455 assert(*i == menu);
\r
456 m_stack.erase(i);*/
\r
458 if(m_stack.size() != 0)
\r
459 (*m_stack.getLast())->setVisible(true);
\r
464 return m_stack.size();
\r
467 core::list<GUIModalMenu*> m_stack;
\r
470 MainMenuManager g_menumgr;
\r
472 bool noMenuActive()
\r
474 return (g_menumgr.menuCount() == 0);
\r
477 bool g_disconnect_requested = false;
\r
479 class MainGameCallback : public IGameCallback
\r
482 virtual void exitToOS()
\r
484 g_device->closeDevice();
\r
487 virtual void disconnect()
\r
489 g_disconnect_requested = true;
\r
493 MainGameCallback g_gamecallback;
\r
495 // Inventory actions from the menu are buffered here before sending
\r
496 Queue<InventoryAction*> inventory_action_queue;
\r
497 // This is a copy of the inventory that the client's environment has
\r
498 Inventory local_inventory;
\r
500 u16 g_selected_item = 0;
\r
507 std::ostream *dout_con_ptr = &dummyout;
\r
508 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
509 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
510 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
511 //std::ostream *dout_con_ptr = &dstream;
\r
512 //std::ostream *derr_con_ptr = &dstream;
\r
515 std::ostream *dout_server_ptr = &dstream;
\r
516 std::ostream *derr_server_ptr = &dstream;
\r
519 std::ostream *dout_client_ptr = &dstream;
\r
520 std::ostream *derr_client_ptr = &dstream;
\r
523 gettime.h implementation
\r
529 Use irrlicht because it is more precise than porting.h's
\r
532 if(g_irrlicht == NULL)
\r
534 return g_irrlicht->getTime();
\r
541 struct TextDestSign : public TextDest
\r
543 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
545 m_blockpos = blockpos;
\r
549 void gotText(std::wstring text)
\r
551 std::string ntext = wide_to_narrow(text);
\r
552 dstream<<"Changing text of a sign object: "
\r
553 <<ntext<<std::endl;
\r
554 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
562 struct TextDestChat : public TextDest
\r
564 TextDestChat(Client *client)
\r
568 void gotText(std::wstring text)
\r
570 m_client->sendChatMessage(text);
\r
571 m_client->addChatMessage(text);
\r
577 class MyEventReceiver : public IEventReceiver
\r
580 // This is the one method that we have to implement
\r
581 virtual bool OnEvent(const SEvent& event)
\r
584 React to nothing here if a menu is active
\r
586 if(noMenuActive() == false)
\r
592 // Remember whether each key is down or up
\r
593 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
595 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
597 if(event.KeyInput.PressedDown)
\r
599 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
605 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
607 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
609 dstream<<DTIME<<"MyEventReceiver: "
\r
610 <<"Launching pause menu"<<std::endl;
\r
611 // It will delete itself by itself
\r
612 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
613 &g_menumgr))->drop();
\r
616 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
618 dstream<<DTIME<<"MyEventReceiver: "
\r
619 <<"Launching inventory"<<std::endl;
\r
620 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
621 &local_inventory, &inventory_action_queue,
\r
622 &g_menumgr))->drop();
\r
625 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
627 TextDest *dest = new TextDestChat(g_client);
\r
629 (new GUITextInputMenu(guienv, guiroot, -1,
\r
635 // Material selection
\r
636 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
638 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
641 g_selected_item = 0;
\r
642 dstream<<DTIME<<"Selected item: "
\r
643 <<g_selected_item<<std::endl;
\r
646 // Viewing range selection
\r
647 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
649 if(draw_control.range_all)
\r
651 draw_control.range_all = false;
\r
652 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
656 draw_control.range_all = true;
\r
657 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
661 // Print debug stacks
\r
662 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
664 dstream<<"-----------------------------------------"
\r
666 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
667 dstream<<"-----------------------------------------"
\r
669 debug_stacks_print();
\r
674 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
676 if(noMenuActive() == false)
\r
678 left_active = false;
\r
679 middle_active = false;
\r
680 right_active = false;
\r
684 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
685 left_active = event.MouseInput.isLeftPressed();
\r
686 middle_active = event.MouseInput.isMiddlePressed();
\r
687 right_active = event.MouseInput.isRightPressed();
\r
689 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
691 leftclicked = true;
\r
693 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
695 rightclicked = true;
\r
697 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
699 leftreleased = true;
\r
701 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
703 rightreleased = true;
\r
705 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
707 /*dstream<<"event.MouseInput.Wheel="
\r
708 <<event.MouseInput.Wheel<<std::endl;*/
\r
709 if(event.MouseInput.Wheel < 0)
\r
711 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
714 g_selected_item = 0;
\r
716 else if(event.MouseInput.Wheel > 0)
\r
718 if(g_selected_item > 0)
\r
721 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
730 // This is used to check whether a key is being held down
\r
731 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
733 return keyIsDown[keyCode];
\r
738 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
739 keyIsDown[i] = false;
\r
741 leftclicked = false;
\r
742 rightclicked = false;
\r
743 leftreleased = false;
\r
744 rightreleased = false;
\r
746 left_active = false;
\r
747 middle_active = false;
\r
748 right_active = false;
\r
759 bool rightreleased;
\r
762 bool middle_active;
\r
766 // We use this array to store the current state of each key
\r
767 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
770 IrrlichtDevice *m_device;
\r
779 virtual ~InputHandler()
\r
783 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
785 virtual v2s32 getMousePos() = 0;
\r
786 virtual void setMousePos(s32 x, s32 y) = 0;
\r
788 virtual bool getLeftState() = 0;
\r
789 virtual bool getRightState() = 0;
\r
791 virtual bool getLeftClicked() = 0;
\r
792 virtual bool getRightClicked() = 0;
\r
793 virtual void resetLeftClicked() = 0;
\r
794 virtual void resetRightClicked() = 0;
\r
796 virtual bool getLeftReleased() = 0;
\r
797 virtual bool getRightReleased() = 0;
\r
798 virtual void resetLeftReleased() = 0;
\r
799 virtual void resetRightReleased() = 0;
\r
801 virtual void step(float dtime) {};
\r
803 virtual void clear() {};
\r
806 InputHandler *g_input = NULL;
\r
808 class RealInputHandler : public InputHandler
\r
811 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
813 m_receiver(receiver)
\r
816 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
818 return m_receiver->IsKeyDown(keyCode);
\r
820 virtual v2s32 getMousePos()
\r
822 return m_device->getCursorControl()->getPosition();
\r
824 virtual void setMousePos(s32 x, s32 y)
\r
826 m_device->getCursorControl()->setPosition(x, y);
\r
829 virtual bool getLeftState()
\r
831 return m_receiver->left_active;
\r
833 virtual bool getRightState()
\r
835 return m_receiver->right_active;
\r
838 virtual bool getLeftClicked()
\r
840 return m_receiver->leftclicked;
\r
842 virtual bool getRightClicked()
\r
844 return m_receiver->rightclicked;
\r
846 virtual void resetLeftClicked()
\r
848 m_receiver->leftclicked = false;
\r
850 virtual void resetRightClicked()
\r
852 m_receiver->rightclicked = false;
\r
855 virtual bool getLeftReleased()
\r
857 return m_receiver->leftreleased;
\r
859 virtual bool getRightReleased()
\r
861 return m_receiver->rightreleased;
\r
863 virtual void resetLeftReleased()
\r
865 m_receiver->leftreleased = false;
\r
867 virtual void resetRightReleased()
\r
869 m_receiver->rightreleased = false;
\r
874 resetRightClicked();
\r
875 resetLeftClicked();
\r
878 IrrlichtDevice *m_device;
\r
879 MyEventReceiver *m_receiver;
\r
882 class RandomInputHandler : public InputHandler
\r
885 RandomInputHandler()
\r
887 leftclicked = false;
\r
888 rightclicked = false;
\r
889 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
890 keydown[i] = false;
\r
892 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
894 return keydown[keyCode];
\r
896 virtual v2s32 getMousePos()
\r
900 virtual void setMousePos(s32 x, s32 y)
\r
902 mousepos = v2s32(x,y);
\r
905 virtual bool getLeftState()
\r
909 virtual bool getRightState()
\r
914 virtual bool getLeftClicked()
\r
916 return leftclicked;
\r
918 virtual bool getRightClicked()
\r
920 return rightclicked;
\r
922 virtual void resetLeftClicked()
\r
924 leftclicked = false;
\r
926 virtual void resetRightClicked()
\r
928 rightclicked = false;
\r
931 virtual bool getLeftReleased()
\r
935 virtual bool getRightReleased()
\r
939 virtual void resetLeftReleased()
\r
942 virtual void resetRightReleased()
\r
946 virtual void step(float dtime)
\r
949 static float counter1 = 0;
\r
953 counter1 = 0.1*Rand(1,10);
\r
954 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
955 g_selected_material++;
\r
957 g_selected_material = 0;*/
\r
958 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
961 g_selected_item = 0;
\r
965 static float counter1 = 0;
\r
969 counter1 = 0.1*Rand(1, 40);
\r
970 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
974 static float counter1 = 0;
\r
978 counter1 = 0.1*Rand(1, 40);
\r
979 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
983 static float counter1 = 0;
\r
987 counter1 = 0.1*Rand(1, 40);
\r
988 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
992 static float counter1 = 0;
\r
996 counter1 = 0.1*Rand(1, 40);
\r
997 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1001 static float counter1 = 0;
\r
1002 counter1 -= dtime;
\r
1003 if(counter1 < 0.0)
\r
1005 counter1 = 0.1*Rand(1, 20);
\r
1006 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1010 static float counter1 = 0;
\r
1011 counter1 -= dtime;
\r
1012 if(counter1 < 0.0)
\r
1014 counter1 = 0.1*Rand(1, 30);
\r
1015 leftclicked = true;
\r
1019 static float counter1 = 0;
\r
1020 counter1 -= dtime;
\r
1021 if(counter1 < 0.0)
\r
1023 counter1 = 0.1*Rand(1, 20);
\r
1024 rightclicked = true;
\r
1027 mousepos += mousespeed;
\r
1030 s32 Rand(s32 min, s32 max)
\r
1032 return (myrand()%(max-min+1))+min;
\r
1035 bool keydown[KEY_KEY_CODES_COUNT];
\r
1039 bool rightclicked;
\r
1042 void updateViewingRange(f32 frametime_in, Client *client)
\r
1044 if(draw_control.range_all == true)
\r
1047 static f32 added_frametime = 0;
\r
1048 static s16 added_frames = 0;
\r
1050 added_frametime += frametime_in;
\r
1051 added_frames += 1;
\r
1053 // Actually this counter kind of sucks because frametime is busytime
\r
1054 static f32 counter = 0;
\r
1055 counter -= frametime_in;
\r
1061 /*dstream<<__FUNCTION_NAME
\r
1062 <<": Collected "<<added_frames<<" frames, total of "
\r
1063 <<added_frametime<<"s."<<std::endl;*/
\r
1065 /*dstream<<"draw_control.blocks_drawn="
\r
1066 <<draw_control.blocks_drawn
\r
1067 <<", draw_control.blocks_would_have_drawn="
\r
1068 <<draw_control.blocks_would_have_drawn
\r
1071 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1072 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1074 draw_control.wanted_min_range = range_min;
\r
1075 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1077 float block_draw_ratio = 1.0;
\r
1078 if(draw_control.blocks_would_have_drawn != 0)
\r
1080 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1081 / (float)draw_control.blocks_would_have_drawn;
\r
1084 // Calculate the average frametime in the case that all wanted
\r
1085 // blocks had been drawn
\r
1086 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1088 added_frametime = 0.0;
\r
1091 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1092 float wanted_frametime = 1.0 / wanted_fps;
\r
1094 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1095 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1097 // If needed frametime change is very small, just return
\r
1098 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1100 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1104 float range = draw_control.wanted_range;
\r
1105 float new_range = range;
\r
1107 static s16 range_old = 0;
\r
1108 static f32 frametime_old = 0;
\r
1110 float d_range = range - range_old;
\r
1111 f32 d_frametime = frametime - frametime_old;
\r
1112 // A sane default of 30ms per 50 nodes of range
\r
1113 static f32 time_per_range = 30. / 50;
\r
1116 time_per_range = d_frametime / d_range;
\r
1119 // The minimum allowed calculated frametime-range derivative:
\r
1120 // Practically this sets the maximum speed of changing the range.
\r
1121 // The lower this value, the higher the maximum changing speed.
\r
1122 // A low value here results in wobbly range (0.001)
\r
1123 // A high value here results in slow changing range (0.0025)
\r
1124 // SUGG: This could be dynamically adjusted so that when
\r
1125 // the camera is turning, this is lower
\r
1126 //float min_time_per_range = 0.0015;
\r
1127 float min_time_per_range = 0.0010;
\r
1128 //float min_time_per_range = 0.05 / range;
\r
1129 if(time_per_range < min_time_per_range)
\r
1131 time_per_range = min_time_per_range;
\r
1132 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1136 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1139 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1140 // Dampen the change a bit to kill oscillations
\r
1141 //wanted_range_change *= 0.9;
\r
1142 //wanted_range_change *= 0.75;
\r
1143 wanted_range_change *= 0.5;
\r
1144 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1146 // If needed range change is very small, just return
\r
1147 if(fabs(wanted_range_change) < 0.001)
\r
1149 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1153 new_range += wanted_range_change;
\r
1154 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1156 //float new_range_unclamped = new_range;
\r
1157 if(new_range < range_min)
\r
1158 new_range = range_min;
\r
1159 if(new_range > range_max)
\r
1160 new_range = range_max;
\r
1162 /*if(new_range != new_range_unclamped)
\r
1163 dstream<<", clamped to "<<new_range<<std::endl;
\r
1165 dstream<<std::endl;*/
\r
1167 draw_control.wanted_range = new_range;
\r
1169 range_old = new_range;
\r
1170 frametime_old = frametime;
\r
1173 class GUIQuickInventory : public IEventReceiver
\r
1176 GUIQuickInventory(
\r
1177 gui::IGUIEnvironment* env,
\r
1178 gui::IGUIElement* parent,
\r
1181 Inventory *inventory):
\r
1182 m_itemcount(itemcount),
\r
1183 m_inventory(inventory)
\r
1185 core::rect<s32> imgsize(0,0,48,48);
\r
1186 core::rect<s32> textsize(0,0,48,16);
\r
1187 v2s32 spacing(0, 64);
\r
1188 for(s32 i=0; i<m_itemcount; i++)
\r
1190 m_images.push_back(env->addImage(
\r
1191 imgsize + pos + spacing*i
\r
1193 m_images[i]->setScaleImage(true);
\r
1194 m_texts.push_back(env->addStaticText(
\r
1196 textsize + pos + spacing*i,
\r
1199 m_texts[i]->setBackgroundColor(
\r
1200 video::SColor(128,0,0,0));
\r
1201 m_texts[i]->setTextAlignment(
\r
1202 gui::EGUIA_CENTER,
\r
1203 gui::EGUIA_UPPERLEFT);
\r
1207 ~GUIQuickInventory()
\r
1209 for(u32 i=0; i<m_texts.size(); i++)
\r
1211 m_texts[i]->remove();
\r
1213 for(u32 i=0; i<m_images.size(); i++)
\r
1215 m_images[i]->remove();
\r
1219 virtual bool OnEvent(const SEvent& event)
\r
1224 void setSelection(s32 i)
\r
1233 start = m_selection - m_itemcount / 2;
\r
1235 InventoryList *mainlist = m_inventory->getList("main");
\r
1237 for(s32 i=0; i<m_itemcount; i++)
\r
1239 s32 j = i + start;
\r
1241 if(j > (s32)mainlist->getSize() - 1)
\r
1242 j -= mainlist->getSize();
\r
1244 j += mainlist->getSize();
\r
1246 InventoryItem *item = mainlist->getItem(j);
\r
1250 m_images[i]->setImage(NULL);
\r
1253 if(m_selection == j)
\r
1254 swprintf(t, 10, L"<-");
\r
1256 swprintf(t, 10, L"");
\r
1257 m_texts[i]->setText(t);
\r
1259 // The next ifs will segfault with a NULL pointer
\r
1264 m_images[i]->setImage(item->getImage());
\r
1267 if(m_selection == j)
\r
1268 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1270 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1271 m_texts[i]->setText(t);
\r
1277 core::array<gui::IGUIStaticText*> m_texts;
\r
1278 core::array<gui::IGUIImage*> m_images;
\r
1279 Inventory *m_inventory;
\r
1290 ChatLine(const std::wstring &a_text):
\r
1296 std::wstring text;
\r
1299 // These are defined global so that they're not optimized too much.
\r
1300 // Can't change them to volatile.
\r
1305 std::string tempstring;
\r
1306 std::string tempstring2;
\r
1311 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1312 TimeTaker timer("Testing std::string speed");
\r
1313 const u32 jj = 10000;
\r
1314 for(u32 j=0; j<jj; j++)
\r
1318 const u32 ii = 10;
\r
1319 for(u32 i=0; i<ii; i++){
\r
1320 tempstring2 += "asd";
\r
1322 for(u32 i=0; i<ii+1; i++){
\r
1323 tempstring += "asd";
\r
1324 if(tempstring == tempstring2)
\r
1330 dstream<<"All of the following tests should take around 100ms each."
\r
1334 TimeTaker timer("Testing floating-point conversion speed");
\r
1336 for(u32 i=0; i<4000000; i++){
\r
1343 TimeTaker timer("Testing floating-point vector speed");
\r
1345 tempv3f1 = v3f(1,2,3);
\r
1346 tempv3f2 = v3f(4,5,6);
\r
1347 for(u32 i=0; i<10000000; i++){
\r
1348 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1349 tempv3f2 += v3f(7,8,9);
\r
1354 TimeTaker timer("Testing core::map speed");
\r
1356 core::map<v2s16, f32> map1;
\r
1359 for(s16 y=0; y<ii; y++){
\r
1360 for(s16 x=0; x<ii; x++){
\r
1361 map1.insert(v2s16(x,y), tempf);
\r
1365 for(s16 y=ii-1; y>=0; y--){
\r
1366 for(s16 x=0; x<ii; x++){
\r
1367 tempf = map1[v2s16(x,y)];
\r
1373 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1374 TimeTaker timer("Testing mutex speed");
\r
1387 // Do at least 10ms
\r
1388 while(timer.getTime() < 10);
\r
1390 u32 dtime = timer.stop();
\r
1391 u32 per_ms = n / dtime;
\r
1392 std::cout<<"Done. "<<dtime<<"ms, "
\r
1393 <<per_ms<<"/ms"<<std::endl;
\r
1397 int main(int argc, char *argv[])
\r
1400 Parse command line
\r
1403 // List all allowed options
\r
1404 core::map<std::string, ValueSpec> allowed_options;
\r
1405 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1406 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1407 "Run server directly"));
\r
1408 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1409 "Load configuration from specified file"));
\r
1410 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1411 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1412 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1413 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1414 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1415 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1417 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1419 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1421 Settings cmd_args;
\r
1423 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1425 if(ret == false || cmd_args.getFlag("help"))
\r
1427 dstream<<"Allowed options:"<<std::endl;
\r
1428 for(core::map<std::string, ValueSpec>::Iterator
\r
1429 i = allowed_options.getIterator();
\r
1430 i.atEnd() == false; i++)
\r
1432 dstream<<" --"<<i.getNode()->getKey();
\r
1433 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1438 dstream<<" <value>";
\r
1440 dstream<<std::endl;
\r
1442 if(i.getNode()->getValue().help != NULL)
\r
1444 dstream<<" "<<i.getNode()->getValue().help
\r
1449 return cmd_args.getFlag("help") ? 0 : 1;
\r
1453 Low-level initialization
\r
1456 bool disable_stderr = false;
\r
1458 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1459 disable_stderr = true;
\r
1462 // Initialize debug streams
\r
1463 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1464 // Initialize debug stacks
\r
1465 debug_stacks_init();
\r
1467 DSTACK(__FUNCTION_NAME);
\r
1469 porting::initializePaths();
\r
1470 // Create user data directory
\r
1471 fs::CreateDir(porting::path_userdata);
\r
1473 // C-style stuff initialization
\r
1474 initializeMaterialProperties();
\r
1477 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1479 // Print startup message
\r
1480 dstream<<DTIME<<"minetest-c55"
\r
1481 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1482 <<", "<<BUILD_INFO
\r
1486 Basic initialization
\r
1489 // Initialize default settings
\r
1490 set_default_settings();
\r
1492 // Set locale. This is for forcing '.' as the decimal point.
\r
1493 std::locale::global(std::locale("C"));
\r
1494 // This enables printing all characters in bitmap font
\r
1495 setlocale(LC_CTYPE, "en_US");
\r
1497 // Initialize sockets
\r
1499 atexit(sockets_cleanup);
\r
1509 // Path of configuration file in use
\r
1510 std::string configpath = "";
\r
1512 if(cmd_args.exists("config"))
\r
1514 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1517 dstream<<"Could not read configuration from \""
\r
1518 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1521 configpath = cmd_args.get("config");
\r
1525 core::array<std::string> filenames;
\r
1526 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1527 #ifdef RUN_IN_PLACE
\r
1528 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1531 for(u32 i=0; i<filenames.size(); i++)
\r
1533 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1536 configpath = filenames[i];
\r
1542 // Initialize random seed
\r
1547 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1549 These are needed for unit tests at least.
\r
1552 IIrrlichtWrapper irrlicht_dummy;
\r
1554 init_mapnode(&irrlicht_dummy);
\r
1559 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1560 || cmd_args.getFlag("enable-unittests") == true)
\r
1565 // Read map parameters from settings
\r
1567 HMParams hm_params;
\r
1568 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1569 hm_params.randmax = g_settings.get("height_randmax");
\r
1570 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1571 hm_params.base = g_settings.get("height_base");*/
\r
1573 MapParams map_params;
\r
1574 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1575 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1583 if(cmd_args.exists("port"))
\r
1584 port = cmd_args.getU16("port");
\r
1585 else if(cmd_args.exists("port"))
\r
1586 port = g_settings.getU16("port");
\r
1589 std::string map_dir = porting::path_userdata+"/map";
\r
1590 if(cmd_args.exists("map-dir"))
\r
1591 map_dir = cmd_args.get("map-dir");
\r
1592 else if(g_settings.exists("map-dir"))
\r
1593 map_dir = g_settings.get("map-dir");
\r
1595 // Run dedicated server if asked to
\r
1596 if(cmd_args.getFlag("server"))
\r
1598 DSTACK("Dedicated server branch");
\r
1601 Server server(map_dir.c_str(), hm_params, map_params);
\r
1602 server.start(port);
\r
1605 dedicated_server_loop(server);
\r
1614 // Address to connect to
\r
1615 std::string address = "";
\r
1617 if(cmd_args.exists("address"))
\r
1619 address = cmd_args.get("address");
\r
1623 address = g_settings.get("address");
\r
1626 std::string playername = g_settings.get("name");
\r
1629 Resolution selection
\r
1632 bool fullscreen = false;
\r
1633 u16 screenW = g_settings.getU16("screenW");
\r
1634 u16 screenH = g_settings.getU16("screenH");
\r
1638 MyEventReceiver receiver;
\r
1640 video::E_DRIVER_TYPE driverType;
\r
1643 //driverType = video::EDT_DIRECT3D9;
\r
1644 driverType = video::EDT_OPENGL;
\r
1646 driverType = video::EDT_OPENGL;
\r
1647 //driverType = video::EDT_BURNINGSVIDEO;
\r
1650 // create device and exit if creation failed
\r
1652 IrrlichtDevice *device;
\r
1653 device = createDevice(driverType,
\r
1654 core::dimension2d<u32>(screenW, screenH),
\r
1655 16, fullscreen, false, false, &receiver);
\r
1658 return 1; // could not create selected driver.
\r
1660 g_device = device;
\r
1661 g_irrlicht = new IrrlichtWrapper(device);
\r
1664 Speed tests (done after irrlicht is loaded to get timer)
\r
1666 if(cmd_args.getFlag("speedtests"))
\r
1668 dstream<<"Running speed tests"<<std::endl;
\r
1673 device->setResizable(true);
\r
1675 bool random_input = g_settings.getBool("random_input")
\r
1676 || cmd_args.getFlag("random-input");
\r
1678 g_input = new RandomInputHandler();
\r
1680 g_input = new RealInputHandler(device, &receiver);
\r
1683 Continue initialization
\r
1686 video::IVideoDriver* driver = device->getVideoDriver();
\r
1689 This changes the minimum allowed number of vertices in a VBO
\r
1691 //driver->setMinHardwareBufferVertexCount(50);
\r
1693 scene::ISceneManager* smgr = device->getSceneManager();
\r
1695 guienv = device->getGUIEnvironment();
\r
1696 gui::IGUISkin* skin = guienv->getSkin();
\r
1697 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1699 skin->setFont(font);
\r
1701 dstream<<"WARNING: Font file was not found."
\r
1702 " Using default font."<<std::endl;
\r
1703 // If font was not found, this will get us one
\r
1704 font = skin->getFont();
\r
1707 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1708 dstream<<"text_height="<<text_height<<std::endl;
\r
1710 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1711 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1712 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1713 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1714 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1715 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1718 Preload some textures and stuff
\r
1721 init_content_inventory_texture_paths();
\r
1722 init_mapnode(g_irrlicht);
\r
1723 init_mineral(g_irrlicht);
\r
1730 We need some kind of a root node to be able to add
\r
1731 custom gui elements directly on the screen.
\r
1732 Otherwise they won't be automatically drawn.
\r
1734 guiroot = guienv->addStaticText(L"",
\r
1735 core::rect<s32>(0, 0, 10000, 10000));
\r
1737 // First line of debug text
\r
1738 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1740 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1742 // Second line of debug text
\r
1743 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1745 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1748 // At the middle of the screen
\r
1749 // Object infos are shown in this
\r
1750 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1752 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1756 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1758 core::rect<s32>(0,0,0,0),
\r
1760 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1761 core::list<ChatLine> chat_lines;
\r
1764 If an error occurs, this is set to something and the
\r
1765 menu-game loop is restarted. It is then displayed before
\r
1768 std::wstring error_message = L"";
\r
1773 while(g_device->run())
\r
1776 // This is used for catching disconnects
\r
1781 Out-of-game menu loop.
\r
1783 Loop quits when menu returns proper parameters.
\r
1787 // Cursor can be non-visible when coming from the game
\r
1788 device->getCursorControl()->setVisible(true);
\r
1789 // Some stuff are left to scene manager when coming from the game
\r
1790 // (map at least?)
\r
1792 // Reset or hide the debug gui texts
\r
1793 guitext->setText(L"Minetest-c55");
\r
1794 guitext2->setVisible(false);
\r
1795 guitext_info->setVisible(false);
\r
1796 guitext_chat->setVisible(false);
\r
1798 // Initialize menu data
\r
1799 MainMenuData menudata;
\r
1800 menudata.address = narrow_to_wide(address);
\r
1801 menudata.name = narrow_to_wide(playername);
\r
1802 menudata.port = narrow_to_wide(itos(port));
\r
1803 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1805 GUIMainMenu *menu =
\r
1806 new GUIMainMenu(guienv, guiroot, -1,
\r
1807 &g_menumgr, &menudata, &g_gamecallback);
\r
1808 menu->allowFocusRemoval(true);
\r
1810 if(error_message != L"")
\r
1812 GUIMessageMenu *menu2 =
\r
1813 new GUIMessageMenu(guienv, guiroot, -1,
\r
1814 &g_menumgr, error_message.c_str());
\r
1816 error_message = L"";
\r
1819 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1821 dstream<<"Created main menu"<<std::endl;
\r
1823 while(g_device->run())
\r
1825 // Run global IrrlichtWrapper's main thread processing stuff
\r
1826 g_irrlicht->Run();
\r
1828 if(menu->getStatus() == true)
\r
1831 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1832 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1833 guienv->drawAll();
\r
1834 driver->endScene();
\r
1837 // Break out of menu-game loop to shut down cleanly
\r
1838 if(g_device->run() == false)
\r
1841 dstream<<"Dropping main menu"<<std::endl;
\r
1845 // Delete map if requested
\r
1846 if(menudata.delete_map)
\r
1848 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1850 error_message = L"Delete failed";
\r
1854 playername = wide_to_narrow(menudata.name);
\r
1855 address = wide_to_narrow(menudata.address);
\r
1856 port = stoi(wide_to_narrow(menudata.port));
\r
1857 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1859 // Check for valid parameters, restart menu if invalid.
\r
1860 if(playername == "")
\r
1862 error_message = L"Name required.";
\r
1867 g_settings.set("name", playername);
\r
1868 g_settings.set("address", address);
\r
1869 g_settings.set("port", itos(port));
\r
1870 // Update configuration file
\r
1871 if(configpath != "")
\r
1872 g_settings.updateConfigFile(configpath.c_str());
\r
1874 // Continue to game
\r
1878 // Break out of menu-game loop to shut down cleanly
\r
1879 if(g_device->run() == false)
\r
1883 Make a scope here so that the client and the server and other
\r
1884 stuff gets removed when disconnected or the irrlicht device
\r
1890 Draw "Loading" screen
\r
1892 const wchar_t *text = L"Loading and connecting...";
\r
1893 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1894 core::vector2d<s32> textsize(300, text_height);
\r
1895 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1897 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1898 text, textrect, false, false);
\r
1899 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1901 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1902 guienv->drawAll();
\r
1903 driver->endScene();
\r
1905 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1909 SharedPtr will delete it when it goes out of scope.
\r
1911 SharedPtr<Server> server;
\r
1912 if(address == ""){
\r
1913 server = new Server(map_dir, hm_params, map_params);
\r
1914 server->start(port);
\r
1921 Client client(device, playername.c_str(), draw_control);
\r
1923 g_client = &client;
\r
1925 Address connect_address(0,0,0,0, port);
\r
1928 connect_address.Resolve("localhost");
\r
1930 connect_address.Resolve(address.c_str());
\r
1932 catch(ResolveError &e)
\r
1934 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1936 error_message = L"Couldn't resolve address";
\r
1937 gui_loadingtext->remove();
\r
1941 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1942 client.connect(connect_address);
\r
1945 while(client.connectedAndInitialized() == false)
\r
1948 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1949 guienv->drawAll();
\r
1950 driver->endScene();
\r
1952 // Update client and server
\r
1956 if(server != NULL)
\r
1957 server->step(0.1);
\r
1963 catch(con::PeerNotFoundException &e)
\r
1965 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1967 error_message = L"Connection timed out.";
\r
1968 gui_loadingtext->remove();
\r
1975 /*scene::ISceneNode* skybox;
\r
1976 skybox = smgr->addSkyBoxSceneNode(
\r
1977 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1978 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1979 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1980 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1981 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1982 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1985 Create the camera node
\r
1988 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1989 0, // Camera parent
\r
1990 v3f(BS*100, BS*2, BS*100), // Look from
\r
1991 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1995 if(camera == NULL)
\r
1998 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2000 camera->setFOV(FOV_ANGLE);
\r
2002 // Just so big a value that everything rendered is visible
\r
2003 camera->setFarValue(100000*BS);
\r
2005 f32 camera_yaw = 0; // "right/left"
\r
2006 f32 camera_pitch = 0; // "up/down"
\r
2012 gui_loadingtext->remove();
\r
2015 Add some gui stuff
\r
2018 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2019 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2021 // Test the text input system
\r
2022 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2024 /*GUIMessageMenu *menu =
\r
2025 new GUIMessageMenu(guienv, guiroot, -1,
\r
2030 // Launch pause menu
\r
2031 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2032 &g_menumgr))->drop();
\r
2035 guitext2->setVisible(true);
\r
2036 guitext_info->setVisible(true);
\r
2037 guitext_chat->setVisible(true);
\r
2040 Some statistics are collected in these
\r
2043 u32 beginscenetime = 0;
\r
2044 u32 scenetime = 0;
\r
2045 u32 endscenetime = 0;
\r
2048 //throw con::PeerNotFoundException("lol");
\r
2054 bool first_loop_after_window_activation = true;
\r
2056 // Time is in milliseconds
\r
2057 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2058 // NOTE: So we have to use getTime() and call run()s between them
\r
2059 u32 lasttime = device->getTimer()->getTime();
\r
2061 while(device->run())
\r
2063 if(g_disconnect_requested)
\r
2065 g_disconnect_requested = false;
\r
2070 Run global IrrlichtWrapper's main thread processing stuff
\r
2072 g_irrlicht->Run();
\r
2075 Random calculations
\r
2077 v2u32 screensize = driver->getScreenSize();
\r
2078 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2080 // Hilight boxes collected during the loop and displayed
\r
2081 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2084 std::wstring infotext;
\r
2086 //TimeTaker //timer1("//timer1");
\r
2088 // Time of frame without fps limit
\r
2092 // not using getRealTime is necessary for wine
\r
2093 u32 time = device->getTimer()->getTime();
\r
2094 if(time > lasttime)
\r
2095 busytime_u32 = time - lasttime;
\r
2098 busytime = busytime_u32 / 1000.0;
\r
2101 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2103 // Absolutelu necessary for wine!
\r
2110 updateViewingRange(busytime, &client);
\r
2117 float fps_max = g_settings.getFloat("fps_max");
\r
2118 u32 frametime_min = 1000./fps_max;
\r
2120 if(busytime_u32 < frametime_min)
\r
2122 u32 sleeptime = frametime_min - busytime_u32;
\r
2123 device->sleep(sleeptime);
\r
2127 // Absolutelu necessary for wine!
\r
2131 Time difference calculation
\r
2133 f32 dtime; // in seconds
\r
2135 u32 time = device->getTimer()->getTime();
\r
2136 if(time > lasttime)
\r
2137 dtime = (time - lasttime) / 1000.0;
\r
2143 Time average and jitter calculation
\r
2146 static f32 dtime_avg1 = 0.0;
\r
2147 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2148 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2150 static f32 dtime_jitter1_max_sample = 0.0;
\r
2151 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2153 static f32 jitter1_max = 0.0;
\r
2154 static f32 counter = 0.0;
\r
2155 if(dtime_jitter1 > jitter1_max)
\r
2156 jitter1_max = dtime_jitter1;
\r
2161 dtime_jitter1_max_sample = jitter1_max;
\r
2162 dtime_jitter1_max_fraction
\r
2163 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2164 jitter1_max = 0.0;
\r
2169 Busytime average and jitter calculation
\r
2172 static f32 busytime_avg1 = 0.0;
\r
2173 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2174 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2176 static f32 busytime_jitter1_max_sample = 0.0;
\r
2177 static f32 busytime_jitter1_min_sample = 0.0;
\r
2179 static f32 jitter1_max = 0.0;
\r
2180 static f32 jitter1_min = 0.0;
\r
2181 static f32 counter = 0.0;
\r
2182 if(busytime_jitter1 > jitter1_max)
\r
2183 jitter1_max = busytime_jitter1;
\r
2184 if(busytime_jitter1 < jitter1_min)
\r
2185 jitter1_min = busytime_jitter1;
\r
2187 if(counter > 0.0){
\r
2189 busytime_jitter1_max_sample = jitter1_max;
\r
2190 busytime_jitter1_min_sample = jitter1_min;
\r
2191 jitter1_max = 0.0;
\r
2192 jitter1_min = 0.0;
\r
2197 Debug info for client
\r
2200 static float counter = 0.0;
\r
2205 client.printDebugInfo(std::cout);
\r
2210 Input handler step()
\r
2212 g_input->step(dtime);
\r
2215 Player speed control
\r
2224 bool a_superspeed,
\r
2227 PlayerControl control(
\r
2228 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2229 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2230 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2231 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2232 g_input->isKeyDown(irr::KEY_SPACE),
\r
2233 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2237 client.setPlayerControl(control);
\r
2241 Process environment
\r
2245 //TimeTaker timer("client.step(dtime)");
\r
2246 client.step(dtime);
\r
2247 //client.step(dtime_avg1);
\r
2250 if(server != NULL)
\r
2252 //TimeTaker timer("server->step(dtime)");
\r
2253 server->step(dtime);
\r
2256 v3f player_position = client.getPlayerPosition();
\r
2258 //TimeTaker //timer2("//timer2");
\r
2261 Mouse and camera control
\r
2264 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2267 device->getCursorControl()->setVisible(false);
\r
2269 if(first_loop_after_window_activation){
\r
2270 //std::cout<<"window active, first loop"<<std::endl;
\r
2271 first_loop_after_window_activation = false;
\r
2274 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2275 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2276 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2277 camera_yaw -= dx*0.2;
\r
2278 camera_pitch += dy*0.2;
\r
2279 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2280 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2282 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2285 device->getCursorControl()->setVisible(true);
\r
2287 //std::cout<<"window inactive"<<std::endl;
\r
2288 first_loop_after_window_activation = true;
\r
2291 camera_yaw = wrapDegrees(camera_yaw);
\r
2292 camera_pitch = wrapDegrees(camera_pitch);
\r
2294 v3f camera_direction = v3f(0,0,1);
\r
2295 camera_direction.rotateYZBy(camera_pitch);
\r
2296 camera_direction.rotateXZBy(camera_yaw);
\r
2298 // This is at the height of the eyes of the current figure
\r
2299 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2300 // This is more like in minecraft
\r
2301 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2303 camera->setPosition(camera_position);
\r
2304 // *100.0 helps in large map coordinates
\r
2305 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2307 if(FIELD_OF_VIEW_TEST){
\r
2308 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2309 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2312 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2313 //TimeTaker timer("client.updateCamera");
\r
2314 client.updateCamera(camera_position, camera_direction);
\r
2318 //TimeTaker //timer3("//timer3");
\r
2321 Calculate what block is the crosshair pointing to
\r
2324 //u32 t1 = device->getTimer()->getRealTime();
\r
2326 //f32 d = 4; // max. distance
\r
2327 f32 d = 4; // max. distance
\r
2328 core::line3d<f32> shootline(camera_position,
\r
2329 camera_position + camera_direction * BS * (d+1));
\r
2331 MapBlockObject *selected_object = client.getSelectedObject
\r
2332 (d*BS, camera_position, shootline);
\r
2335 If it's pointing to a MapBlockObject
\r
2338 if(selected_object != NULL)
\r
2340 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2342 core::aabbox3d<f32> box_on_map
\r
2343 = selected_object->getSelectionBoxOnMap();
\r
2345 hilightboxes.push_back(box_on_map);
\r
2347 infotext = narrow_to_wide(selected_object->infoText());
\r
2349 if(g_input->getLeftClicked())
\r
2351 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2352 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2353 selected_object->getId(), g_selected_item);
\r
2355 else if(g_input->getRightClicked())
\r
2357 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2359 Check if we want to modify the object ourselves
\r
2361 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2363 dstream<<"Sign object right-clicked"<<std::endl;
\r
2365 if(random_input == false)
\r
2367 // Get a new text for it
\r
2369 TextDest *dest = new TextDestSign(
\r
2370 selected_object->getBlock()->getPos(),
\r
2371 selected_object->getId(),
\r
2374 SignObject *sign_object = (SignObject*)selected_object;
\r
2376 std::wstring wtext =
\r
2377 narrow_to_wide(sign_object->getText());
\r
2379 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2385 Otherwise pass the event to the server as-is
\r
2389 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2390 selected_object->getId(), g_selected_item);
\r
2394 else // selected_object == NULL
\r
2398 Find out which node we are pointing at
\r
2401 bool nodefound = false;
\r
2403 v3s16 neighbourpos;
\r
2404 core::aabbox3d<f32> nodehilightbox;
\r
2405 f32 mindistance = BS * 1001;
\r
2407 v3s16 pos_i = floatToInt(player_position);
\r
2409 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2413 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2414 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2415 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2416 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2417 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2418 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2420 for(s16 y = ystart; y <= yend; y++)
\r
2421 for(s16 z = zstart; z <= zend; z++)
\r
2422 for(s16 x = xstart; x <= xend; x++)
\r
2427 n = client.getNode(v3s16(x,y,z));
\r
2428 if(content_pointable(n.d) == false)
\r
2431 catch(InvalidPositionException &e)
\r
2437 v3f npf = intToFloat(np);
\r
2442 v3s16(0,0,1), // back
\r
2443 v3s16(0,1,0), // top
\r
2444 v3s16(1,0,0), // right
\r
2445 v3s16(0,0,-1), // front
\r
2446 v3s16(0,-1,0), // bottom
\r
2447 v3s16(-1,0,0), // left
\r
2453 if(n.d == CONTENT_TORCH)
\r
2455 v3s16 dir = unpackDir(n.dir);
\r
2456 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2457 dir_f *= BS/2 - BS/6 - BS/20;
\r
2458 v3f cpf = npf + dir_f;
\r
2459 f32 distance = (cpf - camera_position).getLength();
\r
2461 core::aabbox3d<f32> box;
\r
2464 if(dir == v3s16(0,-1,0))
\r
2466 box = core::aabbox3d<f32>(
\r
2467 npf - v3f(BS/6, BS/2, BS/6),
\r
2468 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2472 else if(dir == v3s16(0,1,0))
\r
2474 box = core::aabbox3d<f32>(
\r
2475 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2476 npf + v3f(BS/6, BS/2, BS/6)
\r
2482 box = core::aabbox3d<f32>(
\r
2483 cpf - v3f(BS/6, BS/3, BS/6),
\r
2484 cpf + v3f(BS/6, BS/3, BS/6)
\r
2488 if(distance < mindistance)
\r
2490 if(box.intersectsWithLine(shootline))
\r
2494 neighbourpos = np;
\r
2495 mindistance = distance;
\r
2496 nodehilightbox = box;
\r
2505 for(u16 i=0; i<6; i++)
\r
2507 v3f dir_f = v3f(dirs[i].X,
\r
2508 dirs[i].Y, dirs[i].Z);
\r
2509 v3f centerpoint = npf + dir_f * BS/2;
\r
2511 (centerpoint - camera_position).getLength();
\r
2513 if(distance < mindistance)
\r
2515 core::CMatrix4<f32> m;
\r
2516 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2518 // This is the back face
\r
2519 v3f corners[2] = {
\r
2520 v3f(BS/2, BS/2, BS/2),
\r
2521 v3f(-BS/2, -BS/2, BS/2+d)
\r
2524 for(u16 j=0; j<2; j++)
\r
2526 m.rotateVect(corners[j]);
\r
2527 corners[j] += npf;
\r
2530 core::aabbox3d<f32> facebox(corners[0]);
\r
2531 facebox.addInternalPoint(corners[1]);
\r
2533 if(facebox.intersectsWithLine(shootline))
\r
2537 neighbourpos = np + dirs[i];
\r
2538 mindistance = distance;
\r
2540 //nodehilightbox = facebox;
\r
2542 const float d = 0.502;
\r
2543 core::aabbox3d<f32> nodebox
\r
2544 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2545 v3f nodepos_f = intToFloat(nodepos);
\r
2546 nodebox.MinEdge += nodepos_f;
\r
2547 nodebox.MaxEdge += nodepos_f;
\r
2548 nodehilightbox = nodebox;
\r
2550 } // if distance < mindistance
\r
2552 } // regular block
\r
2555 static float nodig_delay_counter = 0.0;
\r
2559 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2561 static float dig_time = 0.0;
\r
2562 static u16 dig_index = 0;
\r
2564 // Visualize selection
\r
2566 hilightboxes.push_back(nodehilightbox);
\r
2570 if(g_input->getLeftReleased())
\r
2572 client.clearTempMod(nodepos);
\r
2576 if(nodig_delay_counter > 0.0)
\r
2578 nodig_delay_counter -= dtime;
\r
2582 if(nodepos != nodepos_old)
\r
2584 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2585 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2587 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2589 client.clearTempMod(nodepos_old);
\r
2594 if(g_input->getLeftClicked() ||
\r
2595 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2597 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2598 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2600 if(g_input->getLeftClicked())
\r
2602 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2604 if(g_input->getLeftState())
\r
2606 MapNode n = client.getNode(nodepos);
\r
2608 // Get tool name. Default is "" = bare hands
\r
2609 std::string toolname = "";
\r
2610 InventoryList *mlist = local_inventory.getList("main");
\r
2613 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2614 if(item && (std::string)item->getName() == "ToolItem")
\r
2616 ToolItem *titem = (ToolItem*)item;
\r
2617 toolname = titem->getToolName();
\r
2621 // Get digging properties for material and tool
\r
2622 u8 material = n.d;
\r
2623 DiggingProperties prop =
\r
2624 getDiggingProperties(material, toolname);
\r
2626 float dig_time_complete = 0.0;
\r
2628 if(prop.diggable == false)
\r
2630 /*dstream<<"Material "<<(int)material
\r
2631 <<" not diggable with \""
\r
2632 <<toolname<<"\""<<std::endl;*/
\r
2633 // I guess nobody will wait for this long
\r
2634 dig_time_complete = 10000000.0;
\r
2638 dig_time_complete = prop.time;
\r
2641 if(dig_time_complete >= 0.001)
\r
2643 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2644 * dig_time/dig_time_complete);
\r
2646 // This is for torches
\r
2649 dig_index = CRACK_ANIMATION_LENGTH;
\r
2652 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2654 //TimeTaker timer("client.setTempMod");
\r
2655 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2656 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2660 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2661 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2662 client.clearTempMod(nodepos);
\r
2663 client.removeNode(nodepos);
\r
2667 nodig_delay_counter = dig_time_complete
\r
2668 / (float)CRACK_ANIMATION_LENGTH;
\r
2670 // We don't want a corresponding delay to
\r
2671 // very time consuming nodes
\r
2672 if(nodig_delay_counter > 0.5)
\r
2674 nodig_delay_counter = 0.5;
\r
2676 // We want a slight delay to very little
\r
2677 // time consuming nodes
\r
2678 //float mindelay = 0.15;
\r
2679 float mindelay = 0.20;
\r
2680 if(nodig_delay_counter < mindelay)
\r
2682 nodig_delay_counter = mindelay;
\r
2686 dig_time += dtime;
\r
2690 if(g_input->getRightClicked())
\r
2692 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2693 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2696 nodepos_old = nodepos;
\r
2701 } // selected_object == NULL
\r
2703 g_input->resetLeftClicked();
\r
2704 g_input->resetRightClicked();
\r
2706 if(g_input->getLeftReleased())
\r
2708 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2710 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2712 if(g_input->getRightReleased())
\r
2714 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2718 g_input->resetLeftReleased();
\r
2719 g_input->resetRightReleased();
\r
2722 Calculate stuff for drawing
\r
2725 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2727 u32 daynight_ratio = client.getDayNightRatio();
\r
2728 /*video::SColor bgcolor = video::SColor(
\r
2730 skycolor.getRed() * daynight_ratio / 1000,
\r
2731 skycolor.getGreen() * daynight_ratio / 1000,
\r
2732 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2734 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2735 video::SColor bgcolor = video::SColor(
\r
2737 skycolor.getRed() * l / 255,
\r
2738 skycolor.getGreen() * l / 255,
\r
2739 skycolor.getBlue() * l / 255);
\r
2745 if(g_settings.getBool("enable_fog") == true)
\r
2747 f32 range = draw_control.wanted_range * BS;
\r
2748 if(draw_control.range_all)
\r
2749 range = 100000*BS;
\r
2753 video::EFT_FOG_LINEAR,
\r
2757 false, // pixel fog
\r
2758 false // range fog
\r
2764 Update gui stuff (0ms)
\r
2767 //TimeTaker guiupdatetimer("Gui updating");
\r
2770 wchar_t temptext[150];
\r
2772 static float drawtime_avg = 0;
\r
2773 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2774 static float beginscenetime_avg = 0;
\r
2775 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2776 static float scenetime_avg = 0;
\r
2777 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2778 static float endscenetime_avg = 0;
\r
2779 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2781 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2783 L", R: range_all=%i"
\r
2785 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2787 draw_control.range_all,
\r
2789 beginscenetime_avg,
\r
2794 guitext->setText(temptext);
\r
2798 wchar_t temptext[150];
\r
2799 swprintf(temptext, 150,
\r
2800 L"(% .1f, % .1f, % .1f)"
\r
2801 L" (% .3f < btime_jitter < % .3f"
\r
2802 L", dtime_jitter = % .1f %%"
\r
2803 L", v_range = %.1f)",
\r
2804 player_position.X/BS,
\r
2805 player_position.Y/BS,
\r
2806 player_position.Z/BS,
\r
2807 busytime_jitter1_min_sample,
\r
2808 busytime_jitter1_max_sample,
\r
2809 dtime_jitter1_max_fraction * 100.0,
\r
2810 draw_control.wanted_range
\r
2813 guitext2->setText(temptext);
\r
2817 guitext_info->setText(infotext.c_str());
\r
2821 Get chat messages from client
\r
2824 // Get new messages
\r
2825 std::wstring message;
\r
2826 while(client.getChatMessage(message))
\r
2828 chat_lines.push_back(ChatLine(message));
\r
2829 /*if(chat_lines.size() > 6)
\r
2831 core::list<ChatLine>::Iterator
\r
2832 i = chat_lines.begin();
\r
2833 chat_lines.erase(i);
\r
2836 // Append them to form the whole static text and throw
\r
2837 // it to the gui element
\r
2838 std::wstring whole;
\r
2839 // This will correspond to the line number counted from
\r
2840 // top to bottom, from size-1 to 0
\r
2841 s16 line_number = chat_lines.size();
\r
2842 // Count of messages to be removed from the top
\r
2843 u16 to_be_removed_count = 0;
\r
2844 for(core::list<ChatLine>::Iterator
\r
2845 i = chat_lines.begin();
\r
2846 i != chat_lines.end(); i++)
\r
2848 // After this, line number is valid for this loop
\r
2851 (*i).age += dtime;
\r
2853 This results in a maximum age of 60*6 to the
\r
2854 lowermost line and a maximum of 6 lines
\r
2856 float allowed_age = (6-line_number) * 60.0;
\r
2858 if((*i).age > allowed_age)
\r
2860 to_be_removed_count++;
\r
2863 whole += (*i).text + L'\n';
\r
2865 for(u16 i=0; i<to_be_removed_count; i++)
\r
2867 core::list<ChatLine>::Iterator
\r
2868 it = chat_lines.begin();
\r
2869 chat_lines.erase(it);
\r
2871 guitext_chat->setText(whole.c_str());
\r
2872 // Update gui element size and position
\r
2873 core::rect<s32> rect(
\r
2875 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2876 screensize.X - 10,
\r
2879 guitext_chat->setRelativePosition(rect);
\r
2881 if(chat_lines.size() == 0)
\r
2882 guitext_chat->setVisible(false);
\r
2884 guitext_chat->setVisible(true);
\r
2891 static u16 old_selected_item = 65535;
\r
2892 if(client.getLocalInventoryUpdated()
\r
2893 || g_selected_item != old_selected_item)
\r
2895 old_selected_item = g_selected_item;
\r
2896 //std::cout<<"Updating local inventory"<<std::endl;
\r
2897 client.getLocalInventory(local_inventory);
\r
2898 quick_inventory->setSelection(g_selected_item);
\r
2899 quick_inventory->update();
\r
2903 Send actions returned by the inventory menu
\r
2905 while(inventory_action_queue.size() != 0)
\r
2907 InventoryAction *a = inventory_action_queue.pop_front();
\r
2909 client.sendInventoryAction(a);
\r
2918 TimeTaker drawtimer("Drawing");
\r
2922 TimeTaker timer("beginScene");
\r
2923 driver->beginScene(true, true, bgcolor);
\r
2924 //driver->beginScene(false, true, bgcolor);
\r
2925 beginscenetime = timer.stop(true);
\r
2930 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2933 TimeTaker timer("smgr");
\r
2935 scenetime = timer.stop(true);
\r
2939 //TimeTaker timer9("auxiliary drawings");
\r
2943 //TimeTaker //timer10("//timer10");
\r
2945 video::SMaterial m;
\r
2946 //m.Thickness = 10;
\r
2948 m.Lighting = false;
\r
2949 driver->setMaterial(m);
\r
2951 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2953 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2954 i != hilightboxes.end(); i++)
\r
2956 /*std::cout<<"hilightbox min="
\r
2957 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2959 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2961 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2967 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2968 displaycenter + core::vector2d<s32>(10,0),
\r
2969 video::SColor(255,255,255,255));
\r
2970 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2971 displaycenter + core::vector2d<s32>(0,10),
\r
2972 video::SColor(255,255,255,255));
\r
2977 //TimeTaker //timer11("//timer11");
\r
2983 guienv->drawAll();
\r
2987 TimeTaker timer("endScene");
\r
2988 driver->endScene();
\r
2989 endscenetime = timer.stop(true);
\r
2992 drawtime = drawtimer.stop(true);
\r
2998 static s16 lastFPS = 0;
\r
2999 //u16 fps = driver->getFPS();
\r
3000 u16 fps = (1.0/dtime_avg1);
\r
3002 if (lastFPS != fps)
\r
3004 core::stringw str = L"Minetest [";
\r
3005 str += driver->getName();
\r
3009 device->setWindowCaption(str.c_str());
\r
3015 device->yield();*/
\r
3018 delete quick_inventory;
\r
3020 } // client and server are deleted at this point
\r
3023 catch(con::PeerNotFoundException &e)
\r
3025 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3026 error_message = L"Connection timed out.";
\r
3029 } // Menu-game loop
\r
3034 In the end, delete the Irrlicht device.
\r
3039 Update configuration file
\r
3041 /*if(configpath != "")
\r
3043 g_settings.updateConfigFile(configpath.c_str());
\r
3046 END_DEBUG_EXCEPTION_HANDLER
\r
3048 debugstreams_deinit();
\r