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
318 Doing now (most important at the top):
\r
319 --------------------------------------
\r
323 * Perlin noise stuff sucks in heightmap generation, fix it
\r
324 * Create perlin noise functions and use them to get natural randomness
\r
325 in everything. No need for attributes or fractal terrain.
\r
326 * Do something about AttributeDatabase/List being too slow
\r
328 * Save chunk metadata on disk
\r
329 * Remove all kinds of systems that are made redundant by the new map
\r
331 - Sector heightmaps? At least they should be made redundant.
\r
333 * Fix the strange mineral occurences
\r
334 - Do they appear anymore?
\r
335 * Make server find the spawning place from the real map data, not from
\r
337 - But the changing borders of chunk have to be avoided, because
\r
338 there is time to generate only one chunk.
\r
339 * only_from_disk might not work anymore - check and fix it.
\r
340 * Make the generator to run in background and not blocking block
\r
341 placement and transfer
\r
342 * Add some kind of erosion and other stuff that now is possible
\r
343 * Make client to fetch stuff asynchronously
\r
344 - Needs method SyncProcessData
\r
345 * What is the problem with the server constantly saving one or a few
\r
346 blocks? List the first saved block, maybe it explains.
\r
347 - Does it still do this?
\r
348 * Water doesn't start flowing after map generation like it should
\r
349 - Are there still problems?
\r
350 * Better water generation (spread it to underwater caverns)
\r
351 * When generating a chunk and the neighboring chunk doesn't have mud
\r
352 and stuff yet and the ground is fairly flat, the mud will flow to
\r
353 the other chunk making nasty straight walls when the other chunk
\r
354 is generated. Fix it.
\r
355 * Save map seed to a metafile (with version information)
\r
356 - Remove master heightmap
\r
358 ======================================================================
\r
363 Setting this to 1 enables a special camera mode that forces
\r
364 the renderers to think that the camera statically points from
\r
365 the starting place to a static direction.
\r
367 This allows one to move around with the player and see what
\r
368 is actually drawn behind solid things and behind the player.
\r
370 #define FIELD_OF_VIEW_TEST 0
\r
374 #pragma message ("Disabling unit tests")
\r
376 #warning "Disabling unit tests"
\r
378 // Disable unit tests
\r
379 #define ENABLE_TESTS 0
\r
381 // Enable unit tests
\r
382 #define ENABLE_TESTS 1
\r
386 #pragma comment(lib, "Irrlicht.lib")
\r
387 //#pragma comment(lib, "jthread.lib")
\r
388 #pragma comment(lib, "zlibwapi.lib")
\r
389 #pragma comment(lib, "Shell32.lib")
\r
390 // This would get rid of the console window
\r
391 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
394 #include <iostream>
\r
396 #include <jmutexautolock.h>
\r
397 #include <locale.h>
\r
398 #include "common_irrlicht.h"
\r
401 #include "player.h"
\r
404 #include "environment.h"
\r
405 #include "server.h"
\r
406 #include "client.h"
\r
407 #include "serialization.h"
\r
408 #include "constants.h"
\r
409 #include "strfnd.h"
\r
410 #include "porting.h"
\r
411 #include "irrlichtwrapper.h"
\r
412 #include "gettime.h"
\r
413 #include "porting.h"
\r
414 #include "guiPauseMenu.h"
\r
415 #include "guiInventoryMenu.h"
\r
416 #include "guiTextInputMenu.h"
\r
417 #include "materials.h"
\r
418 #include "guiMessageMenu.h"
\r
419 #include "filesys.h"
\r
420 #include "config.h"
\r
421 #include "guiMainMenu.h"
\r
422 #include "mineral.h"
\r
424 IrrlichtWrapper *g_irrlicht;
\r
426 MapDrawControl draw_control;
\r
430 These are loaded from the config file.
\r
433 Settings g_settings;
\r
435 extern void set_default_settings();
\r
441 IrrlichtDevice *g_device = NULL;
\r
442 Client *g_client = NULL;
\r
448 gui::IGUIEnvironment* guienv = NULL;
\r
449 gui::IGUIStaticText *guiroot = NULL;
\r
451 class MainMenuManager : public IMenuManager
\r
454 virtual void createdMenu(GUIModalMenu *menu)
\r
456 for(core::list<GUIModalMenu*>::Iterator
\r
457 i = m_stack.begin();
\r
458 i != m_stack.end(); i++)
\r
460 assert(*i != menu);
\r
463 if(m_stack.size() != 0)
\r
464 (*m_stack.getLast())->setVisible(false);
\r
465 m_stack.push_back(menu);
\r
468 virtual void deletingMenu(GUIModalMenu *menu)
\r
470 // Remove all entries if there are duplicates
\r
471 bool removed_entry;
\r
473 removed_entry = false;
\r
474 for(core::list<GUIModalMenu*>::Iterator
\r
475 i = m_stack.begin();
\r
476 i != m_stack.end(); i++)
\r
481 removed_entry = true;
\r
485 }while(removed_entry);
\r
487 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
488 assert(*i == menu);
\r
489 m_stack.erase(i);*/
\r
491 if(m_stack.size() != 0)
\r
492 (*m_stack.getLast())->setVisible(true);
\r
497 return m_stack.size();
\r
500 core::list<GUIModalMenu*> m_stack;
\r
503 MainMenuManager g_menumgr;
\r
505 bool noMenuActive()
\r
507 return (g_menumgr.menuCount() == 0);
\r
510 bool g_disconnect_requested = false;
\r
512 class MainGameCallback : public IGameCallback
\r
515 virtual void exitToOS()
\r
517 g_device->closeDevice();
\r
520 virtual void disconnect()
\r
522 g_disconnect_requested = true;
\r
526 MainGameCallback g_gamecallback;
\r
528 // Inventory actions from the menu are buffered here before sending
\r
529 Queue<InventoryAction*> inventory_action_queue;
\r
530 // This is a copy of the inventory that the client's environment has
\r
531 Inventory local_inventory;
\r
533 u16 g_selected_item = 0;
\r
540 std::ostream *dout_con_ptr = &dummyout;
\r
541 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
542 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
543 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
544 //std::ostream *dout_con_ptr = &dstream;
\r
545 //std::ostream *derr_con_ptr = &dstream;
\r
548 std::ostream *dout_server_ptr = &dstream;
\r
549 std::ostream *derr_server_ptr = &dstream;
\r
552 std::ostream *dout_client_ptr = &dstream;
\r
553 std::ostream *derr_client_ptr = &dstream;
\r
556 gettime.h implementation
\r
562 Use irrlicht because it is more precise than porting.h's
\r
565 if(g_irrlicht == NULL)
\r
567 return g_irrlicht->getTime();
\r
574 struct TextDestSign : public TextDest
\r
576 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
578 m_blockpos = blockpos;
\r
582 void gotText(std::wstring text)
\r
584 std::string ntext = wide_to_narrow(text);
\r
585 dstream<<"Changing text of a sign object: "
\r
586 <<ntext<<std::endl;
\r
587 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
595 struct TextDestChat : public TextDest
\r
597 TextDestChat(Client *client)
\r
601 void gotText(std::wstring text)
\r
603 m_client->sendChatMessage(text);
\r
604 m_client->addChatMessage(text);
\r
610 class MyEventReceiver : public IEventReceiver
\r
613 // This is the one method that we have to implement
\r
614 virtual bool OnEvent(const SEvent& event)
\r
617 React to nothing here if a menu is active
\r
619 if(noMenuActive() == false)
\r
625 // Remember whether each key is down or up
\r
626 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
628 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
630 if(event.KeyInput.PressedDown)
\r
632 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
638 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
640 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
642 dstream<<DTIME<<"MyEventReceiver: "
\r
643 <<"Launching pause menu"<<std::endl;
\r
644 // It will delete itself by itself
\r
645 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
646 &g_menumgr))->drop();
\r
649 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
651 dstream<<DTIME<<"MyEventReceiver: "
\r
652 <<"Launching inventory"<<std::endl;
\r
653 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
654 &local_inventory, &inventory_action_queue,
\r
655 &g_menumgr))->drop();
\r
658 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
660 TextDest *dest = new TextDestChat(g_client);
\r
662 (new GUITextInputMenu(guienv, guiroot, -1,
\r
668 // Material selection
\r
669 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
671 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
674 g_selected_item = 0;
\r
675 dstream<<DTIME<<"Selected item: "
\r
676 <<g_selected_item<<std::endl;
\r
679 // Viewing range selection
\r
680 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
682 if(draw_control.range_all)
\r
684 draw_control.range_all = false;
\r
685 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
689 draw_control.range_all = true;
\r
690 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
694 // Print debug stacks
\r
695 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
697 dstream<<"-----------------------------------------"
\r
699 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
700 dstream<<"-----------------------------------------"
\r
702 debug_stacks_print();
\r
707 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
709 if(noMenuActive() == false)
\r
711 left_active = false;
\r
712 middle_active = false;
\r
713 right_active = false;
\r
717 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
718 left_active = event.MouseInput.isLeftPressed();
\r
719 middle_active = event.MouseInput.isMiddlePressed();
\r
720 right_active = event.MouseInput.isRightPressed();
\r
722 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
724 leftclicked = true;
\r
726 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
728 rightclicked = true;
\r
730 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
732 leftreleased = true;
\r
734 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
736 rightreleased = true;
\r
738 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
740 /*dstream<<"event.MouseInput.Wheel="
\r
741 <<event.MouseInput.Wheel<<std::endl;*/
\r
742 if(event.MouseInput.Wheel < 0)
\r
744 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
747 g_selected_item = 0;
\r
749 else if(event.MouseInput.Wheel > 0)
\r
751 if(g_selected_item > 0)
\r
754 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
763 // This is used to check whether a key is being held down
\r
764 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
766 return keyIsDown[keyCode];
\r
771 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
772 keyIsDown[i] = false;
\r
774 leftclicked = false;
\r
775 rightclicked = false;
\r
776 leftreleased = false;
\r
777 rightreleased = false;
\r
779 left_active = false;
\r
780 middle_active = false;
\r
781 right_active = false;
\r
792 bool rightreleased;
\r
795 bool middle_active;
\r
799 // We use this array to store the current state of each key
\r
800 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
803 IrrlichtDevice *m_device;
\r
812 virtual ~InputHandler()
\r
816 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
818 virtual v2s32 getMousePos() = 0;
\r
819 virtual void setMousePos(s32 x, s32 y) = 0;
\r
821 virtual bool getLeftState() = 0;
\r
822 virtual bool getRightState() = 0;
\r
824 virtual bool getLeftClicked() = 0;
\r
825 virtual bool getRightClicked() = 0;
\r
826 virtual void resetLeftClicked() = 0;
\r
827 virtual void resetRightClicked() = 0;
\r
829 virtual bool getLeftReleased() = 0;
\r
830 virtual bool getRightReleased() = 0;
\r
831 virtual void resetLeftReleased() = 0;
\r
832 virtual void resetRightReleased() = 0;
\r
834 virtual void step(float dtime) {};
\r
836 virtual void clear() {};
\r
839 InputHandler *g_input = NULL;
\r
841 class RealInputHandler : public InputHandler
\r
844 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
846 m_receiver(receiver)
\r
849 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
851 return m_receiver->IsKeyDown(keyCode);
\r
853 virtual v2s32 getMousePos()
\r
855 return m_device->getCursorControl()->getPosition();
\r
857 virtual void setMousePos(s32 x, s32 y)
\r
859 m_device->getCursorControl()->setPosition(x, y);
\r
862 virtual bool getLeftState()
\r
864 return m_receiver->left_active;
\r
866 virtual bool getRightState()
\r
868 return m_receiver->right_active;
\r
871 virtual bool getLeftClicked()
\r
873 return m_receiver->leftclicked;
\r
875 virtual bool getRightClicked()
\r
877 return m_receiver->rightclicked;
\r
879 virtual void resetLeftClicked()
\r
881 m_receiver->leftclicked = false;
\r
883 virtual void resetRightClicked()
\r
885 m_receiver->rightclicked = false;
\r
888 virtual bool getLeftReleased()
\r
890 return m_receiver->leftreleased;
\r
892 virtual bool getRightReleased()
\r
894 return m_receiver->rightreleased;
\r
896 virtual void resetLeftReleased()
\r
898 m_receiver->leftreleased = false;
\r
900 virtual void resetRightReleased()
\r
902 m_receiver->rightreleased = false;
\r
907 resetRightClicked();
\r
908 resetLeftClicked();
\r
911 IrrlichtDevice *m_device;
\r
912 MyEventReceiver *m_receiver;
\r
915 class RandomInputHandler : public InputHandler
\r
918 RandomInputHandler()
\r
920 leftclicked = false;
\r
921 rightclicked = false;
\r
922 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
923 keydown[i] = false;
\r
925 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
927 return keydown[keyCode];
\r
929 virtual v2s32 getMousePos()
\r
933 virtual void setMousePos(s32 x, s32 y)
\r
935 mousepos = v2s32(x,y);
\r
938 virtual bool getLeftState()
\r
942 virtual bool getRightState()
\r
947 virtual bool getLeftClicked()
\r
949 return leftclicked;
\r
951 virtual bool getRightClicked()
\r
953 return rightclicked;
\r
955 virtual void resetLeftClicked()
\r
957 leftclicked = false;
\r
959 virtual void resetRightClicked()
\r
961 rightclicked = false;
\r
964 virtual bool getLeftReleased()
\r
968 virtual bool getRightReleased()
\r
972 virtual void resetLeftReleased()
\r
975 virtual void resetRightReleased()
\r
979 virtual void step(float dtime)
\r
982 static float counter1 = 0;
\r
986 counter1 = 0.1*Rand(1,10);
\r
987 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
988 g_selected_material++;
\r
990 g_selected_material = 0;*/
\r
991 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
994 g_selected_item = 0;
\r
998 static float counter1 = 0;
\r
1000 if(counter1 < 0.0)
\r
1002 counter1 = 0.1*Rand(1, 40);
\r
1003 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
1007 static float counter1 = 0;
\r
1008 counter1 -= dtime;
\r
1009 if(counter1 < 0.0)
\r
1011 counter1 = 0.1*Rand(1, 40);
\r
1012 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
1016 static float counter1 = 0;
\r
1017 counter1 -= dtime;
\r
1018 if(counter1 < 0.0)
\r
1020 counter1 = 0.1*Rand(1, 40);
\r
1021 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1025 static float counter1 = 0;
\r
1026 counter1 -= dtime;
\r
1027 if(counter1 < 0.0)
\r
1029 counter1 = 0.1*Rand(1, 40);
\r
1030 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1034 static float counter1 = 0;
\r
1035 counter1 -= dtime;
\r
1036 if(counter1 < 0.0)
\r
1038 counter1 = 0.1*Rand(1, 20);
\r
1039 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1043 static float counter1 = 0;
\r
1044 counter1 -= dtime;
\r
1045 if(counter1 < 0.0)
\r
1047 counter1 = 0.1*Rand(1, 30);
\r
1048 leftclicked = true;
\r
1052 static float counter1 = 0;
\r
1053 counter1 -= dtime;
\r
1054 if(counter1 < 0.0)
\r
1056 counter1 = 0.1*Rand(1, 20);
\r
1057 rightclicked = true;
\r
1060 mousepos += mousespeed;
\r
1063 s32 Rand(s32 min, s32 max)
\r
1065 return (myrand()%(max-min+1))+min;
\r
1068 bool keydown[KEY_KEY_CODES_COUNT];
\r
1072 bool rightclicked;
\r
1075 void updateViewingRange(f32 frametime_in, Client *client)
\r
1077 if(draw_control.range_all == true)
\r
1080 static f32 added_frametime = 0;
\r
1081 static s16 added_frames = 0;
\r
1083 added_frametime += frametime_in;
\r
1084 added_frames += 1;
\r
1086 // Actually this counter kind of sucks because frametime is busytime
\r
1087 static f32 counter = 0;
\r
1088 counter -= frametime_in;
\r
1094 /*dstream<<__FUNCTION_NAME
\r
1095 <<": Collected "<<added_frames<<" frames, total of "
\r
1096 <<added_frametime<<"s."<<std::endl;*/
\r
1098 /*dstream<<"draw_control.blocks_drawn="
\r
1099 <<draw_control.blocks_drawn
\r
1100 <<", draw_control.blocks_would_have_drawn="
\r
1101 <<draw_control.blocks_would_have_drawn
\r
1104 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1105 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1107 draw_control.wanted_min_range = range_min;
\r
1108 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1110 float block_draw_ratio = 1.0;
\r
1111 if(draw_control.blocks_would_have_drawn != 0)
\r
1113 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1114 / (float)draw_control.blocks_would_have_drawn;
\r
1117 // Calculate the average frametime in the case that all wanted
\r
1118 // blocks had been drawn
\r
1119 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1121 added_frametime = 0.0;
\r
1124 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1125 float wanted_frametime = 1.0 / wanted_fps;
\r
1127 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1128 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1130 // If needed frametime change is very small, just return
\r
1131 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1133 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1137 float range = draw_control.wanted_range;
\r
1138 float new_range = range;
\r
1140 static s16 range_old = 0;
\r
1141 static f32 frametime_old = 0;
\r
1143 float d_range = range - range_old;
\r
1144 f32 d_frametime = frametime - frametime_old;
\r
1145 // A sane default of 30ms per 50 nodes of range
\r
1146 static f32 time_per_range = 30. / 50;
\r
1149 time_per_range = d_frametime / d_range;
\r
1152 // The minimum allowed calculated frametime-range derivative:
\r
1153 // Practically this sets the maximum speed of changing the range.
\r
1154 // The lower this value, the higher the maximum changing speed.
\r
1155 // A low value here results in wobbly range (0.001)
\r
1156 // A high value here results in slow changing range (0.0025)
\r
1157 // SUGG: This could be dynamically adjusted so that when
\r
1158 // the camera is turning, this is lower
\r
1159 //float min_time_per_range = 0.0015;
\r
1160 float min_time_per_range = 0.0010;
\r
1161 //float min_time_per_range = 0.05 / range;
\r
1162 if(time_per_range < min_time_per_range)
\r
1164 time_per_range = min_time_per_range;
\r
1165 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1169 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1172 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1173 // Dampen the change a bit to kill oscillations
\r
1174 //wanted_range_change *= 0.9;
\r
1175 //wanted_range_change *= 0.75;
\r
1176 wanted_range_change *= 0.5;
\r
1177 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1179 // If needed range change is very small, just return
\r
1180 if(fabs(wanted_range_change) < 0.001)
\r
1182 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1186 new_range += wanted_range_change;
\r
1187 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1189 //float new_range_unclamped = new_range;
\r
1190 if(new_range < range_min)
\r
1191 new_range = range_min;
\r
1192 if(new_range > range_max)
\r
1193 new_range = range_max;
\r
1195 /*if(new_range != new_range_unclamped)
\r
1196 dstream<<", clamped to "<<new_range<<std::endl;
\r
1198 dstream<<std::endl;*/
\r
1200 draw_control.wanted_range = new_range;
\r
1202 range_old = new_range;
\r
1203 frametime_old = frametime;
\r
1206 class GUIQuickInventory : public IEventReceiver
\r
1209 GUIQuickInventory(
\r
1210 gui::IGUIEnvironment* env,
\r
1211 gui::IGUIElement* parent,
\r
1214 Inventory *inventory):
\r
1215 m_itemcount(itemcount),
\r
1216 m_inventory(inventory)
\r
1218 core::rect<s32> imgsize(0,0,48,48);
\r
1219 core::rect<s32> textsize(0,0,48,16);
\r
1220 v2s32 spacing(0, 64);
\r
1221 for(s32 i=0; i<m_itemcount; i++)
\r
1223 m_images.push_back(env->addImage(
\r
1224 imgsize + pos + spacing*i
\r
1226 m_images[i]->setScaleImage(true);
\r
1227 m_texts.push_back(env->addStaticText(
\r
1229 textsize + pos + spacing*i,
\r
1232 m_texts[i]->setBackgroundColor(
\r
1233 video::SColor(128,0,0,0));
\r
1234 m_texts[i]->setTextAlignment(
\r
1235 gui::EGUIA_CENTER,
\r
1236 gui::EGUIA_UPPERLEFT);
\r
1240 ~GUIQuickInventory()
\r
1242 for(u32 i=0; i<m_texts.size(); i++)
\r
1244 m_texts[i]->remove();
\r
1246 for(u32 i=0; i<m_images.size(); i++)
\r
1248 m_images[i]->remove();
\r
1252 virtual bool OnEvent(const SEvent& event)
\r
1257 void setSelection(s32 i)
\r
1266 start = m_selection - m_itemcount / 2;
\r
1268 InventoryList *mainlist = m_inventory->getList("main");
\r
1270 for(s32 i=0; i<m_itemcount; i++)
\r
1272 s32 j = i + start;
\r
1274 if(j > (s32)mainlist->getSize() - 1)
\r
1275 j -= mainlist->getSize();
\r
1277 j += mainlist->getSize();
\r
1279 InventoryItem *item = mainlist->getItem(j);
\r
1283 m_images[i]->setImage(NULL);
\r
1286 if(m_selection == j)
\r
1287 swprintf(t, 10, L"<-");
\r
1289 swprintf(t, 10, L"");
\r
1290 m_texts[i]->setText(t);
\r
1292 // The next ifs will segfault with a NULL pointer
\r
1297 m_images[i]->setImage(item->getImage());
\r
1300 if(m_selection == j)
\r
1301 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1303 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1304 m_texts[i]->setText(t);
\r
1310 core::array<gui::IGUIStaticText*> m_texts;
\r
1311 core::array<gui::IGUIImage*> m_images;
\r
1312 Inventory *m_inventory;
\r
1323 ChatLine(const std::wstring &a_text):
\r
1329 std::wstring text;
\r
1332 // These are defined global so that they're not optimized too much.
\r
1333 // Can't change them to volatile.
\r
1338 std::string tempstring;
\r
1339 std::string tempstring2;
\r
1344 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1345 TimeTaker timer("Testing std::string speed");
\r
1346 const u32 jj = 10000;
\r
1347 for(u32 j=0; j<jj; j++)
\r
1351 const u32 ii = 10;
\r
1352 for(u32 i=0; i<ii; i++){
\r
1353 tempstring2 += "asd";
\r
1355 for(u32 i=0; i<ii+1; i++){
\r
1356 tempstring += "asd";
\r
1357 if(tempstring == tempstring2)
\r
1363 dstream<<"All of the following tests should take around 100ms each."
\r
1367 TimeTaker timer("Testing floating-point conversion speed");
\r
1369 for(u32 i=0; i<4000000; i++){
\r
1376 TimeTaker timer("Testing floating-point vector speed");
\r
1378 tempv3f1 = v3f(1,2,3);
\r
1379 tempv3f2 = v3f(4,5,6);
\r
1380 for(u32 i=0; i<10000000; i++){
\r
1381 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1382 tempv3f2 += v3f(7,8,9);
\r
1387 TimeTaker timer("Testing core::map speed");
\r
1389 core::map<v2s16, f32> map1;
\r
1392 for(s16 y=0; y<ii; y++){
\r
1393 for(s16 x=0; x<ii; x++){
\r
1394 map1.insert(v2s16(x,y), tempf);
\r
1398 for(s16 y=ii-1; y>=0; y--){
\r
1399 for(s16 x=0; x<ii; x++){
\r
1400 tempf = map1[v2s16(x,y)];
\r
1406 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1407 TimeTaker timer("Testing mutex speed");
\r
1420 // Do at least 10ms
\r
1421 while(timer.getTime() < 10);
\r
1423 u32 dtime = timer.stop();
\r
1424 u32 per_ms = n / dtime;
\r
1425 std::cout<<"Done. "<<dtime<<"ms, "
\r
1426 <<per_ms<<"/ms"<<std::endl;
\r
1430 int main(int argc, char *argv[])
\r
1433 Parse command line
\r
1436 // List all allowed options
\r
1437 core::map<std::string, ValueSpec> allowed_options;
\r
1438 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1439 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1440 "Run server directly"));
\r
1441 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1442 "Load configuration from specified file"));
\r
1443 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1444 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1445 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1446 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1447 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1448 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1450 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1452 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1454 Settings cmd_args;
\r
1456 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1458 if(ret == false || cmd_args.getFlag("help"))
\r
1460 dstream<<"Allowed options:"<<std::endl;
\r
1461 for(core::map<std::string, ValueSpec>::Iterator
\r
1462 i = allowed_options.getIterator();
\r
1463 i.atEnd() == false; i++)
\r
1465 dstream<<" --"<<i.getNode()->getKey();
\r
1466 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1471 dstream<<" <value>";
\r
1473 dstream<<std::endl;
\r
1475 if(i.getNode()->getValue().help != NULL)
\r
1477 dstream<<" "<<i.getNode()->getValue().help
\r
1482 return cmd_args.getFlag("help") ? 0 : 1;
\r
1486 Low-level initialization
\r
1489 bool disable_stderr = false;
\r
1491 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1492 disable_stderr = true;
\r
1495 // Initialize debug streams
\r
1496 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1497 // Initialize debug stacks
\r
1498 debug_stacks_init();
\r
1500 DSTACK(__FUNCTION_NAME);
\r
1502 porting::initializePaths();
\r
1503 // Create user data directory
\r
1504 fs::CreateDir(porting::path_userdata);
\r
1506 // C-style stuff initialization
\r
1507 initializeMaterialProperties();
\r
1510 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1512 // Print startup message
\r
1513 dstream<<DTIME<<"minetest-c55"
\r
1514 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1515 <<", "<<BUILD_INFO
\r
1519 Basic initialization
\r
1522 // Initialize default settings
\r
1523 set_default_settings();
\r
1525 // Set locale. This is for forcing '.' as the decimal point.
\r
1526 std::locale::global(std::locale("C"));
\r
1527 // This enables printing all characters in bitmap font
\r
1528 setlocale(LC_CTYPE, "en_US");
\r
1530 // Initialize sockets
\r
1532 atexit(sockets_cleanup);
\r
1542 // Path of configuration file in use
\r
1543 std::string configpath = "";
\r
1545 if(cmd_args.exists("config"))
\r
1547 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1550 dstream<<"Could not read configuration from \""
\r
1551 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1554 configpath = cmd_args.get("config");
\r
1558 core::array<std::string> filenames;
\r
1559 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1560 #ifdef RUN_IN_PLACE
\r
1561 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1564 for(u32 i=0; i<filenames.size(); i++)
\r
1566 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1569 configpath = filenames[i];
\r
1575 // Initialize random seed
\r
1580 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1582 These are needed for unit tests at least.
\r
1585 IIrrlichtWrapper irrlicht_dummy;
\r
1587 init_mapnode(&irrlicht_dummy);
\r
1592 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1593 || cmd_args.getFlag("enable-unittests") == true)
\r
1598 // Read map parameters from settings
\r
1600 HMParams hm_params;
\r
1601 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1602 hm_params.randmax = g_settings.get("height_randmax");
\r
1603 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1604 hm_params.base = g_settings.get("height_base");*/
\r
1606 MapParams map_params;
\r
1607 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1608 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1616 if(cmd_args.exists("port"))
\r
1617 port = cmd_args.getU16("port");
\r
1618 else if(cmd_args.exists("port"))
\r
1619 port = g_settings.getU16("port");
\r
1622 std::string map_dir = porting::path_userdata+"/map";
\r
1623 if(cmd_args.exists("map-dir"))
\r
1624 map_dir = cmd_args.get("map-dir");
\r
1625 else if(g_settings.exists("map-dir"))
\r
1626 map_dir = g_settings.get("map-dir");
\r
1628 // Run dedicated server if asked to
\r
1629 if(cmd_args.getFlag("server"))
\r
1631 DSTACK("Dedicated server branch");
\r
1634 Server server(map_dir.c_str(), hm_params, map_params);
\r
1635 server.start(port);
\r
1638 dedicated_server_loop(server);
\r
1647 // Address to connect to
\r
1648 std::string address = "";
\r
1650 if(cmd_args.exists("address"))
\r
1652 address = cmd_args.get("address");
\r
1656 address = g_settings.get("address");
\r
1659 std::string playername = g_settings.get("name");
\r
1662 Resolution selection
\r
1665 bool fullscreen = false;
\r
1666 u16 screenW = g_settings.getU16("screenW");
\r
1667 u16 screenH = g_settings.getU16("screenH");
\r
1671 MyEventReceiver receiver;
\r
1673 video::E_DRIVER_TYPE driverType;
\r
1676 //driverType = video::EDT_DIRECT3D9;
\r
1677 driverType = video::EDT_OPENGL;
\r
1679 driverType = video::EDT_OPENGL;
\r
1680 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1683 // create device and exit if creation failed
\r
1685 IrrlichtDevice *device;
\r
1686 device = createDevice(driverType,
\r
1687 core::dimension2d<u32>(screenW, screenH),
\r
1688 16, fullscreen, false, false, &receiver);
\r
1691 return 1; // could not create selected driver.
\r
1693 g_device = device;
\r
1694 g_irrlicht = new IrrlichtWrapper(device);
\r
1697 Speed tests (done after irrlicht is loaded to get timer)
\r
1699 if(cmd_args.getFlag("speedtests"))
\r
1701 dstream<<"Running speed tests"<<std::endl;
\r
1706 device->setResizable(true);
\r
1708 bool random_input = g_settings.getBool("random_input")
\r
1709 || cmd_args.getFlag("random-input");
\r
1711 g_input = new RandomInputHandler();
\r
1713 g_input = new RealInputHandler(device, &receiver);
\r
1716 Continue initialization
\r
1719 video::IVideoDriver* driver = device->getVideoDriver();
\r
1722 This changes the minimum allowed number of vertices in a VBO
\r
1724 //driver->setMinHardwareBufferVertexCount(50);
\r
1726 scene::ISceneManager* smgr = device->getSceneManager();
\r
1728 guienv = device->getGUIEnvironment();
\r
1729 gui::IGUISkin* skin = guienv->getSkin();
\r
1730 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1732 skin->setFont(font);
\r
1734 dstream<<"WARNING: Font file was not found."
\r
1735 " Using default font."<<std::endl;
\r
1736 // If font was not found, this will get us one
\r
1737 font = skin->getFont();
\r
1740 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1741 dstream<<"text_height="<<text_height<<std::endl;
\r
1743 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1744 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1745 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1746 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1747 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1748 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1751 Preload some textures and stuff
\r
1754 init_content_inventory_texture_paths();
\r
1755 init_mapnode(g_irrlicht);
\r
1756 init_mineral(g_irrlicht);
\r
1763 We need some kind of a root node to be able to add
\r
1764 custom gui elements directly on the screen.
\r
1765 Otherwise they won't be automatically drawn.
\r
1767 guiroot = guienv->addStaticText(L"",
\r
1768 core::rect<s32>(0, 0, 10000, 10000));
\r
1770 // First line of debug text
\r
1771 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1773 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1775 // Second line of debug text
\r
1776 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1778 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1781 // At the middle of the screen
\r
1782 // Object infos are shown in this
\r
1783 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1785 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1789 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1791 core::rect<s32>(0,0,0,0),
\r
1793 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1794 core::list<ChatLine> chat_lines;
\r
1797 If an error occurs, this is set to something and the
\r
1798 menu-game loop is restarted. It is then displayed before
\r
1801 std::wstring error_message = L"";
\r
1806 while(g_device->run())
\r
1809 // This is used for catching disconnects
\r
1814 Out-of-game menu loop.
\r
1816 Loop quits when menu returns proper parameters.
\r
1820 // Cursor can be non-visible when coming from the game
\r
1821 device->getCursorControl()->setVisible(true);
\r
1822 // Some stuff are left to scene manager when coming from the game
\r
1823 // (map at least?)
\r
1825 // Reset or hide the debug gui texts
\r
1826 guitext->setText(L"Minetest-c55");
\r
1827 guitext2->setVisible(false);
\r
1828 guitext_info->setVisible(false);
\r
1829 guitext_chat->setVisible(false);
\r
1831 // Initialize menu data
\r
1832 MainMenuData menudata;
\r
1833 menudata.address = narrow_to_wide(address);
\r
1834 menudata.name = narrow_to_wide(playername);
\r
1835 menudata.port = narrow_to_wide(itos(port));
\r
1836 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1838 GUIMainMenu *menu =
\r
1839 new GUIMainMenu(guienv, guiroot, -1,
\r
1840 &g_menumgr, &menudata, &g_gamecallback);
\r
1841 menu->allowFocusRemoval(true);
\r
1843 if(error_message != L"")
\r
1845 GUIMessageMenu *menu2 =
\r
1846 new GUIMessageMenu(guienv, guiroot, -1,
\r
1847 &g_menumgr, error_message.c_str());
\r
1849 error_message = L"";
\r
1852 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1854 dstream<<"Created main menu"<<std::endl;
\r
1856 while(g_device->run())
\r
1858 // Run global IrrlichtWrapper's main thread processing stuff
\r
1859 g_irrlicht->Run();
\r
1861 if(menu->getStatus() == true)
\r
1864 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1865 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1866 guienv->drawAll();
\r
1867 driver->endScene();
\r
1870 // Break out of menu-game loop to shut down cleanly
\r
1871 if(g_device->run() == false)
\r
1874 dstream<<"Dropping main menu"<<std::endl;
\r
1878 // Delete map if requested
\r
1879 if(menudata.delete_map)
\r
1881 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1883 error_message = L"Delete failed";
\r
1887 playername = wide_to_narrow(menudata.name);
\r
1888 address = wide_to_narrow(menudata.address);
\r
1889 port = stoi(wide_to_narrow(menudata.port));
\r
1890 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1892 // Check for valid parameters, restart menu if invalid.
\r
1893 if(playername == "")
\r
1895 error_message = L"Name required.";
\r
1900 g_settings.set("name", playername);
\r
1901 g_settings.set("address", address);
\r
1902 g_settings.set("port", itos(port));
\r
1903 // Update configuration file
\r
1904 if(configpath != "")
\r
1905 g_settings.updateConfigFile(configpath.c_str());
\r
1907 // Continue to game
\r
1911 // Break out of menu-game loop to shut down cleanly
\r
1912 if(g_device->run() == false)
\r
1916 Make a scope here so that the client and the server and other
\r
1917 stuff gets removed when disconnected or the irrlicht device
\r
1922 // This is set to true at the end of the scope
\r
1923 g_irrlicht->Shutdown(false);
\r
1926 Draw "Loading" screen
\r
1928 const wchar_t *text = L"Loading and connecting...";
\r
1929 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1930 core::vector2d<s32> textsize(300, text_height);
\r
1931 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1933 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1934 text, textrect, false, false);
\r
1935 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1937 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1938 guienv->drawAll();
\r
1939 driver->endScene();
\r
1941 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1945 SharedPtr will delete it when it goes out of scope.
\r
1947 SharedPtr<Server> server;
\r
1948 if(address == ""){
\r
1949 server = new Server(map_dir, hm_params, map_params);
\r
1950 server->start(port);
\r
1957 Client client(device, playername.c_str(), draw_control);
\r
1959 g_client = &client;
\r
1961 Address connect_address(0,0,0,0, port);
\r
1964 connect_address.Resolve("localhost");
\r
1966 connect_address.Resolve(address.c_str());
\r
1968 catch(ResolveError &e)
\r
1970 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1972 error_message = L"Couldn't resolve address";
\r
1973 gui_loadingtext->remove();
\r
1977 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1978 client.connect(connect_address);
\r
1981 while(client.connectedAndInitialized() == false)
\r
1984 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1985 guienv->drawAll();
\r
1986 driver->endScene();
\r
1988 // Update client and server
\r
1992 if(server != NULL)
\r
1993 server->step(0.1);
\r
1999 catch(con::PeerNotFoundException &e)
\r
2001 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2003 error_message = L"Connection timed out.";
\r
2004 gui_loadingtext->remove();
\r
2011 /*scene::ISceneNode* skybox;
\r
2012 skybox = smgr->addSkyBoxSceneNode(
\r
2013 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2014 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2015 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2016 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2017 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2018 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2021 Create the camera node
\r
2024 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2025 0, // Camera parent
\r
2026 v3f(BS*100, BS*2, BS*100), // Look from
\r
2027 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2031 if(camera == NULL)
\r
2034 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2036 camera->setFOV(FOV_ANGLE);
\r
2038 // Just so big a value that everything rendered is visible
\r
2039 camera->setFarValue(100000*BS);
\r
2041 f32 camera_yaw = 0; // "right/left"
\r
2042 f32 camera_pitch = 0; // "up/down"
\r
2048 gui_loadingtext->remove();
\r
2051 Add some gui stuff
\r
2054 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2055 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2057 // Test the text input system
\r
2058 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2060 /*GUIMessageMenu *menu =
\r
2061 new GUIMessageMenu(guienv, guiroot, -1,
\r
2066 // Launch pause menu
\r
2067 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2068 &g_menumgr))->drop();
\r
2071 guitext2->setVisible(true);
\r
2072 guitext_info->setVisible(true);
\r
2073 guitext_chat->setVisible(true);
\r
2076 Some statistics are collected in these
\r
2079 u32 beginscenetime = 0;
\r
2080 u32 scenetime = 0;
\r
2081 u32 endscenetime = 0;
\r
2084 //throw con::PeerNotFoundException("lol");
\r
2090 bool first_loop_after_window_activation = true;
\r
2092 // Time is in milliseconds
\r
2093 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2094 // NOTE: So we have to use getTime() and call run()s between them
\r
2095 u32 lasttime = device->getTimer()->getTime();
\r
2097 while(device->run())
\r
2099 if(g_disconnect_requested)
\r
2101 g_disconnect_requested = false;
\r
2106 Run global IrrlichtWrapper's main thread processing stuff
\r
2108 g_irrlicht->Run();
\r
2111 Random calculations
\r
2113 v2u32 screensize = driver->getScreenSize();
\r
2114 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2116 // Hilight boxes collected during the loop and displayed
\r
2117 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2120 std::wstring infotext;
\r
2122 //TimeTaker //timer1("//timer1");
\r
2124 // Time of frame without fps limit
\r
2128 // not using getRealTime is necessary for wine
\r
2129 u32 time = device->getTimer()->getTime();
\r
2130 if(time > lasttime)
\r
2131 busytime_u32 = time - lasttime;
\r
2134 busytime = busytime_u32 / 1000.0;
\r
2137 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2139 // Absolutelu necessary for wine!
\r
2146 updateViewingRange(busytime, &client);
\r
2153 float fps_max = g_settings.getFloat("fps_max");
\r
2154 u32 frametime_min = 1000./fps_max;
\r
2156 if(busytime_u32 < frametime_min)
\r
2158 u32 sleeptime = frametime_min - busytime_u32;
\r
2159 device->sleep(sleeptime);
\r
2163 // Absolutelu necessary for wine!
\r
2167 Time difference calculation
\r
2169 f32 dtime; // in seconds
\r
2171 u32 time = device->getTimer()->getTime();
\r
2172 if(time > lasttime)
\r
2173 dtime = (time - lasttime) / 1000.0;
\r
2179 Time average and jitter calculation
\r
2182 static f32 dtime_avg1 = 0.0;
\r
2183 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2184 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2186 static f32 dtime_jitter1_max_sample = 0.0;
\r
2187 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2189 static f32 jitter1_max = 0.0;
\r
2190 static f32 counter = 0.0;
\r
2191 if(dtime_jitter1 > jitter1_max)
\r
2192 jitter1_max = dtime_jitter1;
\r
2197 dtime_jitter1_max_sample = jitter1_max;
\r
2198 dtime_jitter1_max_fraction
\r
2199 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2200 jitter1_max = 0.0;
\r
2205 Busytime average and jitter calculation
\r
2208 static f32 busytime_avg1 = 0.0;
\r
2209 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2210 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2212 static f32 busytime_jitter1_max_sample = 0.0;
\r
2213 static f32 busytime_jitter1_min_sample = 0.0;
\r
2215 static f32 jitter1_max = 0.0;
\r
2216 static f32 jitter1_min = 0.0;
\r
2217 static f32 counter = 0.0;
\r
2218 if(busytime_jitter1 > jitter1_max)
\r
2219 jitter1_max = busytime_jitter1;
\r
2220 if(busytime_jitter1 < jitter1_min)
\r
2221 jitter1_min = busytime_jitter1;
\r
2223 if(counter > 0.0){
\r
2225 busytime_jitter1_max_sample = jitter1_max;
\r
2226 busytime_jitter1_min_sample = jitter1_min;
\r
2227 jitter1_max = 0.0;
\r
2228 jitter1_min = 0.0;
\r
2233 Debug info for client
\r
2236 static float counter = 0.0;
\r
2241 client.printDebugInfo(std::cout);
\r
2246 Input handler step()
\r
2248 g_input->step(dtime);
\r
2251 Player speed control
\r
2260 bool a_superspeed,
\r
2263 PlayerControl control(
\r
2264 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2265 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2266 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2267 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2268 g_input->isKeyDown(irr::KEY_SPACE),
\r
2269 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2273 client.setPlayerControl(control);
\r
2277 Process environment
\r
2281 //TimeTaker timer("client.step(dtime)");
\r
2282 client.step(dtime);
\r
2283 //client.step(dtime_avg1);
\r
2286 if(server != NULL)
\r
2288 //TimeTaker timer("server->step(dtime)");
\r
2289 server->step(dtime);
\r
2292 v3f player_position = client.getPlayerPosition();
\r
2294 //TimeTaker //timer2("//timer2");
\r
2297 Mouse and camera control
\r
2300 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2303 device->getCursorControl()->setVisible(false);
\r
2305 if(first_loop_after_window_activation){
\r
2306 //std::cout<<"window active, first loop"<<std::endl;
\r
2307 first_loop_after_window_activation = false;
\r
2310 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2311 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2312 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2313 camera_yaw -= dx*0.2;
\r
2314 camera_pitch += dy*0.2;
\r
2315 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2316 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2318 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2321 device->getCursorControl()->setVisible(true);
\r
2323 //std::cout<<"window inactive"<<std::endl;
\r
2324 first_loop_after_window_activation = true;
\r
2327 camera_yaw = wrapDegrees(camera_yaw);
\r
2328 camera_pitch = wrapDegrees(camera_pitch);
\r
2330 v3f camera_direction = v3f(0,0,1);
\r
2331 camera_direction.rotateYZBy(camera_pitch);
\r
2332 camera_direction.rotateXZBy(camera_yaw);
\r
2334 // This is at the height of the eyes of the current figure
\r
2335 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2336 // This is more like in minecraft
\r
2337 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2339 camera->setPosition(camera_position);
\r
2340 // *100.0 helps in large map coordinates
\r
2341 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2343 if(FIELD_OF_VIEW_TEST){
\r
2344 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2345 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2348 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2349 //TimeTaker timer("client.updateCamera");
\r
2350 client.updateCamera(camera_position, camera_direction);
\r
2354 //TimeTaker //timer3("//timer3");
\r
2357 Calculate what block is the crosshair pointing to
\r
2360 //u32 t1 = device->getTimer()->getRealTime();
\r
2362 //f32 d = 4; // max. distance
\r
2363 f32 d = 4; // max. distance
\r
2364 core::line3d<f32> shootline(camera_position,
\r
2365 camera_position + camera_direction * BS * (d+1));
\r
2367 MapBlockObject *selected_object = client.getSelectedObject
\r
2368 (d*BS, camera_position, shootline);
\r
2371 If it's pointing to a MapBlockObject
\r
2374 if(selected_object != NULL)
\r
2376 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2378 core::aabbox3d<f32> box_on_map
\r
2379 = selected_object->getSelectionBoxOnMap();
\r
2381 hilightboxes.push_back(box_on_map);
\r
2383 infotext = narrow_to_wide(selected_object->infoText());
\r
2385 if(g_input->getLeftClicked())
\r
2387 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2388 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2389 selected_object->getId(), g_selected_item);
\r
2391 else if(g_input->getRightClicked())
\r
2393 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2395 Check if we want to modify the object ourselves
\r
2397 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2399 dstream<<"Sign object right-clicked"<<std::endl;
\r
2401 if(random_input == false)
\r
2403 // Get a new text for it
\r
2405 TextDest *dest = new TextDestSign(
\r
2406 selected_object->getBlock()->getPos(),
\r
2407 selected_object->getId(),
\r
2410 SignObject *sign_object = (SignObject*)selected_object;
\r
2412 std::wstring wtext =
\r
2413 narrow_to_wide(sign_object->getText());
\r
2415 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2421 Otherwise pass the event to the server as-is
\r
2425 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2426 selected_object->getId(), g_selected_item);
\r
2430 else // selected_object == NULL
\r
2434 Find out which node we are pointing at
\r
2437 bool nodefound = false;
\r
2439 v3s16 neighbourpos;
\r
2440 core::aabbox3d<f32> nodehilightbox;
\r
2441 f32 mindistance = BS * 1001;
\r
2443 v3s16 pos_i = floatToInt(player_position);
\r
2445 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2449 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2450 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2451 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2452 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2453 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2454 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2456 for(s16 y = ystart; y <= yend; y++)
\r
2457 for(s16 z = zstart; z <= zend; z++)
\r
2458 for(s16 x = xstart; x <= xend; x++)
\r
2463 n = client.getNode(v3s16(x,y,z));
\r
2464 if(content_pointable(n.d) == false)
\r
2467 catch(InvalidPositionException &e)
\r
2473 v3f npf = intToFloat(np);
\r
2478 v3s16(0,0,1), // back
\r
2479 v3s16(0,1,0), // top
\r
2480 v3s16(1,0,0), // right
\r
2481 v3s16(0,0,-1), // front
\r
2482 v3s16(0,-1,0), // bottom
\r
2483 v3s16(-1,0,0), // left
\r
2489 if(n.d == CONTENT_TORCH)
\r
2491 v3s16 dir = unpackDir(n.dir);
\r
2492 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2493 dir_f *= BS/2 - BS/6 - BS/20;
\r
2494 v3f cpf = npf + dir_f;
\r
2495 f32 distance = (cpf - camera_position).getLength();
\r
2497 core::aabbox3d<f32> box;
\r
2500 if(dir == v3s16(0,-1,0))
\r
2502 box = core::aabbox3d<f32>(
\r
2503 npf - v3f(BS/6, BS/2, BS/6),
\r
2504 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2508 else if(dir == v3s16(0,1,0))
\r
2510 box = core::aabbox3d<f32>(
\r
2511 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2512 npf + v3f(BS/6, BS/2, BS/6)
\r
2518 box = core::aabbox3d<f32>(
\r
2519 cpf - v3f(BS/6, BS/3, BS/6),
\r
2520 cpf + v3f(BS/6, BS/3, BS/6)
\r
2524 if(distance < mindistance)
\r
2526 if(box.intersectsWithLine(shootline))
\r
2530 neighbourpos = np;
\r
2531 mindistance = distance;
\r
2532 nodehilightbox = box;
\r
2541 for(u16 i=0; i<6; i++)
\r
2543 v3f dir_f = v3f(dirs[i].X,
\r
2544 dirs[i].Y, dirs[i].Z);
\r
2545 v3f centerpoint = npf + dir_f * BS/2;
\r
2547 (centerpoint - camera_position).getLength();
\r
2549 if(distance < mindistance)
\r
2551 core::CMatrix4<f32> m;
\r
2552 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2554 // This is the back face
\r
2555 v3f corners[2] = {
\r
2556 v3f(BS/2, BS/2, BS/2),
\r
2557 v3f(-BS/2, -BS/2, BS/2+d)
\r
2560 for(u16 j=0; j<2; j++)
\r
2562 m.rotateVect(corners[j]);
\r
2563 corners[j] += npf;
\r
2566 core::aabbox3d<f32> facebox(corners[0]);
\r
2567 facebox.addInternalPoint(corners[1]);
\r
2569 if(facebox.intersectsWithLine(shootline))
\r
2573 neighbourpos = np + dirs[i];
\r
2574 mindistance = distance;
\r
2576 //nodehilightbox = facebox;
\r
2578 const float d = 0.502;
\r
2579 core::aabbox3d<f32> nodebox
\r
2580 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2581 v3f nodepos_f = intToFloat(nodepos);
\r
2582 nodebox.MinEdge += nodepos_f;
\r
2583 nodebox.MaxEdge += nodepos_f;
\r
2584 nodehilightbox = nodebox;
\r
2586 } // if distance < mindistance
\r
2588 } // regular block
\r
2591 static float nodig_delay_counter = 0.0;
\r
2595 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2597 static float dig_time = 0.0;
\r
2598 static u16 dig_index = 0;
\r
2600 // Visualize selection
\r
2602 hilightboxes.push_back(nodehilightbox);
\r
2606 if(g_input->getLeftReleased())
\r
2608 client.clearTempMod(nodepos);
\r
2612 if(nodig_delay_counter > 0.0)
\r
2614 nodig_delay_counter -= dtime;
\r
2618 if(nodepos != nodepos_old)
\r
2620 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2621 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2623 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2625 client.clearTempMod(nodepos_old);
\r
2630 if(g_input->getLeftClicked() ||
\r
2631 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2633 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2634 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2636 if(g_input->getLeftClicked())
\r
2638 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2640 if(g_input->getLeftState())
\r
2642 MapNode n = client.getNode(nodepos);
\r
2644 // Get tool name. Default is "" = bare hands
\r
2645 std::string toolname = "";
\r
2646 InventoryList *mlist = local_inventory.getList("main");
\r
2649 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2650 if(item && (std::string)item->getName() == "ToolItem")
\r
2652 ToolItem *titem = (ToolItem*)item;
\r
2653 toolname = titem->getToolName();
\r
2657 // Get digging properties for material and tool
\r
2658 u8 material = n.d;
\r
2659 DiggingProperties prop =
\r
2660 getDiggingProperties(material, toolname);
\r
2662 float dig_time_complete = 0.0;
\r
2664 if(prop.diggable == false)
\r
2666 /*dstream<<"Material "<<(int)material
\r
2667 <<" not diggable with \""
\r
2668 <<toolname<<"\""<<std::endl;*/
\r
2669 // I guess nobody will wait for this long
\r
2670 dig_time_complete = 10000000.0;
\r
2674 dig_time_complete = prop.time;
\r
2677 if(dig_time_complete >= 0.001)
\r
2679 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2680 * dig_time/dig_time_complete);
\r
2682 // This is for torches
\r
2685 dig_index = CRACK_ANIMATION_LENGTH;
\r
2688 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2690 //TimeTaker timer("client.setTempMod");
\r
2691 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2692 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2696 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2697 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2698 client.clearTempMod(nodepos);
\r
2699 client.removeNode(nodepos);
\r
2703 nodig_delay_counter = dig_time_complete
\r
2704 / (float)CRACK_ANIMATION_LENGTH;
\r
2706 // We don't want a corresponding delay to
\r
2707 // very time consuming nodes
\r
2708 if(nodig_delay_counter > 0.5)
\r
2710 nodig_delay_counter = 0.5;
\r
2712 // We want a slight delay to very little
\r
2713 // time consuming nodes
\r
2714 float mindelay = 0.15;
\r
2715 if(nodig_delay_counter < mindelay)
\r
2717 nodig_delay_counter = mindelay;
\r
2721 dig_time += dtime;
\r
2725 if(g_input->getRightClicked())
\r
2727 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2728 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2731 nodepos_old = nodepos;
\r
2736 } // selected_object == NULL
\r
2738 g_input->resetLeftClicked();
\r
2739 g_input->resetRightClicked();
\r
2741 if(g_input->getLeftReleased())
\r
2743 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2745 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2747 if(g_input->getRightReleased())
\r
2749 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2753 g_input->resetLeftReleased();
\r
2754 g_input->resetRightReleased();
\r
2757 Calculate stuff for drawing
\r
2760 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2762 u32 daynight_ratio = client.getDayNightRatio();
\r
2763 /*video::SColor bgcolor = video::SColor(
\r
2765 skycolor.getRed() * daynight_ratio / 1000,
\r
2766 skycolor.getGreen() * daynight_ratio / 1000,
\r
2767 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2769 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2770 video::SColor bgcolor = video::SColor(
\r
2772 skycolor.getRed() * l / 255,
\r
2773 skycolor.getGreen() * l / 255,
\r
2774 skycolor.getBlue() * l / 255);
\r
2780 if(g_settings.getBool("enable_fog") == true)
\r
2782 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2783 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2784 if(draw_control.range_all)
\r
2785 range = 100000*BS;
\r
2789 video::EFT_FOG_LINEAR,
\r
2793 false, // pixel fog
\r
2794 false // range fog
\r
2800 Update gui stuff (0ms)
\r
2803 //TimeTaker guiupdatetimer("Gui updating");
\r
2806 wchar_t temptext[150];
\r
2808 static float drawtime_avg = 0;
\r
2809 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2810 static float beginscenetime_avg = 0;
\r
2811 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2812 static float scenetime_avg = 0;
\r
2813 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2814 static float endscenetime_avg = 0;
\r
2815 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2817 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2819 L", R: range_all=%i"
\r
2821 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2823 draw_control.range_all,
\r
2825 beginscenetime_avg,
\r
2830 guitext->setText(temptext);
\r
2834 wchar_t temptext[150];
\r
2835 swprintf(temptext, 150,
\r
2836 L"(% .1f, % .1f, % .1f)"
\r
2837 L" (% .3f < btime_jitter < % .3f"
\r
2838 L", dtime_jitter = % .1f %%"
\r
2839 L", v_range = %.1f)",
\r
2840 player_position.X/BS,
\r
2841 player_position.Y/BS,
\r
2842 player_position.Z/BS,
\r
2843 busytime_jitter1_min_sample,
\r
2844 busytime_jitter1_max_sample,
\r
2845 dtime_jitter1_max_fraction * 100.0,
\r
2846 draw_control.wanted_range
\r
2849 guitext2->setText(temptext);
\r
2853 guitext_info->setText(infotext.c_str());
\r
2857 Get chat messages from client
\r
2860 // Get new messages
\r
2861 std::wstring message;
\r
2862 while(client.getChatMessage(message))
\r
2864 chat_lines.push_back(ChatLine(message));
\r
2865 /*if(chat_lines.size() > 6)
\r
2867 core::list<ChatLine>::Iterator
\r
2868 i = chat_lines.begin();
\r
2869 chat_lines.erase(i);
\r
2872 // Append them to form the whole static text and throw
\r
2873 // it to the gui element
\r
2874 std::wstring whole;
\r
2875 // This will correspond to the line number counted from
\r
2876 // top to bottom, from size-1 to 0
\r
2877 s16 line_number = chat_lines.size();
\r
2878 // Count of messages to be removed from the top
\r
2879 u16 to_be_removed_count = 0;
\r
2880 for(core::list<ChatLine>::Iterator
\r
2881 i = chat_lines.begin();
\r
2882 i != chat_lines.end(); i++)
\r
2884 // After this, line number is valid for this loop
\r
2887 (*i).age += dtime;
\r
2889 This results in a maximum age of 60*6 to the
\r
2890 lowermost line and a maximum of 6 lines
\r
2892 float allowed_age = (6-line_number) * 60.0;
\r
2894 if((*i).age > allowed_age)
\r
2896 to_be_removed_count++;
\r
2899 whole += (*i).text + L'\n';
\r
2901 for(u16 i=0; i<to_be_removed_count; i++)
\r
2903 core::list<ChatLine>::Iterator
\r
2904 it = chat_lines.begin();
\r
2905 chat_lines.erase(it);
\r
2907 guitext_chat->setText(whole.c_str());
\r
2908 // Update gui element size and position
\r
2909 core::rect<s32> rect(
\r
2911 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2912 screensize.X - 10,
\r
2915 guitext_chat->setRelativePosition(rect);
\r
2917 if(chat_lines.size() == 0)
\r
2918 guitext_chat->setVisible(false);
\r
2920 guitext_chat->setVisible(true);
\r
2927 static u16 old_selected_item = 65535;
\r
2928 if(client.getLocalInventoryUpdated()
\r
2929 || g_selected_item != old_selected_item)
\r
2931 old_selected_item = g_selected_item;
\r
2932 //std::cout<<"Updating local inventory"<<std::endl;
\r
2933 client.getLocalInventory(local_inventory);
\r
2934 quick_inventory->setSelection(g_selected_item);
\r
2935 quick_inventory->update();
\r
2939 Send actions returned by the inventory menu
\r
2941 while(inventory_action_queue.size() != 0)
\r
2943 InventoryAction *a = inventory_action_queue.pop_front();
\r
2945 client.sendInventoryAction(a);
\r
2954 TimeTaker drawtimer("Drawing");
\r
2958 TimeTaker timer("beginScene");
\r
2959 driver->beginScene(true, true, bgcolor);
\r
2960 //driver->beginScene(false, true, bgcolor);
\r
2961 beginscenetime = timer.stop(true);
\r
2966 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2969 TimeTaker timer("smgr");
\r
2971 scenetime = timer.stop(true);
\r
2975 //TimeTaker timer9("auxiliary drawings");
\r
2979 //TimeTaker //timer10("//timer10");
\r
2981 video::SMaterial m;
\r
2982 //m.Thickness = 10;
\r
2984 m.Lighting = false;
\r
2985 driver->setMaterial(m);
\r
2987 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2989 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2990 i != hilightboxes.end(); i++)
\r
2992 /*std::cout<<"hilightbox min="
\r
2993 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2995 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2997 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3003 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3004 displaycenter + core::vector2d<s32>(10,0),
\r
3005 video::SColor(255,255,255,255));
\r
3006 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3007 displaycenter + core::vector2d<s32>(0,10),
\r
3008 video::SColor(255,255,255,255));
\r
3013 //TimeTaker //timer11("//timer11");
\r
3019 guienv->drawAll();
\r
3023 TimeTaker timer("endScene");
\r
3024 driver->endScene();
\r
3025 endscenetime = timer.stop(true);
\r
3028 drawtime = drawtimer.stop(true);
\r
3034 static s16 lastFPS = 0;
\r
3035 //u16 fps = driver->getFPS();
\r
3036 u16 fps = (1.0/dtime_avg1);
\r
3038 if (lastFPS != fps)
\r
3040 core::stringw str = L"Minetest [";
\r
3041 str += driver->getName();
\r
3045 device->setWindowCaption(str.c_str());
\r
3051 device->yield();*/
\r
3054 delete quick_inventory;
\r
3057 Disable texture fetches and other stuff that is queued
\r
3058 to be processed by the main loop.
\r
3060 This has to be done before client goes out of scope.
\r
3062 g_irrlicht->Shutdown(true);
\r
3064 } // client and server are deleted at this point
\r
3067 catch(con::PeerNotFoundException &e)
\r
3069 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3070 error_message = L"Connection timed out.";
\r
3073 } // Menu-game loop
\r
3078 In the end, delete the Irrlicht device.
\r
3083 Update configuration file
\r
3085 /*if(configpath != "")
\r
3087 g_settings.updateConfigFile(configpath.c_str());
\r
3090 END_DEBUG_EXCEPTION_HANDLER
\r
3092 debugstreams_deinit();
\r