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
350 ======================================================================
\r
355 Setting this to 1 enables a special camera mode that forces
\r
356 the renderers to think that the camera statically points from
\r
357 the starting place to a static direction.
\r
359 This allows one to move around with the player and see what
\r
360 is actually drawn behind solid things and behind the player.
\r
362 #define FIELD_OF_VIEW_TEST 0
\r
366 #pragma message ("Disabling unit tests")
\r
368 #warning "Disabling unit tests"
\r
370 // Disable unit tests
\r
371 #define ENABLE_TESTS 0
\r
373 // Enable unit tests
\r
374 #define ENABLE_TESTS 1
\r
378 #pragma comment(lib, "Irrlicht.lib")
\r
379 //#pragma comment(lib, "jthread.lib")
\r
380 #pragma comment(lib, "zlibwapi.lib")
\r
381 #pragma comment(lib, "Shell32.lib")
\r
382 // This would get rid of the console window
\r
383 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
386 #include <iostream>
\r
388 #include <jmutexautolock.h>
\r
389 #include <locale.h>
\r
390 #include "common_irrlicht.h"
\r
393 #include "player.h"
\r
396 #include "environment.h"
\r
397 #include "server.h"
\r
398 #include "client.h"
\r
399 #include "serialization.h"
\r
400 #include "constants.h"
\r
401 #include "strfnd.h"
\r
402 #include "porting.h"
\r
403 #include "irrlichtwrapper.h"
\r
404 #include "gettime.h"
\r
405 #include "porting.h"
\r
406 #include "guiPauseMenu.h"
\r
407 #include "guiInventoryMenu.h"
\r
408 #include "guiTextInputMenu.h"
\r
409 #include "materials.h"
\r
410 #include "guiMessageMenu.h"
\r
411 #include "filesys.h"
\r
412 #include "config.h"
\r
413 #include "guiMainMenu.h"
\r
414 #include "mineral.h"
\r
416 IrrlichtWrapper *g_irrlicht;
\r
418 MapDrawControl draw_control;
\r
422 These are loaded from the config file.
\r
425 Settings g_settings;
\r
427 extern void set_default_settings();
\r
433 IrrlichtDevice *g_device = NULL;
\r
434 Client *g_client = NULL;
\r
440 gui::IGUIEnvironment* guienv = NULL;
\r
441 gui::IGUIStaticText *guiroot = NULL;
\r
443 class MainMenuManager : public IMenuManager
\r
446 virtual void createdMenu(GUIModalMenu *menu)
\r
448 for(core::list<GUIModalMenu*>::Iterator
\r
449 i = m_stack.begin();
\r
450 i != m_stack.end(); i++)
\r
452 assert(*i != menu);
\r
455 if(m_stack.size() != 0)
\r
456 (*m_stack.getLast())->setVisible(false);
\r
457 m_stack.push_back(menu);
\r
460 virtual void deletingMenu(GUIModalMenu *menu)
\r
462 // Remove all entries if there are duplicates
\r
463 bool removed_entry;
\r
465 removed_entry = false;
\r
466 for(core::list<GUIModalMenu*>::Iterator
\r
467 i = m_stack.begin();
\r
468 i != m_stack.end(); i++)
\r
473 removed_entry = true;
\r
477 }while(removed_entry);
\r
479 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
480 assert(*i == menu);
\r
481 m_stack.erase(i);*/
\r
483 if(m_stack.size() != 0)
\r
484 (*m_stack.getLast())->setVisible(true);
\r
489 return m_stack.size();
\r
492 core::list<GUIModalMenu*> m_stack;
\r
495 MainMenuManager g_menumgr;
\r
497 bool noMenuActive()
\r
499 return (g_menumgr.menuCount() == 0);
\r
502 bool g_disconnect_requested = false;
\r
504 class MainGameCallback : public IGameCallback
\r
507 virtual void exitToOS()
\r
509 g_device->closeDevice();
\r
512 virtual void disconnect()
\r
514 g_disconnect_requested = true;
\r
518 MainGameCallback g_gamecallback;
\r
520 // Inventory actions from the menu are buffered here before sending
\r
521 Queue<InventoryAction*> inventory_action_queue;
\r
522 // This is a copy of the inventory that the client's environment has
\r
523 Inventory local_inventory;
\r
525 u16 g_selected_item = 0;
\r
532 std::ostream *dout_con_ptr = &dummyout;
\r
533 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
534 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
535 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
536 //std::ostream *dout_con_ptr = &dstream;
\r
537 //std::ostream *derr_con_ptr = &dstream;
\r
540 std::ostream *dout_server_ptr = &dstream;
\r
541 std::ostream *derr_server_ptr = &dstream;
\r
544 std::ostream *dout_client_ptr = &dstream;
\r
545 std::ostream *derr_client_ptr = &dstream;
\r
548 gettime.h implementation
\r
554 Use irrlicht because it is more precise than porting.h's
\r
557 if(g_irrlicht == NULL)
\r
559 return g_irrlicht->getTime();
\r
566 struct TextDestSign : public TextDest
\r
568 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
570 m_blockpos = blockpos;
\r
574 void gotText(std::wstring text)
\r
576 std::string ntext = wide_to_narrow(text);
\r
577 dstream<<"Changing text of a sign object: "
\r
578 <<ntext<<std::endl;
\r
579 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
587 struct TextDestChat : public TextDest
\r
589 TextDestChat(Client *client)
\r
593 void gotText(std::wstring text)
\r
595 m_client->sendChatMessage(text);
\r
596 m_client->addChatMessage(text);
\r
602 class MyEventReceiver : public IEventReceiver
\r
605 // This is the one method that we have to implement
\r
606 virtual bool OnEvent(const SEvent& event)
\r
609 React to nothing here if a menu is active
\r
611 if(noMenuActive() == false)
\r
617 // Remember whether each key is down or up
\r
618 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
620 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
622 if(event.KeyInput.PressedDown)
\r
624 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
630 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
632 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
634 dstream<<DTIME<<"MyEventReceiver: "
\r
635 <<"Launching pause menu"<<std::endl;
\r
636 // It will delete itself by itself
\r
637 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
638 &g_menumgr))->drop();
\r
641 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
643 dstream<<DTIME<<"MyEventReceiver: "
\r
644 <<"Launching inventory"<<std::endl;
\r
645 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
646 &local_inventory, &inventory_action_queue,
\r
647 &g_menumgr))->drop();
\r
650 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
652 TextDest *dest = new TextDestChat(g_client);
\r
654 (new GUITextInputMenu(guienv, guiroot, -1,
\r
660 // Material selection
\r
661 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
663 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
666 g_selected_item = 0;
\r
667 dstream<<DTIME<<"Selected item: "
\r
668 <<g_selected_item<<std::endl;
\r
671 // Viewing range selection
\r
672 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
674 if(draw_control.range_all)
\r
676 draw_control.range_all = false;
\r
677 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
681 draw_control.range_all = true;
\r
682 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
686 // Print debug stacks
\r
687 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
689 dstream<<"-----------------------------------------"
\r
691 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
692 dstream<<"-----------------------------------------"
\r
694 debug_stacks_print();
\r
699 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
701 if(noMenuActive() == false)
\r
703 left_active = false;
\r
704 middle_active = false;
\r
705 right_active = false;
\r
709 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
710 left_active = event.MouseInput.isLeftPressed();
\r
711 middle_active = event.MouseInput.isMiddlePressed();
\r
712 right_active = event.MouseInput.isRightPressed();
\r
714 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
716 leftclicked = true;
\r
718 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
720 rightclicked = true;
\r
722 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
724 leftreleased = true;
\r
726 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
728 rightreleased = true;
\r
730 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
732 /*dstream<<"event.MouseInput.Wheel="
\r
733 <<event.MouseInput.Wheel<<std::endl;*/
\r
734 if(event.MouseInput.Wheel < 0)
\r
736 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
739 g_selected_item = 0;
\r
741 else if(event.MouseInput.Wheel > 0)
\r
743 if(g_selected_item > 0)
\r
746 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
755 // This is used to check whether a key is being held down
\r
756 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
758 return keyIsDown[keyCode];
\r
763 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
764 keyIsDown[i] = false;
\r
766 leftclicked = false;
\r
767 rightclicked = false;
\r
768 leftreleased = false;
\r
769 rightreleased = false;
\r
771 left_active = false;
\r
772 middle_active = false;
\r
773 right_active = false;
\r
784 bool rightreleased;
\r
787 bool middle_active;
\r
791 // We use this array to store the current state of each key
\r
792 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
795 IrrlichtDevice *m_device;
\r
804 virtual ~InputHandler()
\r
808 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
810 virtual v2s32 getMousePos() = 0;
\r
811 virtual void setMousePos(s32 x, s32 y) = 0;
\r
813 virtual bool getLeftState() = 0;
\r
814 virtual bool getRightState() = 0;
\r
816 virtual bool getLeftClicked() = 0;
\r
817 virtual bool getRightClicked() = 0;
\r
818 virtual void resetLeftClicked() = 0;
\r
819 virtual void resetRightClicked() = 0;
\r
821 virtual bool getLeftReleased() = 0;
\r
822 virtual bool getRightReleased() = 0;
\r
823 virtual void resetLeftReleased() = 0;
\r
824 virtual void resetRightReleased() = 0;
\r
826 virtual void step(float dtime) {};
\r
828 virtual void clear() {};
\r
831 InputHandler *g_input = NULL;
\r
833 class RealInputHandler : public InputHandler
\r
836 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
838 m_receiver(receiver)
\r
841 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
843 return m_receiver->IsKeyDown(keyCode);
\r
845 virtual v2s32 getMousePos()
\r
847 return m_device->getCursorControl()->getPosition();
\r
849 virtual void setMousePos(s32 x, s32 y)
\r
851 m_device->getCursorControl()->setPosition(x, y);
\r
854 virtual bool getLeftState()
\r
856 return m_receiver->left_active;
\r
858 virtual bool getRightState()
\r
860 return m_receiver->right_active;
\r
863 virtual bool getLeftClicked()
\r
865 return m_receiver->leftclicked;
\r
867 virtual bool getRightClicked()
\r
869 return m_receiver->rightclicked;
\r
871 virtual void resetLeftClicked()
\r
873 m_receiver->leftclicked = false;
\r
875 virtual void resetRightClicked()
\r
877 m_receiver->rightclicked = false;
\r
880 virtual bool getLeftReleased()
\r
882 return m_receiver->leftreleased;
\r
884 virtual bool getRightReleased()
\r
886 return m_receiver->rightreleased;
\r
888 virtual void resetLeftReleased()
\r
890 m_receiver->leftreleased = false;
\r
892 virtual void resetRightReleased()
\r
894 m_receiver->rightreleased = false;
\r
899 resetRightClicked();
\r
900 resetLeftClicked();
\r
903 IrrlichtDevice *m_device;
\r
904 MyEventReceiver *m_receiver;
\r
907 class RandomInputHandler : public InputHandler
\r
910 RandomInputHandler()
\r
912 leftclicked = false;
\r
913 rightclicked = false;
\r
914 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
915 keydown[i] = false;
\r
917 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
919 return keydown[keyCode];
\r
921 virtual v2s32 getMousePos()
\r
925 virtual void setMousePos(s32 x, s32 y)
\r
927 mousepos = v2s32(x,y);
\r
930 virtual bool getLeftState()
\r
934 virtual bool getRightState()
\r
939 virtual bool getLeftClicked()
\r
941 return leftclicked;
\r
943 virtual bool getRightClicked()
\r
945 return rightclicked;
\r
947 virtual void resetLeftClicked()
\r
949 leftclicked = false;
\r
951 virtual void resetRightClicked()
\r
953 rightclicked = false;
\r
956 virtual bool getLeftReleased()
\r
960 virtual bool getRightReleased()
\r
964 virtual void resetLeftReleased()
\r
967 virtual void resetRightReleased()
\r
971 virtual void step(float dtime)
\r
974 static float counter1 = 0;
\r
978 counter1 = 0.1*Rand(1,10);
\r
979 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
980 g_selected_material++;
\r
982 g_selected_material = 0;*/
\r
983 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
986 g_selected_item = 0;
\r
990 static float counter1 = 0;
\r
994 counter1 = 0.1*Rand(1, 40);
\r
995 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
999 static float counter1 = 0;
\r
1000 counter1 -= dtime;
\r
1001 if(counter1 < 0.0)
\r
1003 counter1 = 0.1*Rand(1, 40);
\r
1004 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
1008 static float counter1 = 0;
\r
1009 counter1 -= dtime;
\r
1010 if(counter1 < 0.0)
\r
1012 counter1 = 0.1*Rand(1, 40);
\r
1013 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1017 static float counter1 = 0;
\r
1018 counter1 -= dtime;
\r
1019 if(counter1 < 0.0)
\r
1021 counter1 = 0.1*Rand(1, 40);
\r
1022 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1026 static float counter1 = 0;
\r
1027 counter1 -= dtime;
\r
1028 if(counter1 < 0.0)
\r
1030 counter1 = 0.1*Rand(1, 20);
\r
1031 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1035 static float counter1 = 0;
\r
1036 counter1 -= dtime;
\r
1037 if(counter1 < 0.0)
\r
1039 counter1 = 0.1*Rand(1, 30);
\r
1040 leftclicked = true;
\r
1044 static float counter1 = 0;
\r
1045 counter1 -= dtime;
\r
1046 if(counter1 < 0.0)
\r
1048 counter1 = 0.1*Rand(1, 20);
\r
1049 rightclicked = true;
\r
1052 mousepos += mousespeed;
\r
1055 s32 Rand(s32 min, s32 max)
\r
1057 return (myrand()%(max-min+1))+min;
\r
1060 bool keydown[KEY_KEY_CODES_COUNT];
\r
1064 bool rightclicked;
\r
1067 void updateViewingRange(f32 frametime_in, Client *client)
\r
1069 if(draw_control.range_all == true)
\r
1072 static f32 added_frametime = 0;
\r
1073 static s16 added_frames = 0;
\r
1075 added_frametime += frametime_in;
\r
1076 added_frames += 1;
\r
1078 // Actually this counter kind of sucks because frametime is busytime
\r
1079 static f32 counter = 0;
\r
1080 counter -= frametime_in;
\r
1086 /*dstream<<__FUNCTION_NAME
\r
1087 <<": Collected "<<added_frames<<" frames, total of "
\r
1088 <<added_frametime<<"s."<<std::endl;*/
\r
1090 /*dstream<<"draw_control.blocks_drawn="
\r
1091 <<draw_control.blocks_drawn
\r
1092 <<", draw_control.blocks_would_have_drawn="
\r
1093 <<draw_control.blocks_would_have_drawn
\r
1096 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1097 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1099 draw_control.wanted_min_range = range_min;
\r
1100 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1102 float block_draw_ratio = 1.0;
\r
1103 if(draw_control.blocks_would_have_drawn != 0)
\r
1105 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1106 / (float)draw_control.blocks_would_have_drawn;
\r
1109 // Calculate the average frametime in the case that all wanted
\r
1110 // blocks had been drawn
\r
1111 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1113 added_frametime = 0.0;
\r
1116 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1117 float wanted_frametime = 1.0 / wanted_fps;
\r
1119 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1120 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1122 // If needed frametime change is very small, just return
\r
1123 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1125 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1129 float range = draw_control.wanted_range;
\r
1130 float new_range = range;
\r
1132 static s16 range_old = 0;
\r
1133 static f32 frametime_old = 0;
\r
1135 float d_range = range - range_old;
\r
1136 f32 d_frametime = frametime - frametime_old;
\r
1137 // A sane default of 30ms per 50 nodes of range
\r
1138 static f32 time_per_range = 30. / 50;
\r
1141 time_per_range = d_frametime / d_range;
\r
1144 // The minimum allowed calculated frametime-range derivative:
\r
1145 // Practically this sets the maximum speed of changing the range.
\r
1146 // The lower this value, the higher the maximum changing speed.
\r
1147 // A low value here results in wobbly range (0.001)
\r
1148 // A high value here results in slow changing range (0.0025)
\r
1149 // SUGG: This could be dynamically adjusted so that when
\r
1150 // the camera is turning, this is lower
\r
1151 //float min_time_per_range = 0.0015;
\r
1152 float min_time_per_range = 0.0010;
\r
1153 //float min_time_per_range = 0.05 / range;
\r
1154 if(time_per_range < min_time_per_range)
\r
1156 time_per_range = min_time_per_range;
\r
1157 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1161 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1164 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1165 // Dampen the change a bit to kill oscillations
\r
1166 //wanted_range_change *= 0.9;
\r
1167 //wanted_range_change *= 0.75;
\r
1168 wanted_range_change *= 0.5;
\r
1169 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1171 // If needed range change is very small, just return
\r
1172 if(fabs(wanted_range_change) < 0.001)
\r
1174 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1178 new_range += wanted_range_change;
\r
1179 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1181 //float new_range_unclamped = new_range;
\r
1182 if(new_range < range_min)
\r
1183 new_range = range_min;
\r
1184 if(new_range > range_max)
\r
1185 new_range = range_max;
\r
1187 /*if(new_range != new_range_unclamped)
\r
1188 dstream<<", clamped to "<<new_range<<std::endl;
\r
1190 dstream<<std::endl;*/
\r
1192 draw_control.wanted_range = new_range;
\r
1194 range_old = new_range;
\r
1195 frametime_old = frametime;
\r
1198 class GUIQuickInventory : public IEventReceiver
\r
1201 GUIQuickInventory(
\r
1202 gui::IGUIEnvironment* env,
\r
1203 gui::IGUIElement* parent,
\r
1206 Inventory *inventory):
\r
1207 m_itemcount(itemcount),
\r
1208 m_inventory(inventory)
\r
1210 core::rect<s32> imgsize(0,0,48,48);
\r
1211 core::rect<s32> textsize(0,0,48,16);
\r
1212 v2s32 spacing(0, 64);
\r
1213 for(s32 i=0; i<m_itemcount; i++)
\r
1215 m_images.push_back(env->addImage(
\r
1216 imgsize + pos + spacing*i
\r
1218 m_images[i]->setScaleImage(true);
\r
1219 m_texts.push_back(env->addStaticText(
\r
1221 textsize + pos + spacing*i,
\r
1224 m_texts[i]->setBackgroundColor(
\r
1225 video::SColor(128,0,0,0));
\r
1226 m_texts[i]->setTextAlignment(
\r
1227 gui::EGUIA_CENTER,
\r
1228 gui::EGUIA_UPPERLEFT);
\r
1232 ~GUIQuickInventory()
\r
1234 for(u32 i=0; i<m_texts.size(); i++)
\r
1236 m_texts[i]->remove();
\r
1238 for(u32 i=0; i<m_images.size(); i++)
\r
1240 m_images[i]->remove();
\r
1244 virtual bool OnEvent(const SEvent& event)
\r
1249 void setSelection(s32 i)
\r
1258 start = m_selection - m_itemcount / 2;
\r
1260 InventoryList *mainlist = m_inventory->getList("main");
\r
1262 for(s32 i=0; i<m_itemcount; i++)
\r
1264 s32 j = i + start;
\r
1266 if(j > (s32)mainlist->getSize() - 1)
\r
1267 j -= mainlist->getSize();
\r
1269 j += mainlist->getSize();
\r
1271 InventoryItem *item = mainlist->getItem(j);
\r
1275 m_images[i]->setImage(NULL);
\r
1278 if(m_selection == j)
\r
1279 swprintf(t, 10, L"<-");
\r
1281 swprintf(t, 10, L"");
\r
1282 m_texts[i]->setText(t);
\r
1284 // The next ifs will segfault with a NULL pointer
\r
1289 m_images[i]->setImage(item->getImage());
\r
1292 if(m_selection == j)
\r
1293 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1295 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1296 m_texts[i]->setText(t);
\r
1302 core::array<gui::IGUIStaticText*> m_texts;
\r
1303 core::array<gui::IGUIImage*> m_images;
\r
1304 Inventory *m_inventory;
\r
1315 ChatLine(const std::wstring &a_text):
\r
1321 std::wstring text;
\r
1324 // These are defined global so that they're not optimized too much.
\r
1325 // Can't change them to volatile.
\r
1330 std::string tempstring;
\r
1331 std::string tempstring2;
\r
1336 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1337 TimeTaker timer("Testing std::string speed");
\r
1338 const u32 jj = 10000;
\r
1339 for(u32 j=0; j<jj; j++)
\r
1343 const u32 ii = 10;
\r
1344 for(u32 i=0; i<ii; i++){
\r
1345 tempstring2 += "asd";
\r
1347 for(u32 i=0; i<ii+1; i++){
\r
1348 tempstring += "asd";
\r
1349 if(tempstring == tempstring2)
\r
1355 dstream<<"All of the following tests should take around 100ms each."
\r
1359 TimeTaker timer("Testing floating-point conversion speed");
\r
1361 for(u32 i=0; i<4000000; i++){
\r
1368 TimeTaker timer("Testing floating-point vector speed");
\r
1370 tempv3f1 = v3f(1,2,3);
\r
1371 tempv3f2 = v3f(4,5,6);
\r
1372 for(u32 i=0; i<10000000; i++){
\r
1373 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1374 tempv3f2 += v3f(7,8,9);
\r
1379 TimeTaker timer("Testing core::map speed");
\r
1381 core::map<v2s16, f32> map1;
\r
1384 for(s16 y=0; y<ii; y++){
\r
1385 for(s16 x=0; x<ii; x++){
\r
1386 map1.insert(v2s16(x,y), tempf);
\r
1390 for(s16 y=ii-1; y>=0; y--){
\r
1391 for(s16 x=0; x<ii; x++){
\r
1392 tempf = map1[v2s16(x,y)];
\r
1398 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1399 TimeTaker timer("Testing mutex speed");
\r
1412 // Do at least 10ms
\r
1413 while(timer.getTime() < 10);
\r
1415 u32 dtime = timer.stop();
\r
1416 u32 per_ms = n / dtime;
\r
1417 std::cout<<"Done. "<<dtime<<"ms, "
\r
1418 <<per_ms<<"/ms"<<std::endl;
\r
1422 int main(int argc, char *argv[])
\r
1425 Parse command line
\r
1428 // List all allowed options
\r
1429 core::map<std::string, ValueSpec> allowed_options;
\r
1430 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1431 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1432 "Run server directly"));
\r
1433 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1434 "Load configuration from specified file"));
\r
1435 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1436 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1437 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1438 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1439 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1440 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1442 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1444 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1446 Settings cmd_args;
\r
1448 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1450 if(ret == false || cmd_args.getFlag("help"))
\r
1452 dstream<<"Allowed options:"<<std::endl;
\r
1453 for(core::map<std::string, ValueSpec>::Iterator
\r
1454 i = allowed_options.getIterator();
\r
1455 i.atEnd() == false; i++)
\r
1457 dstream<<" --"<<i.getNode()->getKey();
\r
1458 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1463 dstream<<" <value>";
\r
1465 dstream<<std::endl;
\r
1467 if(i.getNode()->getValue().help != NULL)
\r
1469 dstream<<" "<<i.getNode()->getValue().help
\r
1474 return cmd_args.getFlag("help") ? 0 : 1;
\r
1478 Low-level initialization
\r
1481 bool disable_stderr = false;
\r
1483 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1484 disable_stderr = true;
\r
1487 // Initialize debug streams
\r
1488 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1489 // Initialize debug stacks
\r
1490 debug_stacks_init();
\r
1492 DSTACK(__FUNCTION_NAME);
\r
1494 porting::initializePaths();
\r
1495 // Create user data directory
\r
1496 fs::CreateDir(porting::path_userdata);
\r
1498 // C-style stuff initialization
\r
1499 initializeMaterialProperties();
\r
1502 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1504 // Print startup message
\r
1505 dstream<<DTIME<<"minetest-c55"
\r
1506 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1507 <<", "<<BUILD_INFO
\r
1511 Basic initialization
\r
1514 // Initialize default settings
\r
1515 set_default_settings();
\r
1517 // Set locale. This is for forcing '.' as the decimal point.
\r
1518 std::locale::global(std::locale("C"));
\r
1519 // This enables printing all characters in bitmap font
\r
1520 setlocale(LC_CTYPE, "en_US");
\r
1522 // Initialize sockets
\r
1524 atexit(sockets_cleanup);
\r
1534 // Path of configuration file in use
\r
1535 std::string configpath = "";
\r
1537 if(cmd_args.exists("config"))
\r
1539 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1542 dstream<<"Could not read configuration from \""
\r
1543 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1546 configpath = cmd_args.get("config");
\r
1550 core::array<std::string> filenames;
\r
1551 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1552 #ifdef RUN_IN_PLACE
\r
1553 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1556 for(u32 i=0; i<filenames.size(); i++)
\r
1558 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1561 configpath = filenames[i];
\r
1567 // Initialize random seed
\r
1572 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1574 These are needed for unit tests at least.
\r
1577 IIrrlichtWrapper irrlicht_dummy;
\r
1579 init_mapnode(&irrlicht_dummy);
\r
1584 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1585 || cmd_args.getFlag("enable-unittests") == true)
\r
1590 // Read map parameters from settings
\r
1592 HMParams hm_params;
\r
1593 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1594 hm_params.randmax = g_settings.get("height_randmax");
\r
1595 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1596 hm_params.base = g_settings.get("height_base");*/
\r
1598 MapParams map_params;
\r
1599 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1600 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1608 if(cmd_args.exists("port"))
\r
1609 port = cmd_args.getU16("port");
\r
1610 else if(cmd_args.exists("port"))
\r
1611 port = g_settings.getU16("port");
\r
1614 std::string map_dir = porting::path_userdata+"/map";
\r
1615 if(cmd_args.exists("map-dir"))
\r
1616 map_dir = cmd_args.get("map-dir");
\r
1617 else if(g_settings.exists("map-dir"))
\r
1618 map_dir = g_settings.get("map-dir");
\r
1620 // Run dedicated server if asked to
\r
1621 if(cmd_args.getFlag("server"))
\r
1623 DSTACK("Dedicated server branch");
\r
1626 Server server(map_dir.c_str(), hm_params, map_params);
\r
1627 server.start(port);
\r
1630 dedicated_server_loop(server);
\r
1639 // Address to connect to
\r
1640 std::string address = "";
\r
1642 if(cmd_args.exists("address"))
\r
1644 address = cmd_args.get("address");
\r
1648 address = g_settings.get("address");
\r
1651 std::string playername = g_settings.get("name");
\r
1654 Resolution selection
\r
1657 bool fullscreen = false;
\r
1658 u16 screenW = g_settings.getU16("screenW");
\r
1659 u16 screenH = g_settings.getU16("screenH");
\r
1663 MyEventReceiver receiver;
\r
1665 video::E_DRIVER_TYPE driverType;
\r
1668 //driverType = video::EDT_DIRECT3D9;
\r
1669 driverType = video::EDT_OPENGL;
\r
1671 driverType = video::EDT_OPENGL;
\r
1672 //driverType = video::EDT_BURNINGSVIDEO;
\r
1675 // create device and exit if creation failed
\r
1677 IrrlichtDevice *device;
\r
1678 device = createDevice(driverType,
\r
1679 core::dimension2d<u32>(screenW, screenH),
\r
1680 16, fullscreen, false, false, &receiver);
\r
1683 return 1; // could not create selected driver.
\r
1685 g_device = device;
\r
1686 g_irrlicht = new IrrlichtWrapper(device);
\r
1689 Speed tests (done after irrlicht is loaded to get timer)
\r
1691 if(cmd_args.getFlag("speedtests"))
\r
1693 dstream<<"Running speed tests"<<std::endl;
\r
1698 device->setResizable(true);
\r
1700 bool random_input = g_settings.getBool("random_input")
\r
1701 || cmd_args.getFlag("random-input");
\r
1703 g_input = new RandomInputHandler();
\r
1705 g_input = new RealInputHandler(device, &receiver);
\r
1708 Continue initialization
\r
1711 video::IVideoDriver* driver = device->getVideoDriver();
\r
1714 This changes the minimum allowed number of vertices in a VBO
\r
1716 //driver->setMinHardwareBufferVertexCount(50);
\r
1718 scene::ISceneManager* smgr = device->getSceneManager();
\r
1720 guienv = device->getGUIEnvironment();
\r
1721 gui::IGUISkin* skin = guienv->getSkin();
\r
1722 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1724 skin->setFont(font);
\r
1726 dstream<<"WARNING: Font file was not found."
\r
1727 " Using default font."<<std::endl;
\r
1728 // If font was not found, this will get us one
\r
1729 font = skin->getFont();
\r
1732 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1733 dstream<<"text_height="<<text_height<<std::endl;
\r
1735 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1736 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1737 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1738 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1739 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1740 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1743 Preload some textures and stuff
\r
1746 init_content_inventory_texture_paths();
\r
1747 init_mapnode(g_irrlicht);
\r
1748 init_mineral(g_irrlicht);
\r
1755 We need some kind of a root node to be able to add
\r
1756 custom gui elements directly on the screen.
\r
1757 Otherwise they won't be automatically drawn.
\r
1759 guiroot = guienv->addStaticText(L"",
\r
1760 core::rect<s32>(0, 0, 10000, 10000));
\r
1762 // First line of debug text
\r
1763 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1765 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1767 // Second line of debug text
\r
1768 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1770 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1773 // At the middle of the screen
\r
1774 // Object infos are shown in this
\r
1775 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1777 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1781 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1783 core::rect<s32>(0,0,0,0),
\r
1785 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1786 core::list<ChatLine> chat_lines;
\r
1789 If an error occurs, this is set to something and the
\r
1790 menu-game loop is restarted. It is then displayed before
\r
1793 std::wstring error_message = L"";
\r
1798 while(g_device->run())
\r
1801 // This is used for catching disconnects
\r
1806 Out-of-game menu loop.
\r
1808 Loop quits when menu returns proper parameters.
\r
1812 // Cursor can be non-visible when coming from the game
\r
1813 device->getCursorControl()->setVisible(true);
\r
1814 // Some stuff are left to scene manager when coming from the game
\r
1815 // (map at least?)
\r
1817 // Reset or hide the debug gui texts
\r
1818 guitext->setText(L"Minetest-c55");
\r
1819 guitext2->setVisible(false);
\r
1820 guitext_info->setVisible(false);
\r
1821 guitext_chat->setVisible(false);
\r
1823 // Initialize menu data
\r
1824 MainMenuData menudata;
\r
1825 menudata.address = narrow_to_wide(address);
\r
1826 menudata.name = narrow_to_wide(playername);
\r
1827 menudata.port = narrow_to_wide(itos(port));
\r
1828 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1830 GUIMainMenu *menu =
\r
1831 new GUIMainMenu(guienv, guiroot, -1,
\r
1832 &g_menumgr, &menudata, &g_gamecallback);
\r
1833 menu->allowFocusRemoval(true);
\r
1835 if(error_message != L"")
\r
1837 GUIMessageMenu *menu2 =
\r
1838 new GUIMessageMenu(guienv, guiroot, -1,
\r
1839 &g_menumgr, error_message.c_str());
\r
1841 error_message = L"";
\r
1844 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1846 dstream<<"Created main menu"<<std::endl;
\r
1848 while(g_device->run())
\r
1850 // Run global IrrlichtWrapper's main thread processing stuff
\r
1851 g_irrlicht->Run();
\r
1853 if(menu->getStatus() == true)
\r
1856 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1857 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1858 guienv->drawAll();
\r
1859 driver->endScene();
\r
1862 // Break out of menu-game loop to shut down cleanly
\r
1863 if(g_device->run() == false)
\r
1866 dstream<<"Dropping main menu"<<std::endl;
\r
1870 // Delete map if requested
\r
1871 if(menudata.delete_map)
\r
1873 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1875 error_message = L"Delete failed";
\r
1879 playername = wide_to_narrow(menudata.name);
\r
1880 address = wide_to_narrow(menudata.address);
\r
1881 port = stoi(wide_to_narrow(menudata.port));
\r
1882 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1884 // Check for valid parameters, restart menu if invalid.
\r
1885 if(playername == "")
\r
1887 error_message = L"Name required.";
\r
1892 g_settings.set("name", playername);
\r
1893 g_settings.set("address", address);
\r
1894 g_settings.set("port", itos(port));
\r
1895 // Update configuration file
\r
1896 if(configpath != "")
\r
1897 g_settings.updateConfigFile(configpath.c_str());
\r
1899 // Continue to game
\r
1903 // Break out of menu-game loop to shut down cleanly
\r
1904 if(g_device->run() == false)
\r
1908 Make a scope here so that the client and the server and other
\r
1909 stuff gets removed when disconnected or the irrlicht device
\r
1914 // This is set to true at the end of the scope
\r
1915 g_irrlicht->Shutdown(false);
\r
1918 Draw "Loading" screen
\r
1920 const wchar_t *text = L"Loading and connecting...";
\r
1921 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1922 core::vector2d<s32> textsize(300, text_height);
\r
1923 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1925 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1926 text, textrect, false, false);
\r
1927 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1929 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1930 guienv->drawAll();
\r
1931 driver->endScene();
\r
1933 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1937 SharedPtr will delete it when it goes out of scope.
\r
1939 SharedPtr<Server> server;
\r
1940 if(address == ""){
\r
1941 server = new Server(map_dir, hm_params, map_params);
\r
1942 server->start(port);
\r
1949 Client client(device, playername.c_str(), draw_control);
\r
1951 g_client = &client;
\r
1953 Address connect_address(0,0,0,0, port);
\r
1956 connect_address.Resolve("localhost");
\r
1958 connect_address.Resolve(address.c_str());
\r
1960 catch(ResolveError &e)
\r
1962 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1964 error_message = L"Couldn't resolve address";
\r
1965 gui_loadingtext->remove();
\r
1969 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1970 client.connect(connect_address);
\r
1973 while(client.connectedAndInitialized() == false)
\r
1976 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1977 guienv->drawAll();
\r
1978 driver->endScene();
\r
1980 // Update client and server
\r
1984 if(server != NULL)
\r
1985 server->step(0.1);
\r
1991 catch(con::PeerNotFoundException &e)
\r
1993 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1995 error_message = L"Connection timed out.";
\r
1996 gui_loadingtext->remove();
\r
2003 /*scene::ISceneNode* skybox;
\r
2004 skybox = smgr->addSkyBoxSceneNode(
\r
2005 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2006 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2007 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2008 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2009 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2010 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2013 Create the camera node
\r
2016 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2017 0, // Camera parent
\r
2018 v3f(BS*100, BS*2, BS*100), // Look from
\r
2019 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2023 if(camera == NULL)
\r
2026 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2028 camera->setFOV(FOV_ANGLE);
\r
2030 // Just so big a value that everything rendered is visible
\r
2031 camera->setFarValue(100000*BS);
\r
2033 f32 camera_yaw = 0; // "right/left"
\r
2034 f32 camera_pitch = 0; // "up/down"
\r
2040 gui_loadingtext->remove();
\r
2043 Add some gui stuff
\r
2046 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2047 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2049 // Test the text input system
\r
2050 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2052 /*GUIMessageMenu *menu =
\r
2053 new GUIMessageMenu(guienv, guiroot, -1,
\r
2058 // Launch pause menu
\r
2059 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2060 &g_menumgr))->drop();
\r
2063 guitext2->setVisible(true);
\r
2064 guitext_info->setVisible(true);
\r
2065 guitext_chat->setVisible(true);
\r
2068 Some statistics are collected in these
\r
2071 u32 beginscenetime = 0;
\r
2072 u32 scenetime = 0;
\r
2073 u32 endscenetime = 0;
\r
2076 //throw con::PeerNotFoundException("lol");
\r
2082 bool first_loop_after_window_activation = true;
\r
2084 // Time is in milliseconds
\r
2085 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2086 // NOTE: So we have to use getTime() and call run()s between them
\r
2087 u32 lasttime = device->getTimer()->getTime();
\r
2089 while(device->run())
\r
2091 if(g_disconnect_requested)
\r
2093 g_disconnect_requested = false;
\r
2098 Run global IrrlichtWrapper's main thread processing stuff
\r
2100 g_irrlicht->Run();
\r
2103 Random calculations
\r
2105 v2u32 screensize = driver->getScreenSize();
\r
2106 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2108 // Hilight boxes collected during the loop and displayed
\r
2109 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2112 std::wstring infotext;
\r
2114 //TimeTaker //timer1("//timer1");
\r
2116 // Time of frame without fps limit
\r
2120 // not using getRealTime is necessary for wine
\r
2121 u32 time = device->getTimer()->getTime();
\r
2122 if(time > lasttime)
\r
2123 busytime_u32 = time - lasttime;
\r
2126 busytime = busytime_u32 / 1000.0;
\r
2129 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2131 // Absolutelu necessary for wine!
\r
2138 updateViewingRange(busytime, &client);
\r
2145 float fps_max = g_settings.getFloat("fps_max");
\r
2146 u32 frametime_min = 1000./fps_max;
\r
2148 if(busytime_u32 < frametime_min)
\r
2150 u32 sleeptime = frametime_min - busytime_u32;
\r
2151 device->sleep(sleeptime);
\r
2155 // Absolutelu necessary for wine!
\r
2159 Time difference calculation
\r
2161 f32 dtime; // in seconds
\r
2163 u32 time = device->getTimer()->getTime();
\r
2164 if(time > lasttime)
\r
2165 dtime = (time - lasttime) / 1000.0;
\r
2171 Time average and jitter calculation
\r
2174 static f32 dtime_avg1 = 0.0;
\r
2175 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2176 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2178 static f32 dtime_jitter1_max_sample = 0.0;
\r
2179 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2181 static f32 jitter1_max = 0.0;
\r
2182 static f32 counter = 0.0;
\r
2183 if(dtime_jitter1 > jitter1_max)
\r
2184 jitter1_max = dtime_jitter1;
\r
2189 dtime_jitter1_max_sample = jitter1_max;
\r
2190 dtime_jitter1_max_fraction
\r
2191 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2192 jitter1_max = 0.0;
\r
2197 Busytime average and jitter calculation
\r
2200 static f32 busytime_avg1 = 0.0;
\r
2201 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2202 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2204 static f32 busytime_jitter1_max_sample = 0.0;
\r
2205 static f32 busytime_jitter1_min_sample = 0.0;
\r
2207 static f32 jitter1_max = 0.0;
\r
2208 static f32 jitter1_min = 0.0;
\r
2209 static f32 counter = 0.0;
\r
2210 if(busytime_jitter1 > jitter1_max)
\r
2211 jitter1_max = busytime_jitter1;
\r
2212 if(busytime_jitter1 < jitter1_min)
\r
2213 jitter1_min = busytime_jitter1;
\r
2215 if(counter > 0.0){
\r
2217 busytime_jitter1_max_sample = jitter1_max;
\r
2218 busytime_jitter1_min_sample = jitter1_min;
\r
2219 jitter1_max = 0.0;
\r
2220 jitter1_min = 0.0;
\r
2225 Debug info for client
\r
2228 static float counter = 0.0;
\r
2233 client.printDebugInfo(std::cout);
\r
2238 Input handler step()
\r
2240 g_input->step(dtime);
\r
2243 Player speed control
\r
2252 bool a_superspeed,
\r
2255 PlayerControl control(
\r
2256 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2257 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2258 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2259 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2260 g_input->isKeyDown(irr::KEY_SPACE),
\r
2261 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2265 client.setPlayerControl(control);
\r
2269 Process environment
\r
2273 //TimeTaker timer("client.step(dtime)");
\r
2274 client.step(dtime);
\r
2275 //client.step(dtime_avg1);
\r
2278 if(server != NULL)
\r
2280 //TimeTaker timer("server->step(dtime)");
\r
2281 server->step(dtime);
\r
2284 v3f player_position = client.getPlayerPosition();
\r
2286 //TimeTaker //timer2("//timer2");
\r
2289 Mouse and camera control
\r
2292 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2295 device->getCursorControl()->setVisible(false);
\r
2297 if(first_loop_after_window_activation){
\r
2298 //std::cout<<"window active, first loop"<<std::endl;
\r
2299 first_loop_after_window_activation = false;
\r
2302 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2303 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2304 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2305 camera_yaw -= dx*0.2;
\r
2306 camera_pitch += dy*0.2;
\r
2307 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2308 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2310 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2313 device->getCursorControl()->setVisible(true);
\r
2315 //std::cout<<"window inactive"<<std::endl;
\r
2316 first_loop_after_window_activation = true;
\r
2319 camera_yaw = wrapDegrees(camera_yaw);
\r
2320 camera_pitch = wrapDegrees(camera_pitch);
\r
2322 v3f camera_direction = v3f(0,0,1);
\r
2323 camera_direction.rotateYZBy(camera_pitch);
\r
2324 camera_direction.rotateXZBy(camera_yaw);
\r
2326 // This is at the height of the eyes of the current figure
\r
2327 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2328 // This is more like in minecraft
\r
2329 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2331 camera->setPosition(camera_position);
\r
2332 // *100.0 helps in large map coordinates
\r
2333 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2335 if(FIELD_OF_VIEW_TEST){
\r
2336 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2337 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2340 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2341 //TimeTaker timer("client.updateCamera");
\r
2342 client.updateCamera(camera_position, camera_direction);
\r
2346 //TimeTaker //timer3("//timer3");
\r
2349 Calculate what block is the crosshair pointing to
\r
2352 //u32 t1 = device->getTimer()->getRealTime();
\r
2354 //f32 d = 4; // max. distance
\r
2355 f32 d = 4; // max. distance
\r
2356 core::line3d<f32> shootline(camera_position,
\r
2357 camera_position + camera_direction * BS * (d+1));
\r
2359 MapBlockObject *selected_object = client.getSelectedObject
\r
2360 (d*BS, camera_position, shootline);
\r
2363 If it's pointing to a MapBlockObject
\r
2366 if(selected_object != NULL)
\r
2368 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2370 core::aabbox3d<f32> box_on_map
\r
2371 = selected_object->getSelectionBoxOnMap();
\r
2373 hilightboxes.push_back(box_on_map);
\r
2375 infotext = narrow_to_wide(selected_object->infoText());
\r
2377 if(g_input->getLeftClicked())
\r
2379 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2380 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2381 selected_object->getId(), g_selected_item);
\r
2383 else if(g_input->getRightClicked())
\r
2385 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2387 Check if we want to modify the object ourselves
\r
2389 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2391 dstream<<"Sign object right-clicked"<<std::endl;
\r
2393 if(random_input == false)
\r
2395 // Get a new text for it
\r
2397 TextDest *dest = new TextDestSign(
\r
2398 selected_object->getBlock()->getPos(),
\r
2399 selected_object->getId(),
\r
2402 SignObject *sign_object = (SignObject*)selected_object;
\r
2404 std::wstring wtext =
\r
2405 narrow_to_wide(sign_object->getText());
\r
2407 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2413 Otherwise pass the event to the server as-is
\r
2417 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2418 selected_object->getId(), g_selected_item);
\r
2422 else // selected_object == NULL
\r
2426 Find out which node we are pointing at
\r
2429 bool nodefound = false;
\r
2431 v3s16 neighbourpos;
\r
2432 core::aabbox3d<f32> nodehilightbox;
\r
2433 f32 mindistance = BS * 1001;
\r
2435 v3s16 pos_i = floatToInt(player_position);
\r
2437 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2441 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2442 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2443 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2444 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2445 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2446 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2448 for(s16 y = ystart; y <= yend; y++)
\r
2449 for(s16 z = zstart; z <= zend; z++)
\r
2450 for(s16 x = xstart; x <= xend; x++)
\r
2455 n = client.getNode(v3s16(x,y,z));
\r
2456 if(content_pointable(n.d) == false)
\r
2459 catch(InvalidPositionException &e)
\r
2465 v3f npf = intToFloat(np);
\r
2470 v3s16(0,0,1), // back
\r
2471 v3s16(0,1,0), // top
\r
2472 v3s16(1,0,0), // right
\r
2473 v3s16(0,0,-1), // front
\r
2474 v3s16(0,-1,0), // bottom
\r
2475 v3s16(-1,0,0), // left
\r
2481 if(n.d == CONTENT_TORCH)
\r
2483 v3s16 dir = unpackDir(n.dir);
\r
2484 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2485 dir_f *= BS/2 - BS/6 - BS/20;
\r
2486 v3f cpf = npf + dir_f;
\r
2487 f32 distance = (cpf - camera_position).getLength();
\r
2489 core::aabbox3d<f32> box;
\r
2492 if(dir == v3s16(0,-1,0))
\r
2494 box = core::aabbox3d<f32>(
\r
2495 npf - v3f(BS/6, BS/2, BS/6),
\r
2496 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2500 else if(dir == v3s16(0,1,0))
\r
2502 box = core::aabbox3d<f32>(
\r
2503 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2504 npf + v3f(BS/6, BS/2, BS/6)
\r
2510 box = core::aabbox3d<f32>(
\r
2511 cpf - v3f(BS/6, BS/3, BS/6),
\r
2512 cpf + v3f(BS/6, BS/3, BS/6)
\r
2516 if(distance < mindistance)
\r
2518 if(box.intersectsWithLine(shootline))
\r
2522 neighbourpos = np;
\r
2523 mindistance = distance;
\r
2524 nodehilightbox = box;
\r
2533 for(u16 i=0; i<6; i++)
\r
2535 v3f dir_f = v3f(dirs[i].X,
\r
2536 dirs[i].Y, dirs[i].Z);
\r
2537 v3f centerpoint = npf + dir_f * BS/2;
\r
2539 (centerpoint - camera_position).getLength();
\r
2541 if(distance < mindistance)
\r
2543 core::CMatrix4<f32> m;
\r
2544 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2546 // This is the back face
\r
2547 v3f corners[2] = {
\r
2548 v3f(BS/2, BS/2, BS/2),
\r
2549 v3f(-BS/2, -BS/2, BS/2+d)
\r
2552 for(u16 j=0; j<2; j++)
\r
2554 m.rotateVect(corners[j]);
\r
2555 corners[j] += npf;
\r
2558 core::aabbox3d<f32> facebox(corners[0]);
\r
2559 facebox.addInternalPoint(corners[1]);
\r
2561 if(facebox.intersectsWithLine(shootline))
\r
2565 neighbourpos = np + dirs[i];
\r
2566 mindistance = distance;
\r
2568 //nodehilightbox = facebox;
\r
2570 const float d = 0.502;
\r
2571 core::aabbox3d<f32> nodebox
\r
2572 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2573 v3f nodepos_f = intToFloat(nodepos);
\r
2574 nodebox.MinEdge += nodepos_f;
\r
2575 nodebox.MaxEdge += nodepos_f;
\r
2576 nodehilightbox = nodebox;
\r
2578 } // if distance < mindistance
\r
2580 } // regular block
\r
2583 static float nodig_delay_counter = 0.0;
\r
2587 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2589 static float dig_time = 0.0;
\r
2590 static u16 dig_index = 0;
\r
2592 // Visualize selection
\r
2594 hilightboxes.push_back(nodehilightbox);
\r
2598 if(g_input->getLeftReleased())
\r
2600 client.clearTempMod(nodepos);
\r
2604 if(nodig_delay_counter > 0.0)
\r
2606 nodig_delay_counter -= dtime;
\r
2610 if(nodepos != nodepos_old)
\r
2612 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2613 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2615 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2617 client.clearTempMod(nodepos_old);
\r
2622 if(g_input->getLeftClicked() ||
\r
2623 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2625 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2626 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2628 if(g_input->getLeftClicked())
\r
2630 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2632 if(g_input->getLeftState())
\r
2634 MapNode n = client.getNode(nodepos);
\r
2636 // Get tool name. Default is "" = bare hands
\r
2637 std::string toolname = "";
\r
2638 InventoryList *mlist = local_inventory.getList("main");
\r
2641 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2642 if(item && (std::string)item->getName() == "ToolItem")
\r
2644 ToolItem *titem = (ToolItem*)item;
\r
2645 toolname = titem->getToolName();
\r
2649 // Get digging properties for material and tool
\r
2650 u8 material = n.d;
\r
2651 DiggingProperties prop =
\r
2652 getDiggingProperties(material, toolname);
\r
2654 float dig_time_complete = 0.0;
\r
2656 if(prop.diggable == false)
\r
2658 /*dstream<<"Material "<<(int)material
\r
2659 <<" not diggable with \""
\r
2660 <<toolname<<"\""<<std::endl;*/
\r
2661 // I guess nobody will wait for this long
\r
2662 dig_time_complete = 10000000.0;
\r
2666 dig_time_complete = prop.time;
\r
2669 if(dig_time_complete >= 0.001)
\r
2671 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2672 * dig_time/dig_time_complete);
\r
2674 // This is for torches
\r
2677 dig_index = CRACK_ANIMATION_LENGTH;
\r
2680 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2682 //TimeTaker timer("client.setTempMod");
\r
2683 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2684 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2688 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2689 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2690 client.clearTempMod(nodepos);
\r
2691 client.removeNode(nodepos);
\r
2695 nodig_delay_counter = dig_time_complete
\r
2696 / (float)CRACK_ANIMATION_LENGTH;
\r
2698 // We don't want a corresponding delay to
\r
2699 // very time consuming nodes
\r
2700 if(nodig_delay_counter > 0.5)
\r
2702 nodig_delay_counter = 0.5;
\r
2704 // We want a slight delay to very little
\r
2705 // time consuming nodes
\r
2706 float mindelay = 0.15;
\r
2707 if(nodig_delay_counter < mindelay)
\r
2709 nodig_delay_counter = mindelay;
\r
2713 dig_time += dtime;
\r
2717 if(g_input->getRightClicked())
\r
2719 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2720 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2723 nodepos_old = nodepos;
\r
2728 } // selected_object == NULL
\r
2730 g_input->resetLeftClicked();
\r
2731 g_input->resetRightClicked();
\r
2733 if(g_input->getLeftReleased())
\r
2735 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2737 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2739 if(g_input->getRightReleased())
\r
2741 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2745 g_input->resetLeftReleased();
\r
2746 g_input->resetRightReleased();
\r
2749 Calculate stuff for drawing
\r
2752 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2754 u32 daynight_ratio = client.getDayNightRatio();
\r
2755 /*video::SColor bgcolor = video::SColor(
\r
2757 skycolor.getRed() * daynight_ratio / 1000,
\r
2758 skycolor.getGreen() * daynight_ratio / 1000,
\r
2759 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2761 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2762 video::SColor bgcolor = video::SColor(
\r
2764 skycolor.getRed() * l / 255,
\r
2765 skycolor.getGreen() * l / 255,
\r
2766 skycolor.getBlue() * l / 255);
\r
2772 if(g_settings.getBool("enable_fog") == true)
\r
2774 f32 range = draw_control.wanted_range * BS;
\r
2775 if(draw_control.range_all)
\r
2776 range = 100000*BS;
\r
2780 video::EFT_FOG_LINEAR,
\r
2784 false, // pixel fog
\r
2785 false // range fog
\r
2791 Update gui stuff (0ms)
\r
2794 //TimeTaker guiupdatetimer("Gui updating");
\r
2797 wchar_t temptext[150];
\r
2799 static float drawtime_avg = 0;
\r
2800 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2801 static float beginscenetime_avg = 0;
\r
2802 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2803 static float scenetime_avg = 0;
\r
2804 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2805 static float endscenetime_avg = 0;
\r
2806 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2808 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2810 L", R: range_all=%i"
\r
2812 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2814 draw_control.range_all,
\r
2816 beginscenetime_avg,
\r
2821 guitext->setText(temptext);
\r
2825 wchar_t temptext[150];
\r
2826 swprintf(temptext, 150,
\r
2827 L"(% .1f, % .1f, % .1f)"
\r
2828 L" (% .3f < btime_jitter < % .3f"
\r
2829 L", dtime_jitter = % .1f %%"
\r
2830 L", v_range = %.1f)",
\r
2831 player_position.X/BS,
\r
2832 player_position.Y/BS,
\r
2833 player_position.Z/BS,
\r
2834 busytime_jitter1_min_sample,
\r
2835 busytime_jitter1_max_sample,
\r
2836 dtime_jitter1_max_fraction * 100.0,
\r
2837 draw_control.wanted_range
\r
2840 guitext2->setText(temptext);
\r
2844 guitext_info->setText(infotext.c_str());
\r
2848 Get chat messages from client
\r
2851 // Get new messages
\r
2852 std::wstring message;
\r
2853 while(client.getChatMessage(message))
\r
2855 chat_lines.push_back(ChatLine(message));
\r
2856 /*if(chat_lines.size() > 6)
\r
2858 core::list<ChatLine>::Iterator
\r
2859 i = chat_lines.begin();
\r
2860 chat_lines.erase(i);
\r
2863 // Append them to form the whole static text and throw
\r
2864 // it to the gui element
\r
2865 std::wstring whole;
\r
2866 // This will correspond to the line number counted from
\r
2867 // top to bottom, from size-1 to 0
\r
2868 s16 line_number = chat_lines.size();
\r
2869 // Count of messages to be removed from the top
\r
2870 u16 to_be_removed_count = 0;
\r
2871 for(core::list<ChatLine>::Iterator
\r
2872 i = chat_lines.begin();
\r
2873 i != chat_lines.end(); i++)
\r
2875 // After this, line number is valid for this loop
\r
2878 (*i).age += dtime;
\r
2880 This results in a maximum age of 60*6 to the
\r
2881 lowermost line and a maximum of 6 lines
\r
2883 float allowed_age = (6-line_number) * 60.0;
\r
2885 if((*i).age > allowed_age)
\r
2887 to_be_removed_count++;
\r
2890 whole += (*i).text + L'\n';
\r
2892 for(u16 i=0; i<to_be_removed_count; i++)
\r
2894 core::list<ChatLine>::Iterator
\r
2895 it = chat_lines.begin();
\r
2896 chat_lines.erase(it);
\r
2898 guitext_chat->setText(whole.c_str());
\r
2899 // Update gui element size and position
\r
2900 core::rect<s32> rect(
\r
2902 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2903 screensize.X - 10,
\r
2906 guitext_chat->setRelativePosition(rect);
\r
2908 if(chat_lines.size() == 0)
\r
2909 guitext_chat->setVisible(false);
\r
2911 guitext_chat->setVisible(true);
\r
2918 static u16 old_selected_item = 65535;
\r
2919 if(client.getLocalInventoryUpdated()
\r
2920 || g_selected_item != old_selected_item)
\r
2922 old_selected_item = g_selected_item;
\r
2923 //std::cout<<"Updating local inventory"<<std::endl;
\r
2924 client.getLocalInventory(local_inventory);
\r
2925 quick_inventory->setSelection(g_selected_item);
\r
2926 quick_inventory->update();
\r
2930 Send actions returned by the inventory menu
\r
2932 while(inventory_action_queue.size() != 0)
\r
2934 InventoryAction *a = inventory_action_queue.pop_front();
\r
2936 client.sendInventoryAction(a);
\r
2945 TimeTaker drawtimer("Drawing");
\r
2949 TimeTaker timer("beginScene");
\r
2950 driver->beginScene(true, true, bgcolor);
\r
2951 //driver->beginScene(false, true, bgcolor);
\r
2952 beginscenetime = timer.stop(true);
\r
2957 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2960 TimeTaker timer("smgr");
\r
2962 scenetime = timer.stop(true);
\r
2966 //TimeTaker timer9("auxiliary drawings");
\r
2970 //TimeTaker //timer10("//timer10");
\r
2972 video::SMaterial m;
\r
2973 //m.Thickness = 10;
\r
2975 m.Lighting = false;
\r
2976 driver->setMaterial(m);
\r
2978 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2980 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2981 i != hilightboxes.end(); i++)
\r
2983 /*std::cout<<"hilightbox min="
\r
2984 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2986 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2988 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2994 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2995 displaycenter + core::vector2d<s32>(10,0),
\r
2996 video::SColor(255,255,255,255));
\r
2997 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2998 displaycenter + core::vector2d<s32>(0,10),
\r
2999 video::SColor(255,255,255,255));
\r
3004 //TimeTaker //timer11("//timer11");
\r
3010 guienv->drawAll();
\r
3014 TimeTaker timer("endScene");
\r
3015 driver->endScene();
\r
3016 endscenetime = timer.stop(true);
\r
3019 drawtime = drawtimer.stop(true);
\r
3025 static s16 lastFPS = 0;
\r
3026 //u16 fps = driver->getFPS();
\r
3027 u16 fps = (1.0/dtime_avg1);
\r
3029 if (lastFPS != fps)
\r
3031 core::stringw str = L"Minetest [";
\r
3032 str += driver->getName();
\r
3036 device->setWindowCaption(str.c_str());
\r
3042 device->yield();*/
\r
3045 delete quick_inventory;
\r
3048 Disable texture fetches and other stuff that is queued
\r
3049 to be processed by the main loop.
\r
3051 This has to be done before client goes out of scope.
\r
3053 g_irrlicht->Shutdown(true);
\r
3055 } // client and server are deleted at this point
\r
3058 catch(con::PeerNotFoundException &e)
\r
3060 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3061 error_message = L"Connection timed out.";
\r
3064 } // Menu-game loop
\r
3069 In the end, delete the Irrlicht device.
\r
3074 Update configuration file
\r
3076 /*if(configpath != "")
\r
3078 g_settings.updateConfigFile(configpath.c_str());
\r
3081 END_DEBUG_EXCEPTION_HANDLER
\r
3083 debugstreams_deinit();
\r