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 - No, MapSectors. And as much as it is efficient to do,
\r
300 64x64 might be too much.
\r
301 - FIXME: This is currently halfway done and the generator is
\r
303 * Make the stone level with a heightmap
\r
304 * Carve out stuff in the stone
\r
305 * Dump dirt all around, and simulate it falling off steep
\r
307 * Erosion simulation at map generation time
\r
308 - Simulate water flows, which would carve out dirt fast and
\r
309 then turn stone into gravel and sand and relocate it.
\r
310 - How about relocating minerals, too? Coal and gold in
\r
311 downstream sand and gravel would be kind of cool
\r
312 - This would need a better way of handling minerals, mainly
\r
313 to have mineral content as a separate field. the first
\r
314 parameter field is free for this.
\r
315 - Simulate rock falling from cliffs when water has removed
\r
316 enough solid rock from the bottom
\r
323 * Remove all kinds of systems that are made redundant by the new map
\r
325 - Sector heightmaps? At least they should be made redundant.
\r
327 * Do something about AttributeDatabase/List being too slow
\r
328 * Save chunk metadata on disk
\r
329 * Change water side textures so that buggy water doesn't look bad
\r
330 * Make server find the spawning place from the real map data, not from
\r
332 * only_from_disk doesn't work that well anymore
\r
333 * Make the generator to run in background and not blocking block
\r
334 placement and transfer
\r
335 * Fix the strange mineral occurences
\r
336 * When the map is generated and a place is found for the player, the
\r
337 first chunk is actually still volatile and will have stuff still
\r
338 changed after spawning, which creates a lot of glitches.
\r
339 - This is partly fixed by now allowing only 2-sector deeep
\r
340 modification of volatile chunks. But it should still be fixed?
\r
341 - How about checking that the neighbors are fully generated too and
\r
342 generate them when the middle piece is needed
\r
343 - This is very slow
\r
344 - How about just enabling changed_blocks properly
\r
345 - This is probably a good idea
\r
346 - The server has to make sure the spawn point is not at the
\r
347 changing borders of a chunk
\r
348 * Add some kind of erosion and other stuff that now is possible
\r
349 * Make client to fetch stuff asynchronously
\r
350 - Needs method SyncProcessData
\r
352 ======================================================================
\r
357 Setting this to 1 enables a special camera mode that forces
\r
358 the renderers to think that the camera statically points from
\r
359 the starting place to a static direction.
\r
361 This allows one to move around with the player and see what
\r
362 is actually drawn behind solid things and behind the player.
\r
364 #define FIELD_OF_VIEW_TEST 0
\r
368 #pragma message ("Disabling unit tests")
\r
370 #warning "Disabling unit tests"
\r
372 // Disable unit tests
\r
373 #define ENABLE_TESTS 0
\r
375 // Enable unit tests
\r
376 #define ENABLE_TESTS 1
\r
380 #pragma comment(lib, "Irrlicht.lib")
\r
381 //#pragma comment(lib, "jthread.lib")
\r
382 #pragma comment(lib, "zlibwapi.lib")
\r
383 #pragma comment(lib, "Shell32.lib")
\r
384 // This would get rid of the console window
\r
385 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
388 #include <iostream>
\r
390 #include <jmutexautolock.h>
\r
391 #include <locale.h>
\r
392 #include "common_irrlicht.h"
\r
395 #include "player.h"
\r
398 #include "environment.h"
\r
399 #include "server.h"
\r
400 #include "client.h"
\r
401 #include "serialization.h"
\r
402 #include "constants.h"
\r
403 #include "strfnd.h"
\r
404 #include "porting.h"
\r
405 #include "irrlichtwrapper.h"
\r
406 #include "gettime.h"
\r
407 #include "porting.h"
\r
408 #include "guiPauseMenu.h"
\r
409 #include "guiInventoryMenu.h"
\r
410 #include "guiTextInputMenu.h"
\r
411 #include "materials.h"
\r
412 #include "guiMessageMenu.h"
\r
413 #include "filesys.h"
\r
414 #include "config.h"
\r
415 #include "guiMainMenu.h"
\r
416 #include "mineral.h"
\r
418 IrrlichtWrapper *g_irrlicht;
\r
420 MapDrawControl draw_control;
\r
424 These are loaded from the config file.
\r
427 Settings g_settings;
\r
429 extern void set_default_settings();
\r
435 IrrlichtDevice *g_device = NULL;
\r
436 Client *g_client = NULL;
\r
442 gui::IGUIEnvironment* guienv = NULL;
\r
443 gui::IGUIStaticText *guiroot = NULL;
\r
445 class MainMenuManager : public IMenuManager
\r
448 virtual void createdMenu(GUIModalMenu *menu)
\r
450 for(core::list<GUIModalMenu*>::Iterator
\r
451 i = m_stack.begin();
\r
452 i != m_stack.end(); i++)
\r
454 assert(*i != menu);
\r
457 if(m_stack.size() != 0)
\r
458 (*m_stack.getLast())->setVisible(false);
\r
459 m_stack.push_back(menu);
\r
462 virtual void deletingMenu(GUIModalMenu *menu)
\r
464 // Remove all entries if there are duplicates
\r
465 bool removed_entry;
\r
467 removed_entry = false;
\r
468 for(core::list<GUIModalMenu*>::Iterator
\r
469 i = m_stack.begin();
\r
470 i != m_stack.end(); i++)
\r
475 removed_entry = true;
\r
479 }while(removed_entry);
\r
481 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
482 assert(*i == menu);
\r
483 m_stack.erase(i);*/
\r
485 if(m_stack.size() != 0)
\r
486 (*m_stack.getLast())->setVisible(true);
\r
491 return m_stack.size();
\r
494 core::list<GUIModalMenu*> m_stack;
\r
497 MainMenuManager g_menumgr;
\r
499 bool noMenuActive()
\r
501 return (g_menumgr.menuCount() == 0);
\r
504 bool g_disconnect_requested = false;
\r
506 class MainGameCallback : public IGameCallback
\r
509 virtual void exitToOS()
\r
511 g_device->closeDevice();
\r
514 virtual void disconnect()
\r
516 g_disconnect_requested = true;
\r
520 MainGameCallback g_gamecallback;
\r
522 // Inventory actions from the menu are buffered here before sending
\r
523 Queue<InventoryAction*> inventory_action_queue;
\r
524 // This is a copy of the inventory that the client's environment has
\r
525 Inventory local_inventory;
\r
527 u16 g_selected_item = 0;
\r
534 std::ostream *dout_con_ptr = &dummyout;
\r
535 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
536 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
537 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
538 //std::ostream *dout_con_ptr = &dstream;
\r
539 //std::ostream *derr_con_ptr = &dstream;
\r
542 std::ostream *dout_server_ptr = &dstream;
\r
543 std::ostream *derr_server_ptr = &dstream;
\r
546 std::ostream *dout_client_ptr = &dstream;
\r
547 std::ostream *derr_client_ptr = &dstream;
\r
550 gettime.h implementation
\r
556 Use irrlicht because it is more precise than porting.h's
\r
559 if(g_irrlicht == NULL)
\r
561 return g_irrlicht->getTime();
\r
568 struct TextDestSign : public TextDest
\r
570 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
572 m_blockpos = blockpos;
\r
576 void gotText(std::wstring text)
\r
578 std::string ntext = wide_to_narrow(text);
\r
579 dstream<<"Changing text of a sign object: "
\r
580 <<ntext<<std::endl;
\r
581 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
589 struct TextDestChat : public TextDest
\r
591 TextDestChat(Client *client)
\r
595 void gotText(std::wstring text)
\r
597 m_client->sendChatMessage(text);
\r
598 m_client->addChatMessage(text);
\r
604 class MyEventReceiver : public IEventReceiver
\r
607 // This is the one method that we have to implement
\r
608 virtual bool OnEvent(const SEvent& event)
\r
611 React to nothing here if a menu is active
\r
613 if(noMenuActive() == false)
\r
619 // Remember whether each key is down or up
\r
620 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
622 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
624 if(event.KeyInput.PressedDown)
\r
626 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
632 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
634 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
636 dstream<<DTIME<<"MyEventReceiver: "
\r
637 <<"Launching pause menu"<<std::endl;
\r
638 // It will delete itself by itself
\r
639 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
640 &g_menumgr))->drop();
\r
643 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
645 dstream<<DTIME<<"MyEventReceiver: "
\r
646 <<"Launching inventory"<<std::endl;
\r
647 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
648 &local_inventory, &inventory_action_queue,
\r
649 &g_menumgr))->drop();
\r
652 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
654 TextDest *dest = new TextDestChat(g_client);
\r
656 (new GUITextInputMenu(guienv, guiroot, -1,
\r
662 // Material selection
\r
663 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
665 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
668 g_selected_item = 0;
\r
669 dstream<<DTIME<<"Selected item: "
\r
670 <<g_selected_item<<std::endl;
\r
673 // Viewing range selection
\r
674 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
676 if(draw_control.range_all)
\r
678 draw_control.range_all = false;
\r
679 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
683 draw_control.range_all = true;
\r
684 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
688 // Print debug stacks
\r
689 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
691 dstream<<"-----------------------------------------"
\r
693 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
694 dstream<<"-----------------------------------------"
\r
696 debug_stacks_print();
\r
701 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
703 if(noMenuActive() == false)
\r
705 left_active = false;
\r
706 middle_active = false;
\r
707 right_active = false;
\r
711 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
712 left_active = event.MouseInput.isLeftPressed();
\r
713 middle_active = event.MouseInput.isMiddlePressed();
\r
714 right_active = event.MouseInput.isRightPressed();
\r
716 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
718 leftclicked = true;
\r
720 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
722 rightclicked = true;
\r
724 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
726 leftreleased = true;
\r
728 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
730 rightreleased = true;
\r
732 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
734 /*dstream<<"event.MouseInput.Wheel="
\r
735 <<event.MouseInput.Wheel<<std::endl;*/
\r
736 if(event.MouseInput.Wheel < 0)
\r
738 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
741 g_selected_item = 0;
\r
743 else if(event.MouseInput.Wheel > 0)
\r
745 if(g_selected_item > 0)
\r
748 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
757 // This is used to check whether a key is being held down
\r
758 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
760 return keyIsDown[keyCode];
\r
765 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
766 keyIsDown[i] = false;
\r
768 leftclicked = false;
\r
769 rightclicked = false;
\r
770 leftreleased = false;
\r
771 rightreleased = false;
\r
773 left_active = false;
\r
774 middle_active = false;
\r
775 right_active = false;
\r
786 bool rightreleased;
\r
789 bool middle_active;
\r
793 // We use this array to store the current state of each key
\r
794 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
797 IrrlichtDevice *m_device;
\r
806 virtual ~InputHandler()
\r
810 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
812 virtual v2s32 getMousePos() = 0;
\r
813 virtual void setMousePos(s32 x, s32 y) = 0;
\r
815 virtual bool getLeftState() = 0;
\r
816 virtual bool getRightState() = 0;
\r
818 virtual bool getLeftClicked() = 0;
\r
819 virtual bool getRightClicked() = 0;
\r
820 virtual void resetLeftClicked() = 0;
\r
821 virtual void resetRightClicked() = 0;
\r
823 virtual bool getLeftReleased() = 0;
\r
824 virtual bool getRightReleased() = 0;
\r
825 virtual void resetLeftReleased() = 0;
\r
826 virtual void resetRightReleased() = 0;
\r
828 virtual void step(float dtime) {};
\r
830 virtual void clear() {};
\r
833 InputHandler *g_input = NULL;
\r
835 class RealInputHandler : public InputHandler
\r
838 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
840 m_receiver(receiver)
\r
843 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
845 return m_receiver->IsKeyDown(keyCode);
\r
847 virtual v2s32 getMousePos()
\r
849 return m_device->getCursorControl()->getPosition();
\r
851 virtual void setMousePos(s32 x, s32 y)
\r
853 m_device->getCursorControl()->setPosition(x, y);
\r
856 virtual bool getLeftState()
\r
858 return m_receiver->left_active;
\r
860 virtual bool getRightState()
\r
862 return m_receiver->right_active;
\r
865 virtual bool getLeftClicked()
\r
867 return m_receiver->leftclicked;
\r
869 virtual bool getRightClicked()
\r
871 return m_receiver->rightclicked;
\r
873 virtual void resetLeftClicked()
\r
875 m_receiver->leftclicked = false;
\r
877 virtual void resetRightClicked()
\r
879 m_receiver->rightclicked = false;
\r
882 virtual bool getLeftReleased()
\r
884 return m_receiver->leftreleased;
\r
886 virtual bool getRightReleased()
\r
888 return m_receiver->rightreleased;
\r
890 virtual void resetLeftReleased()
\r
892 m_receiver->leftreleased = false;
\r
894 virtual void resetRightReleased()
\r
896 m_receiver->rightreleased = false;
\r
901 resetRightClicked();
\r
902 resetLeftClicked();
\r
905 IrrlichtDevice *m_device;
\r
906 MyEventReceiver *m_receiver;
\r
909 class RandomInputHandler : public InputHandler
\r
912 RandomInputHandler()
\r
914 leftclicked = false;
\r
915 rightclicked = false;
\r
916 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
917 keydown[i] = false;
\r
919 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
921 return keydown[keyCode];
\r
923 virtual v2s32 getMousePos()
\r
927 virtual void setMousePos(s32 x, s32 y)
\r
929 mousepos = v2s32(x,y);
\r
932 virtual bool getLeftState()
\r
936 virtual bool getRightState()
\r
941 virtual bool getLeftClicked()
\r
943 return leftclicked;
\r
945 virtual bool getRightClicked()
\r
947 return rightclicked;
\r
949 virtual void resetLeftClicked()
\r
951 leftclicked = false;
\r
953 virtual void resetRightClicked()
\r
955 rightclicked = false;
\r
958 virtual bool getLeftReleased()
\r
962 virtual bool getRightReleased()
\r
966 virtual void resetLeftReleased()
\r
969 virtual void resetRightReleased()
\r
973 virtual void step(float dtime)
\r
976 static float counter1 = 0;
\r
980 counter1 = 0.1*Rand(1,10);
\r
981 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
982 g_selected_material++;
\r
984 g_selected_material = 0;*/
\r
985 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
988 g_selected_item = 0;
\r
992 static float counter1 = 0;
\r
996 counter1 = 0.1*Rand(1, 40);
\r
997 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
1001 static float counter1 = 0;
\r
1002 counter1 -= dtime;
\r
1003 if(counter1 < 0.0)
\r
1005 counter1 = 0.1*Rand(1, 40);
\r
1006 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
1010 static float counter1 = 0;
\r
1011 counter1 -= dtime;
\r
1012 if(counter1 < 0.0)
\r
1014 counter1 = 0.1*Rand(1, 40);
\r
1015 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1019 static float counter1 = 0;
\r
1020 counter1 -= dtime;
\r
1021 if(counter1 < 0.0)
\r
1023 counter1 = 0.1*Rand(1, 40);
\r
1024 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1028 static float counter1 = 0;
\r
1029 counter1 -= dtime;
\r
1030 if(counter1 < 0.0)
\r
1032 counter1 = 0.1*Rand(1, 20);
\r
1033 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1037 static float counter1 = 0;
\r
1038 counter1 -= dtime;
\r
1039 if(counter1 < 0.0)
\r
1041 counter1 = 0.1*Rand(1, 30);
\r
1042 leftclicked = true;
\r
1046 static float counter1 = 0;
\r
1047 counter1 -= dtime;
\r
1048 if(counter1 < 0.0)
\r
1050 counter1 = 0.1*Rand(1, 20);
\r
1051 rightclicked = true;
\r
1054 mousepos += mousespeed;
\r
1057 s32 Rand(s32 min, s32 max)
\r
1059 return (myrand()%(max-min+1))+min;
\r
1062 bool keydown[KEY_KEY_CODES_COUNT];
\r
1066 bool rightclicked;
\r
1069 void updateViewingRange(f32 frametime_in, Client *client)
\r
1071 if(draw_control.range_all == true)
\r
1074 static f32 added_frametime = 0;
\r
1075 static s16 added_frames = 0;
\r
1077 added_frametime += frametime_in;
\r
1078 added_frames += 1;
\r
1080 // Actually this counter kind of sucks because frametime is busytime
\r
1081 static f32 counter = 0;
\r
1082 counter -= frametime_in;
\r
1088 /*dstream<<__FUNCTION_NAME
\r
1089 <<": Collected "<<added_frames<<" frames, total of "
\r
1090 <<added_frametime<<"s."<<std::endl;*/
\r
1092 /*dstream<<"draw_control.blocks_drawn="
\r
1093 <<draw_control.blocks_drawn
\r
1094 <<", draw_control.blocks_would_have_drawn="
\r
1095 <<draw_control.blocks_would_have_drawn
\r
1098 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1099 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1101 draw_control.wanted_min_range = range_min;
\r
1102 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1104 float block_draw_ratio = 1.0;
\r
1105 if(draw_control.blocks_would_have_drawn != 0)
\r
1107 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1108 / (float)draw_control.blocks_would_have_drawn;
\r
1111 // Calculate the average frametime in the case that all wanted
\r
1112 // blocks had been drawn
\r
1113 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1115 added_frametime = 0.0;
\r
1118 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1119 float wanted_frametime = 1.0 / wanted_fps;
\r
1121 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1122 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1124 // If needed frametime change is very small, just return
\r
1125 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1127 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1131 float range = draw_control.wanted_range;
\r
1132 float new_range = range;
\r
1134 static s16 range_old = 0;
\r
1135 static f32 frametime_old = 0;
\r
1137 float d_range = range - range_old;
\r
1138 f32 d_frametime = frametime - frametime_old;
\r
1139 // A sane default of 30ms per 50 nodes of range
\r
1140 static f32 time_per_range = 30. / 50;
\r
1143 time_per_range = d_frametime / d_range;
\r
1146 // The minimum allowed calculated frametime-range derivative:
\r
1147 // Practically this sets the maximum speed of changing the range.
\r
1148 // The lower this value, the higher the maximum changing speed.
\r
1149 // A low value here results in wobbly range (0.001)
\r
1150 // A high value here results in slow changing range (0.0025)
\r
1151 // SUGG: This could be dynamically adjusted so that when
\r
1152 // the camera is turning, this is lower
\r
1153 //float min_time_per_range = 0.0015;
\r
1154 float min_time_per_range = 0.0010;
\r
1155 //float min_time_per_range = 0.05 / range;
\r
1156 if(time_per_range < min_time_per_range)
\r
1158 time_per_range = min_time_per_range;
\r
1159 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1163 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1166 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1167 // Dampen the change a bit to kill oscillations
\r
1168 //wanted_range_change *= 0.9;
\r
1169 //wanted_range_change *= 0.75;
\r
1170 wanted_range_change *= 0.5;
\r
1171 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1173 // If needed range change is very small, just return
\r
1174 if(fabs(wanted_range_change) < 0.001)
\r
1176 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1180 new_range += wanted_range_change;
\r
1181 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1183 //float new_range_unclamped = new_range;
\r
1184 if(new_range < range_min)
\r
1185 new_range = range_min;
\r
1186 if(new_range > range_max)
\r
1187 new_range = range_max;
\r
1189 /*if(new_range != new_range_unclamped)
\r
1190 dstream<<", clamped to "<<new_range<<std::endl;
\r
1192 dstream<<std::endl;*/
\r
1194 draw_control.wanted_range = new_range;
\r
1196 range_old = new_range;
\r
1197 frametime_old = frametime;
\r
1200 class GUIQuickInventory : public IEventReceiver
\r
1203 GUIQuickInventory(
\r
1204 gui::IGUIEnvironment* env,
\r
1205 gui::IGUIElement* parent,
\r
1208 Inventory *inventory):
\r
1209 m_itemcount(itemcount),
\r
1210 m_inventory(inventory)
\r
1212 core::rect<s32> imgsize(0,0,48,48);
\r
1213 core::rect<s32> textsize(0,0,48,16);
\r
1214 v2s32 spacing(0, 64);
\r
1215 for(s32 i=0; i<m_itemcount; i++)
\r
1217 m_images.push_back(env->addImage(
\r
1218 imgsize + pos + spacing*i
\r
1220 m_images[i]->setScaleImage(true);
\r
1221 m_texts.push_back(env->addStaticText(
\r
1223 textsize + pos + spacing*i,
\r
1226 m_texts[i]->setBackgroundColor(
\r
1227 video::SColor(128,0,0,0));
\r
1228 m_texts[i]->setTextAlignment(
\r
1229 gui::EGUIA_CENTER,
\r
1230 gui::EGUIA_UPPERLEFT);
\r
1234 ~GUIQuickInventory()
\r
1236 for(u32 i=0; i<m_texts.size(); i++)
\r
1238 m_texts[i]->remove();
\r
1240 for(u32 i=0; i<m_images.size(); i++)
\r
1242 m_images[i]->remove();
\r
1246 virtual bool OnEvent(const SEvent& event)
\r
1251 void setSelection(s32 i)
\r
1260 start = m_selection - m_itemcount / 2;
\r
1262 InventoryList *mainlist = m_inventory->getList("main");
\r
1264 for(s32 i=0; i<m_itemcount; i++)
\r
1266 s32 j = i + start;
\r
1268 if(j > (s32)mainlist->getSize() - 1)
\r
1269 j -= mainlist->getSize();
\r
1271 j += mainlist->getSize();
\r
1273 InventoryItem *item = mainlist->getItem(j);
\r
1277 m_images[i]->setImage(NULL);
\r
1280 if(m_selection == j)
\r
1281 swprintf(t, 10, L"<-");
\r
1283 swprintf(t, 10, L"");
\r
1284 m_texts[i]->setText(t);
\r
1286 // The next ifs will segfault with a NULL pointer
\r
1291 m_images[i]->setImage(item->getImage());
\r
1294 if(m_selection == j)
\r
1295 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1297 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1298 m_texts[i]->setText(t);
\r
1304 core::array<gui::IGUIStaticText*> m_texts;
\r
1305 core::array<gui::IGUIImage*> m_images;
\r
1306 Inventory *m_inventory;
\r
1317 ChatLine(const std::wstring &a_text):
\r
1323 std::wstring text;
\r
1326 // These are defined global so that they're not optimized too much.
\r
1327 // Can't change them to volatile.
\r
1332 std::string tempstring;
\r
1333 std::string tempstring2;
\r
1338 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1339 TimeTaker timer("Testing std::string speed");
\r
1340 const u32 jj = 10000;
\r
1341 for(u32 j=0; j<jj; j++)
\r
1345 const u32 ii = 10;
\r
1346 for(u32 i=0; i<ii; i++){
\r
1347 tempstring2 += "asd";
\r
1349 for(u32 i=0; i<ii+1; i++){
\r
1350 tempstring += "asd";
\r
1351 if(tempstring == tempstring2)
\r
1357 dstream<<"All of the following tests should take around 100ms each."
\r
1361 TimeTaker timer("Testing floating-point conversion speed");
\r
1363 for(u32 i=0; i<4000000; i++){
\r
1370 TimeTaker timer("Testing floating-point vector speed");
\r
1372 tempv3f1 = v3f(1,2,3);
\r
1373 tempv3f2 = v3f(4,5,6);
\r
1374 for(u32 i=0; i<10000000; i++){
\r
1375 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1376 tempv3f2 += v3f(7,8,9);
\r
1381 TimeTaker timer("Testing core::map speed");
\r
1383 core::map<v2s16, f32> map1;
\r
1386 for(s16 y=0; y<ii; y++){
\r
1387 for(s16 x=0; x<ii; x++){
\r
1388 map1.insert(v2s16(x,y), tempf);
\r
1392 for(s16 y=ii-1; y>=0; y--){
\r
1393 for(s16 x=0; x<ii; x++){
\r
1394 tempf = map1[v2s16(x,y)];
\r
1400 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1401 TimeTaker timer("Testing mutex speed");
\r
1414 // Do at least 10ms
\r
1415 while(timer.getTime() < 10);
\r
1417 u32 dtime = timer.stop();
\r
1418 u32 per_ms = n / dtime;
\r
1419 std::cout<<"Done. "<<dtime<<"ms, "
\r
1420 <<per_ms<<"/ms"<<std::endl;
\r
1424 int main(int argc, char *argv[])
\r
1427 Parse command line
\r
1430 // List all allowed options
\r
1431 core::map<std::string, ValueSpec> allowed_options;
\r
1432 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1433 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1434 "Run server directly"));
\r
1435 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1436 "Load configuration from specified file"));
\r
1437 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1438 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1439 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1440 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1441 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1442 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1444 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1446 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1448 Settings cmd_args;
\r
1450 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1452 if(ret == false || cmd_args.getFlag("help"))
\r
1454 dstream<<"Allowed options:"<<std::endl;
\r
1455 for(core::map<std::string, ValueSpec>::Iterator
\r
1456 i = allowed_options.getIterator();
\r
1457 i.atEnd() == false; i++)
\r
1459 dstream<<" --"<<i.getNode()->getKey();
\r
1460 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1465 dstream<<" <value>";
\r
1467 dstream<<std::endl;
\r
1469 if(i.getNode()->getValue().help != NULL)
\r
1471 dstream<<" "<<i.getNode()->getValue().help
\r
1476 return cmd_args.getFlag("help") ? 0 : 1;
\r
1480 Low-level initialization
\r
1483 bool disable_stderr = false;
\r
1485 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1486 disable_stderr = true;
\r
1489 // Initialize debug streams
\r
1490 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1491 // Initialize debug stacks
\r
1492 debug_stacks_init();
\r
1494 DSTACK(__FUNCTION_NAME);
\r
1496 porting::initializePaths();
\r
1497 // Create user data directory
\r
1498 fs::CreateDir(porting::path_userdata);
\r
1500 // C-style stuff initialization
\r
1501 initializeMaterialProperties();
\r
1504 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1506 // Print startup message
\r
1507 dstream<<DTIME<<"minetest-c55"
\r
1508 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1509 <<", "<<BUILD_INFO
\r
1513 Basic initialization
\r
1516 // Initialize default settings
\r
1517 set_default_settings();
\r
1519 // Set locale. This is for forcing '.' as the decimal point.
\r
1520 std::locale::global(std::locale("C"));
\r
1521 // This enables printing all characters in bitmap font
\r
1522 setlocale(LC_CTYPE, "en_US");
\r
1524 // Initialize sockets
\r
1526 atexit(sockets_cleanup);
\r
1536 // Path of configuration file in use
\r
1537 std::string configpath = "";
\r
1539 if(cmd_args.exists("config"))
\r
1541 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1544 dstream<<"Could not read configuration from \""
\r
1545 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1548 configpath = cmd_args.get("config");
\r
1552 core::array<std::string> filenames;
\r
1553 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1554 #ifdef RUN_IN_PLACE
\r
1555 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1558 for(u32 i=0; i<filenames.size(); i++)
\r
1560 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1563 configpath = filenames[i];
\r
1569 // Initialize random seed
\r
1574 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1576 These are needed for unit tests at least.
\r
1579 IIrrlichtWrapper irrlicht_dummy;
\r
1581 init_mapnode(&irrlicht_dummy);
\r
1586 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1587 || cmd_args.getFlag("enable-unittests") == true)
\r
1592 // Read map parameters from settings
\r
1594 HMParams hm_params;
\r
1595 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1596 hm_params.randmax = g_settings.get("height_randmax");
\r
1597 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1598 hm_params.base = g_settings.get("height_base");*/
\r
1600 MapParams map_params;
\r
1601 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1602 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1610 if(cmd_args.exists("port"))
\r
1611 port = cmd_args.getU16("port");
\r
1612 else if(cmd_args.exists("port"))
\r
1613 port = g_settings.getU16("port");
\r
1616 std::string map_dir = porting::path_userdata+"/map";
\r
1617 if(cmd_args.exists("map-dir"))
\r
1618 map_dir = cmd_args.get("map-dir");
\r
1619 else if(g_settings.exists("map-dir"))
\r
1620 map_dir = g_settings.get("map-dir");
\r
1622 // Run dedicated server if asked to
\r
1623 if(cmd_args.getFlag("server"))
\r
1625 DSTACK("Dedicated server branch");
\r
1628 Server server(map_dir.c_str(), hm_params, map_params);
\r
1629 server.start(port);
\r
1632 dedicated_server_loop(server);
\r
1641 // Address to connect to
\r
1642 std::string address = "";
\r
1644 if(cmd_args.exists("address"))
\r
1646 address = cmd_args.get("address");
\r
1650 address = g_settings.get("address");
\r
1653 std::string playername = g_settings.get("name");
\r
1656 Resolution selection
\r
1659 bool fullscreen = false;
\r
1660 u16 screenW = g_settings.getU16("screenW");
\r
1661 u16 screenH = g_settings.getU16("screenH");
\r
1665 MyEventReceiver receiver;
\r
1667 video::E_DRIVER_TYPE driverType;
\r
1670 //driverType = video::EDT_DIRECT3D9;
\r
1671 driverType = video::EDT_OPENGL;
\r
1673 driverType = video::EDT_OPENGL;
\r
1674 //driverType = video::EDT_BURNINGSVIDEO;
\r
1677 // create device and exit if creation failed
\r
1679 IrrlichtDevice *device;
\r
1680 device = createDevice(driverType,
\r
1681 core::dimension2d<u32>(screenW, screenH),
\r
1682 16, fullscreen, false, false, &receiver);
\r
1685 return 1; // could not create selected driver.
\r
1687 g_device = device;
\r
1688 g_irrlicht = new IrrlichtWrapper(device);
\r
1691 Speed tests (done after irrlicht is loaded to get timer)
\r
1693 if(cmd_args.getFlag("speedtests"))
\r
1695 dstream<<"Running speed tests"<<std::endl;
\r
1700 device->setResizable(true);
\r
1702 bool random_input = g_settings.getBool("random_input")
\r
1703 || cmd_args.getFlag("random-input");
\r
1705 g_input = new RandomInputHandler();
\r
1707 g_input = new RealInputHandler(device, &receiver);
\r
1710 Continue initialization
\r
1713 video::IVideoDriver* driver = device->getVideoDriver();
\r
1716 This changes the minimum allowed number of vertices in a VBO
\r
1718 //driver->setMinHardwareBufferVertexCount(50);
\r
1720 scene::ISceneManager* smgr = device->getSceneManager();
\r
1722 guienv = device->getGUIEnvironment();
\r
1723 gui::IGUISkin* skin = guienv->getSkin();
\r
1724 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1726 skin->setFont(font);
\r
1728 dstream<<"WARNING: Font file was not found."
\r
1729 " Using default font."<<std::endl;
\r
1730 // If font was not found, this will get us one
\r
1731 font = skin->getFont();
\r
1734 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1735 dstream<<"text_height="<<text_height<<std::endl;
\r
1737 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1738 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1739 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1740 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1741 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1742 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1745 Preload some textures and stuff
\r
1748 init_content_inventory_texture_paths();
\r
1749 init_mapnode(g_irrlicht);
\r
1750 init_mineral(g_irrlicht);
\r
1757 We need some kind of a root node to be able to add
\r
1758 custom gui elements directly on the screen.
\r
1759 Otherwise they won't be automatically drawn.
\r
1761 guiroot = guienv->addStaticText(L"",
\r
1762 core::rect<s32>(0, 0, 10000, 10000));
\r
1764 // First line of debug text
\r
1765 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1767 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1769 // Second line of debug text
\r
1770 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1772 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1775 // At the middle of the screen
\r
1776 // Object infos are shown in this
\r
1777 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1779 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1783 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1785 core::rect<s32>(0,0,0,0),
\r
1787 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1788 core::list<ChatLine> chat_lines;
\r
1791 If an error occurs, this is set to something and the
\r
1792 menu-game loop is restarted. It is then displayed before
\r
1795 std::wstring error_message = L"";
\r
1800 while(g_device->run())
\r
1803 // This is used for catching disconnects
\r
1808 Out-of-game menu loop.
\r
1810 Loop quits when menu returns proper parameters.
\r
1814 // Cursor can be non-visible when coming from the game
\r
1815 device->getCursorControl()->setVisible(true);
\r
1816 // Some stuff are left to scene manager when coming from the game
\r
1817 // (map at least?)
\r
1819 // Reset or hide the debug gui texts
\r
1820 guitext->setText(L"Minetest-c55");
\r
1821 guitext2->setVisible(false);
\r
1822 guitext_info->setVisible(false);
\r
1823 guitext_chat->setVisible(false);
\r
1825 // Initialize menu data
\r
1826 MainMenuData menudata;
\r
1827 menudata.address = narrow_to_wide(address);
\r
1828 menudata.name = narrow_to_wide(playername);
\r
1829 menudata.port = narrow_to_wide(itos(port));
\r
1830 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1832 GUIMainMenu *menu =
\r
1833 new GUIMainMenu(guienv, guiroot, -1,
\r
1834 &g_menumgr, &menudata, &g_gamecallback);
\r
1835 menu->allowFocusRemoval(true);
\r
1837 if(error_message != L"")
\r
1839 GUIMessageMenu *menu2 =
\r
1840 new GUIMessageMenu(guienv, guiroot, -1,
\r
1841 &g_menumgr, error_message.c_str());
\r
1843 error_message = L"";
\r
1846 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1848 dstream<<"Created main menu"<<std::endl;
\r
1850 while(g_device->run())
\r
1852 // Run global IrrlichtWrapper's main thread processing stuff
\r
1853 g_irrlicht->Run();
\r
1855 if(menu->getStatus() == true)
\r
1858 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1859 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1860 guienv->drawAll();
\r
1861 driver->endScene();
\r
1864 // Break out of menu-game loop to shut down cleanly
\r
1865 if(g_device->run() == false)
\r
1868 dstream<<"Dropping main menu"<<std::endl;
\r
1872 // Delete map if requested
\r
1873 if(menudata.delete_map)
\r
1875 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1877 error_message = L"Delete failed";
\r
1881 playername = wide_to_narrow(menudata.name);
\r
1882 address = wide_to_narrow(menudata.address);
\r
1883 port = stoi(wide_to_narrow(menudata.port));
\r
1884 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1886 // Check for valid parameters, restart menu if invalid.
\r
1887 if(playername == "")
\r
1889 error_message = L"Name required.";
\r
1894 g_settings.set("name", playername);
\r
1895 g_settings.set("address", address);
\r
1896 g_settings.set("port", itos(port));
\r
1897 // Update configuration file
\r
1898 if(configpath != "")
\r
1899 g_settings.updateConfigFile(configpath.c_str());
\r
1901 // Continue to game
\r
1905 // Break out of menu-game loop to shut down cleanly
\r
1906 if(g_device->run() == false)
\r
1910 Make a scope here so that the client and the server and other
\r
1911 stuff gets removed when disconnected or the irrlicht device
\r
1916 // This is set to true at the end of the scope
\r
1917 g_irrlicht->Shutdown(false);
\r
1920 Draw "Loading" screen
\r
1922 const wchar_t *text = L"Loading and connecting...";
\r
1923 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1924 core::vector2d<s32> textsize(300, text_height);
\r
1925 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1927 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1928 text, textrect, false, false);
\r
1929 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1931 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1932 guienv->drawAll();
\r
1933 driver->endScene();
\r
1935 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1939 SharedPtr will delete it when it goes out of scope.
\r
1941 SharedPtr<Server> server;
\r
1942 if(address == ""){
\r
1943 server = new Server(map_dir, hm_params, map_params);
\r
1944 server->start(port);
\r
1951 Client client(device, playername.c_str(), draw_control);
\r
1953 g_client = &client;
\r
1955 Address connect_address(0,0,0,0, port);
\r
1958 connect_address.Resolve("localhost");
\r
1960 connect_address.Resolve(address.c_str());
\r
1962 catch(ResolveError &e)
\r
1964 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1966 error_message = L"Couldn't resolve address";
\r
1967 gui_loadingtext->remove();
\r
1971 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1972 client.connect(connect_address);
\r
1975 while(client.connectedAndInitialized() == false)
\r
1978 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1979 guienv->drawAll();
\r
1980 driver->endScene();
\r
1982 // Update client and server
\r
1986 if(server != NULL)
\r
1987 server->step(0.1);
\r
1993 catch(con::PeerNotFoundException &e)
\r
1995 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1997 error_message = L"Connection timed out.";
\r
1998 gui_loadingtext->remove();
\r
2005 /*scene::ISceneNode* skybox;
\r
2006 skybox = smgr->addSkyBoxSceneNode(
\r
2007 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2008 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2009 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2010 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2011 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2012 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2015 Create the camera node
\r
2018 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2019 0, // Camera parent
\r
2020 v3f(BS*100, BS*2, BS*100), // Look from
\r
2021 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2025 if(camera == NULL)
\r
2028 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2030 camera->setFOV(FOV_ANGLE);
\r
2032 // Just so big a value that everything rendered is visible
\r
2033 camera->setFarValue(100000*BS);
\r
2035 f32 camera_yaw = 0; // "right/left"
\r
2036 f32 camera_pitch = 0; // "up/down"
\r
2042 gui_loadingtext->remove();
\r
2045 Add some gui stuff
\r
2048 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2049 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2051 // Test the text input system
\r
2052 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2054 /*GUIMessageMenu *menu =
\r
2055 new GUIMessageMenu(guienv, guiroot, -1,
\r
2060 // Launch pause menu
\r
2061 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2062 &g_menumgr))->drop();
\r
2065 guitext2->setVisible(true);
\r
2066 guitext_info->setVisible(true);
\r
2067 guitext_chat->setVisible(true);
\r
2070 Some statistics are collected in these
\r
2073 u32 beginscenetime = 0;
\r
2074 u32 scenetime = 0;
\r
2075 u32 endscenetime = 0;
\r
2078 //throw con::PeerNotFoundException("lol");
\r
2084 bool first_loop_after_window_activation = true;
\r
2086 // Time is in milliseconds
\r
2087 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2088 // NOTE: So we have to use getTime() and call run()s between them
\r
2089 u32 lasttime = device->getTimer()->getTime();
\r
2091 while(device->run())
\r
2093 if(g_disconnect_requested)
\r
2095 g_disconnect_requested = false;
\r
2100 Run global IrrlichtWrapper's main thread processing stuff
\r
2102 g_irrlicht->Run();
\r
2105 Random calculations
\r
2107 v2u32 screensize = driver->getScreenSize();
\r
2108 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2110 // Hilight boxes collected during the loop and displayed
\r
2111 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2114 std::wstring infotext;
\r
2116 //TimeTaker //timer1("//timer1");
\r
2118 // Time of frame without fps limit
\r
2122 // not using getRealTime is necessary for wine
\r
2123 u32 time = device->getTimer()->getTime();
\r
2124 if(time > lasttime)
\r
2125 busytime_u32 = time - lasttime;
\r
2128 busytime = busytime_u32 / 1000.0;
\r
2131 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2133 // Absolutelu necessary for wine!
\r
2140 updateViewingRange(busytime, &client);
\r
2147 float fps_max = g_settings.getFloat("fps_max");
\r
2148 u32 frametime_min = 1000./fps_max;
\r
2150 if(busytime_u32 < frametime_min)
\r
2152 u32 sleeptime = frametime_min - busytime_u32;
\r
2153 device->sleep(sleeptime);
\r
2157 // Absolutelu necessary for wine!
\r
2161 Time difference calculation
\r
2163 f32 dtime; // in seconds
\r
2165 u32 time = device->getTimer()->getTime();
\r
2166 if(time > lasttime)
\r
2167 dtime = (time - lasttime) / 1000.0;
\r
2173 Time average and jitter calculation
\r
2176 static f32 dtime_avg1 = 0.0;
\r
2177 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2178 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2180 static f32 dtime_jitter1_max_sample = 0.0;
\r
2181 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2183 static f32 jitter1_max = 0.0;
\r
2184 static f32 counter = 0.0;
\r
2185 if(dtime_jitter1 > jitter1_max)
\r
2186 jitter1_max = dtime_jitter1;
\r
2191 dtime_jitter1_max_sample = jitter1_max;
\r
2192 dtime_jitter1_max_fraction
\r
2193 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2194 jitter1_max = 0.0;
\r
2199 Busytime average and jitter calculation
\r
2202 static f32 busytime_avg1 = 0.0;
\r
2203 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2204 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2206 static f32 busytime_jitter1_max_sample = 0.0;
\r
2207 static f32 busytime_jitter1_min_sample = 0.0;
\r
2209 static f32 jitter1_max = 0.0;
\r
2210 static f32 jitter1_min = 0.0;
\r
2211 static f32 counter = 0.0;
\r
2212 if(busytime_jitter1 > jitter1_max)
\r
2213 jitter1_max = busytime_jitter1;
\r
2214 if(busytime_jitter1 < jitter1_min)
\r
2215 jitter1_min = busytime_jitter1;
\r
2217 if(counter > 0.0){
\r
2219 busytime_jitter1_max_sample = jitter1_max;
\r
2220 busytime_jitter1_min_sample = jitter1_min;
\r
2221 jitter1_max = 0.0;
\r
2222 jitter1_min = 0.0;
\r
2227 Debug info for client
\r
2230 static float counter = 0.0;
\r
2235 client.printDebugInfo(std::cout);
\r
2240 Input handler step()
\r
2242 g_input->step(dtime);
\r
2245 Player speed control
\r
2254 bool a_superspeed,
\r
2257 PlayerControl control(
\r
2258 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2259 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2260 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2261 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2262 g_input->isKeyDown(irr::KEY_SPACE),
\r
2263 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2267 client.setPlayerControl(control);
\r
2271 Process environment
\r
2275 //TimeTaker timer("client.step(dtime)");
\r
2276 client.step(dtime);
\r
2277 //client.step(dtime_avg1);
\r
2280 if(server != NULL)
\r
2282 //TimeTaker timer("server->step(dtime)");
\r
2283 server->step(dtime);
\r
2286 v3f player_position = client.getPlayerPosition();
\r
2288 //TimeTaker //timer2("//timer2");
\r
2291 Mouse and camera control
\r
2294 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2297 device->getCursorControl()->setVisible(false);
\r
2299 if(first_loop_after_window_activation){
\r
2300 //std::cout<<"window active, first loop"<<std::endl;
\r
2301 first_loop_after_window_activation = false;
\r
2304 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2305 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2306 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2307 camera_yaw -= dx*0.2;
\r
2308 camera_pitch += dy*0.2;
\r
2309 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2310 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2312 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2315 device->getCursorControl()->setVisible(true);
\r
2317 //std::cout<<"window inactive"<<std::endl;
\r
2318 first_loop_after_window_activation = true;
\r
2321 camera_yaw = wrapDegrees(camera_yaw);
\r
2322 camera_pitch = wrapDegrees(camera_pitch);
\r
2324 v3f camera_direction = v3f(0,0,1);
\r
2325 camera_direction.rotateYZBy(camera_pitch);
\r
2326 camera_direction.rotateXZBy(camera_yaw);
\r
2328 // This is at the height of the eyes of the current figure
\r
2329 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2330 // This is more like in minecraft
\r
2331 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2333 camera->setPosition(camera_position);
\r
2334 // *100.0 helps in large map coordinates
\r
2335 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2337 if(FIELD_OF_VIEW_TEST){
\r
2338 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2339 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2342 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2343 //TimeTaker timer("client.updateCamera");
\r
2344 client.updateCamera(camera_position, camera_direction);
\r
2348 //TimeTaker //timer3("//timer3");
\r
2351 Calculate what block is the crosshair pointing to
\r
2354 //u32 t1 = device->getTimer()->getRealTime();
\r
2356 //f32 d = 4; // max. distance
\r
2357 f32 d = 4; // max. distance
\r
2358 core::line3d<f32> shootline(camera_position,
\r
2359 camera_position + camera_direction * BS * (d+1));
\r
2361 MapBlockObject *selected_object = client.getSelectedObject
\r
2362 (d*BS, camera_position, shootline);
\r
2365 If it's pointing to a MapBlockObject
\r
2368 if(selected_object != NULL)
\r
2370 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2372 core::aabbox3d<f32> box_on_map
\r
2373 = selected_object->getSelectionBoxOnMap();
\r
2375 hilightboxes.push_back(box_on_map);
\r
2377 infotext = narrow_to_wide(selected_object->infoText());
\r
2379 if(g_input->getLeftClicked())
\r
2381 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2382 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2383 selected_object->getId(), g_selected_item);
\r
2385 else if(g_input->getRightClicked())
\r
2387 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2389 Check if we want to modify the object ourselves
\r
2391 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2393 dstream<<"Sign object right-clicked"<<std::endl;
\r
2395 if(random_input == false)
\r
2397 // Get a new text for it
\r
2399 TextDest *dest = new TextDestSign(
\r
2400 selected_object->getBlock()->getPos(),
\r
2401 selected_object->getId(),
\r
2404 SignObject *sign_object = (SignObject*)selected_object;
\r
2406 std::wstring wtext =
\r
2407 narrow_to_wide(sign_object->getText());
\r
2409 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2415 Otherwise pass the event to the server as-is
\r
2419 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2420 selected_object->getId(), g_selected_item);
\r
2424 else // selected_object == NULL
\r
2428 Find out which node we are pointing at
\r
2431 bool nodefound = false;
\r
2433 v3s16 neighbourpos;
\r
2434 core::aabbox3d<f32> nodehilightbox;
\r
2435 f32 mindistance = BS * 1001;
\r
2437 v3s16 pos_i = floatToInt(player_position);
\r
2439 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2443 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2444 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2445 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2446 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2447 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2448 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2450 for(s16 y = ystart; y <= yend; y++)
\r
2451 for(s16 z = zstart; z <= zend; z++)
\r
2452 for(s16 x = xstart; x <= xend; x++)
\r
2457 n = client.getNode(v3s16(x,y,z));
\r
2458 if(content_pointable(n.d) == false)
\r
2461 catch(InvalidPositionException &e)
\r
2467 v3f npf = intToFloat(np);
\r
2472 v3s16(0,0,1), // back
\r
2473 v3s16(0,1,0), // top
\r
2474 v3s16(1,0,0), // right
\r
2475 v3s16(0,0,-1), // front
\r
2476 v3s16(0,-1,0), // bottom
\r
2477 v3s16(-1,0,0), // left
\r
2483 if(n.d == CONTENT_TORCH)
\r
2485 v3s16 dir = unpackDir(n.dir);
\r
2486 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2487 dir_f *= BS/2 - BS/6 - BS/20;
\r
2488 v3f cpf = npf + dir_f;
\r
2489 f32 distance = (cpf - camera_position).getLength();
\r
2491 core::aabbox3d<f32> box;
\r
2494 if(dir == v3s16(0,-1,0))
\r
2496 box = core::aabbox3d<f32>(
\r
2497 npf - v3f(BS/6, BS/2, BS/6),
\r
2498 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2502 else if(dir == v3s16(0,1,0))
\r
2504 box = core::aabbox3d<f32>(
\r
2505 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2506 npf + v3f(BS/6, BS/2, BS/6)
\r
2512 box = core::aabbox3d<f32>(
\r
2513 cpf - v3f(BS/6, BS/3, BS/6),
\r
2514 cpf + v3f(BS/6, BS/3, BS/6)
\r
2518 if(distance < mindistance)
\r
2520 if(box.intersectsWithLine(shootline))
\r
2524 neighbourpos = np;
\r
2525 mindistance = distance;
\r
2526 nodehilightbox = box;
\r
2535 for(u16 i=0; i<6; i++)
\r
2537 v3f dir_f = v3f(dirs[i].X,
\r
2538 dirs[i].Y, dirs[i].Z);
\r
2539 v3f centerpoint = npf + dir_f * BS/2;
\r
2541 (centerpoint - camera_position).getLength();
\r
2543 if(distance < mindistance)
\r
2545 core::CMatrix4<f32> m;
\r
2546 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2548 // This is the back face
\r
2549 v3f corners[2] = {
\r
2550 v3f(BS/2, BS/2, BS/2),
\r
2551 v3f(-BS/2, -BS/2, BS/2+d)
\r
2554 for(u16 j=0; j<2; j++)
\r
2556 m.rotateVect(corners[j]);
\r
2557 corners[j] += npf;
\r
2560 core::aabbox3d<f32> facebox(corners[0]);
\r
2561 facebox.addInternalPoint(corners[1]);
\r
2563 if(facebox.intersectsWithLine(shootline))
\r
2567 neighbourpos = np + dirs[i];
\r
2568 mindistance = distance;
\r
2570 //nodehilightbox = facebox;
\r
2572 const float d = 0.502;
\r
2573 core::aabbox3d<f32> nodebox
\r
2574 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2575 v3f nodepos_f = intToFloat(nodepos);
\r
2576 nodebox.MinEdge += nodepos_f;
\r
2577 nodebox.MaxEdge += nodepos_f;
\r
2578 nodehilightbox = nodebox;
\r
2580 } // if distance < mindistance
\r
2582 } // regular block
\r
2585 static float nodig_delay_counter = 0.0;
\r
2589 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2591 static float dig_time = 0.0;
\r
2592 static u16 dig_index = 0;
\r
2594 // Visualize selection
\r
2596 hilightboxes.push_back(nodehilightbox);
\r
2600 if(g_input->getLeftReleased())
\r
2602 client.clearTempMod(nodepos);
\r
2606 if(nodig_delay_counter > 0.0)
\r
2608 nodig_delay_counter -= dtime;
\r
2612 if(nodepos != nodepos_old)
\r
2614 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2615 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2617 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2619 client.clearTempMod(nodepos_old);
\r
2624 if(g_input->getLeftClicked() ||
\r
2625 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2627 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2628 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2630 if(g_input->getLeftClicked())
\r
2632 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2634 if(g_input->getLeftState())
\r
2636 MapNode n = client.getNode(nodepos);
\r
2638 // Get tool name. Default is "" = bare hands
\r
2639 std::string toolname = "";
\r
2640 InventoryList *mlist = local_inventory.getList("main");
\r
2643 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2644 if(item && (std::string)item->getName() == "ToolItem")
\r
2646 ToolItem *titem = (ToolItem*)item;
\r
2647 toolname = titem->getToolName();
\r
2651 // Get digging properties for material and tool
\r
2652 u8 material = n.d;
\r
2653 DiggingProperties prop =
\r
2654 getDiggingProperties(material, toolname);
\r
2656 float dig_time_complete = 0.0;
\r
2658 if(prop.diggable == false)
\r
2660 /*dstream<<"Material "<<(int)material
\r
2661 <<" not diggable with \""
\r
2662 <<toolname<<"\""<<std::endl;*/
\r
2663 // I guess nobody will wait for this long
\r
2664 dig_time_complete = 10000000.0;
\r
2668 dig_time_complete = prop.time;
\r
2671 if(dig_time_complete >= 0.001)
\r
2673 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2674 * dig_time/dig_time_complete);
\r
2676 // This is for torches
\r
2679 dig_index = CRACK_ANIMATION_LENGTH;
\r
2682 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2684 //TimeTaker timer("client.setTempMod");
\r
2685 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2686 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2690 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2691 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2692 client.clearTempMod(nodepos);
\r
2693 client.removeNode(nodepos);
\r
2697 nodig_delay_counter = dig_time_complete
\r
2698 / (float)CRACK_ANIMATION_LENGTH;
\r
2700 // We don't want a corresponding delay to
\r
2701 // very time consuming nodes
\r
2702 if(nodig_delay_counter > 0.5)
\r
2704 nodig_delay_counter = 0.5;
\r
2706 // We want a slight delay to very little
\r
2707 // time consuming nodes
\r
2708 float mindelay = 0.15;
\r
2709 if(nodig_delay_counter < mindelay)
\r
2711 nodig_delay_counter = mindelay;
\r
2715 dig_time += dtime;
\r
2719 if(g_input->getRightClicked())
\r
2721 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2722 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2725 nodepos_old = nodepos;
\r
2730 } // selected_object == NULL
\r
2732 g_input->resetLeftClicked();
\r
2733 g_input->resetRightClicked();
\r
2735 if(g_input->getLeftReleased())
\r
2737 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2739 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2741 if(g_input->getRightReleased())
\r
2743 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2747 g_input->resetLeftReleased();
\r
2748 g_input->resetRightReleased();
\r
2751 Calculate stuff for drawing
\r
2754 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2756 u32 daynight_ratio = client.getDayNightRatio();
\r
2757 /*video::SColor bgcolor = video::SColor(
\r
2759 skycolor.getRed() * daynight_ratio / 1000,
\r
2760 skycolor.getGreen() * daynight_ratio / 1000,
\r
2761 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2763 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2764 video::SColor bgcolor = video::SColor(
\r
2766 skycolor.getRed() * l / 255,
\r
2767 skycolor.getGreen() * l / 255,
\r
2768 skycolor.getBlue() * l / 255);
\r
2774 if(g_settings.getBool("enable_fog") == true)
\r
2776 f32 range = draw_control.wanted_range * BS;
\r
2777 if(draw_control.range_all)
\r
2778 range = 100000*BS;
\r
2782 video::EFT_FOG_LINEAR,
\r
2786 false, // pixel fog
\r
2787 false // range fog
\r
2793 Update gui stuff (0ms)
\r
2796 //TimeTaker guiupdatetimer("Gui updating");
\r
2799 wchar_t temptext[150];
\r
2801 static float drawtime_avg = 0;
\r
2802 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2803 static float beginscenetime_avg = 0;
\r
2804 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2805 static float scenetime_avg = 0;
\r
2806 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2807 static float endscenetime_avg = 0;
\r
2808 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2810 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2812 L", R: range_all=%i"
\r
2814 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2816 draw_control.range_all,
\r
2818 beginscenetime_avg,
\r
2823 guitext->setText(temptext);
\r
2827 wchar_t temptext[150];
\r
2828 swprintf(temptext, 150,
\r
2829 L"(% .1f, % .1f, % .1f)"
\r
2830 L" (% .3f < btime_jitter < % .3f"
\r
2831 L", dtime_jitter = % .1f %%"
\r
2832 L", v_range = %.1f)",
\r
2833 player_position.X/BS,
\r
2834 player_position.Y/BS,
\r
2835 player_position.Z/BS,
\r
2836 busytime_jitter1_min_sample,
\r
2837 busytime_jitter1_max_sample,
\r
2838 dtime_jitter1_max_fraction * 100.0,
\r
2839 draw_control.wanted_range
\r
2842 guitext2->setText(temptext);
\r
2846 guitext_info->setText(infotext.c_str());
\r
2850 Get chat messages from client
\r
2853 // Get new messages
\r
2854 std::wstring message;
\r
2855 while(client.getChatMessage(message))
\r
2857 chat_lines.push_back(ChatLine(message));
\r
2858 /*if(chat_lines.size() > 6)
\r
2860 core::list<ChatLine>::Iterator
\r
2861 i = chat_lines.begin();
\r
2862 chat_lines.erase(i);
\r
2865 // Append them to form the whole static text and throw
\r
2866 // it to the gui element
\r
2867 std::wstring whole;
\r
2868 // This will correspond to the line number counted from
\r
2869 // top to bottom, from size-1 to 0
\r
2870 s16 line_number = chat_lines.size();
\r
2871 // Count of messages to be removed from the top
\r
2872 u16 to_be_removed_count = 0;
\r
2873 for(core::list<ChatLine>::Iterator
\r
2874 i = chat_lines.begin();
\r
2875 i != chat_lines.end(); i++)
\r
2877 // After this, line number is valid for this loop
\r
2880 (*i).age += dtime;
\r
2882 This results in a maximum age of 60*6 to the
\r
2883 lowermost line and a maximum of 6 lines
\r
2885 float allowed_age = (6-line_number) * 60.0;
\r
2887 if((*i).age > allowed_age)
\r
2889 to_be_removed_count++;
\r
2892 whole += (*i).text + L'\n';
\r
2894 for(u16 i=0; i<to_be_removed_count; i++)
\r
2896 core::list<ChatLine>::Iterator
\r
2897 it = chat_lines.begin();
\r
2898 chat_lines.erase(it);
\r
2900 guitext_chat->setText(whole.c_str());
\r
2901 // Update gui element size and position
\r
2902 core::rect<s32> rect(
\r
2904 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2905 screensize.X - 10,
\r
2908 guitext_chat->setRelativePosition(rect);
\r
2910 if(chat_lines.size() == 0)
\r
2911 guitext_chat->setVisible(false);
\r
2913 guitext_chat->setVisible(true);
\r
2920 static u16 old_selected_item = 65535;
\r
2921 if(client.getLocalInventoryUpdated()
\r
2922 || g_selected_item != old_selected_item)
\r
2924 old_selected_item = g_selected_item;
\r
2925 //std::cout<<"Updating local inventory"<<std::endl;
\r
2926 client.getLocalInventory(local_inventory);
\r
2927 quick_inventory->setSelection(g_selected_item);
\r
2928 quick_inventory->update();
\r
2932 Send actions returned by the inventory menu
\r
2934 while(inventory_action_queue.size() != 0)
\r
2936 InventoryAction *a = inventory_action_queue.pop_front();
\r
2938 client.sendInventoryAction(a);
\r
2947 TimeTaker drawtimer("Drawing");
\r
2951 TimeTaker timer("beginScene");
\r
2952 driver->beginScene(true, true, bgcolor);
\r
2953 //driver->beginScene(false, true, bgcolor);
\r
2954 beginscenetime = timer.stop(true);
\r
2959 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2962 TimeTaker timer("smgr");
\r
2964 scenetime = timer.stop(true);
\r
2968 //TimeTaker timer9("auxiliary drawings");
\r
2972 //TimeTaker //timer10("//timer10");
\r
2974 video::SMaterial m;
\r
2975 //m.Thickness = 10;
\r
2977 m.Lighting = false;
\r
2978 driver->setMaterial(m);
\r
2980 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2982 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2983 i != hilightboxes.end(); i++)
\r
2985 /*std::cout<<"hilightbox min="
\r
2986 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2988 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2990 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2996 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2997 displaycenter + core::vector2d<s32>(10,0),
\r
2998 video::SColor(255,255,255,255));
\r
2999 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3000 displaycenter + core::vector2d<s32>(0,10),
\r
3001 video::SColor(255,255,255,255));
\r
3006 //TimeTaker //timer11("//timer11");
\r
3012 guienv->drawAll();
\r
3016 TimeTaker timer("endScene");
\r
3017 driver->endScene();
\r
3018 endscenetime = timer.stop(true);
\r
3021 drawtime = drawtimer.stop(true);
\r
3027 static s16 lastFPS = 0;
\r
3028 //u16 fps = driver->getFPS();
\r
3029 u16 fps = (1.0/dtime_avg1);
\r
3031 if (lastFPS != fps)
\r
3033 core::stringw str = L"Minetest [";
\r
3034 str += driver->getName();
\r
3038 device->setWindowCaption(str.c_str());
\r
3044 device->yield();*/
\r
3047 delete quick_inventory;
\r
3050 Disable texture fetches and other stuff that is queued
\r
3051 to be processed by the main loop.
\r
3053 This has to be done before client goes out of scope.
\r
3055 g_irrlicht->Shutdown(true);
\r
3057 } // client and server are deleted at this point
\r
3060 catch(con::PeerNotFoundException &e)
\r
3062 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3063 error_message = L"Connection timed out.";
\r
3066 } // Menu-game loop
\r
3071 In the end, delete the Irrlicht device.
\r
3076 Update configuration file
\r
3078 /*if(configpath != "")
\r
3080 g_settings.updateConfigFile(configpath.c_str());
\r
3083 END_DEBUG_EXCEPTION_HANDLER
\r
3085 debugstreams_deinit();
\r