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: Precalculate lighting translation table at runtime (at startup)
\r
83 - This is not doable because it is currently hand-made and not
\r
84 based on some mathematical function. Now it is not.
\r
86 SUGG: A version number to blocks, which increments when the block is
\r
87 modified (node add/remove, water update, lighting update)
\r
88 - This can then be used to make sure the most recent version of
\r
89 a block has been sent to client
\r
91 SUGG: Make the amount of blocks sending to client and the total
\r
92 amount of blocks dynamically limited. Transferring blocks is the
\r
93 main network eater of this system, so it is the one that has
\r
94 to be throttled so that RTTs stay low.
\r
96 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
97 different directions and then only those drawn that need to be
\r
98 - Also an 1-dimensional tile map would be nice probably
\r
100 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
106 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
108 - The player could go faster by a crafting a boat, or riding an animal
\r
110 - Random NPC traders. what else?
\r
115 Build system / running:
\r
116 -----------------------
\r
118 FIXME: Some network errors on Windows that cause local game to not work
\r
119 - See siggjen's emails.
\r
120 - Is this the famous "windows 7 problem"?
\r
121 - Apparently there might be other errors too
\r
123 Networking and serialization:
\r
124 -----------------------------
\r
126 TODO: Get rid of GotSplitPacketException
\r
131 TODO: Add gui option to remove map
\r
133 TODO: Configuration menu, at least for keys
\r
138 TODO: Optimize day/night mesh updating somehow
\r
139 - create copies of all textures for all lighting values and only
\r
140 change texture for material?
\r
141 - Umm... the collecting of the faces is the slow part
\r
142 -> what about just changing the color values of the existing
\r
143 meshbuffers? It should go quite fast.
\r
144 - This is not easy; There'd need to be a buffer somewhere
\r
145 that would contain the night and day lighting values.
\r
146 - Actually if FastFaces would be stored, they could
\r
149 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
151 - That is >500 vertices
\r
152 - This is not easy; all the MapBlocks close to the player would
\r
153 still need to be drawn separately and combining the blocks
\r
154 would have to happen in a background thread
\r
156 TODO: Make fetching sector's blocks more efficient when rendering
\r
157 sectors that have very large amounts of blocks (on client)
\r
158 - Is this necessary at all?
\r
160 TODO: Flowing water animation
\r
162 NOTE(FIXED): A lock condition is possible:
\r
163 1) MapBlock::updateMesh() is called from client asynchronously:
\r
164 - AsyncProcessData() -> Map::updateMeshes()
\r
165 2) Asynchronous locks m_temp_mods_mutex
\r
166 3) MapBlock::updateMesh() is called from client synchronously:
\r
167 - Client::step() -> Environment::step()
\r
168 4) Synchronous starts waiting for m_temp_mods_mutex
\r
169 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
174 TODO: Make the video backend selectable
\r
179 TODO: Untie client network operations from framerate
\r
180 - Needs some input queues or something
\r
182 TODO: Make morning and evening transition more smooth and maybe shorter
\r
184 TODO: Don't update all meshes always on single node changes, but
\r
185 check which ones should be updated
\r
186 - implement Map::updateNodeMeshes()
\r
191 TODO: When player dies, throw items on map
\r
193 TODO: Make an option to the server to disable building and digging near
\r
194 the starting position
\r
196 TODO: Save players with inventories to disk
\r
197 TODO: Players to be saved as text in map/players/<name>
\r
199 TODO: Copy the text of the last picked sign to inventory in creative
\r
202 TODO: Check what goes wrong with caching map to disk (Kray)
\r
205 TODO: When server sees that client is removing an inexistent block to
\r
206 an existent position, resend the MapBlock.
\r
208 FIXME: Server went into some infinite PeerNotFoundException loop
\r
213 TODO: Better handling of objects and mobs
\r
215 - There has to be some way to do it with less messy code
\r
216 - Make separate classes for client and server
\r
217 - Client should not discriminate between blocks, server should
\r
218 - Make other players utilize the same framework
\r
219 - This is also needed for objects that don't get sent to client
\r
220 but are used for triggers etc
\r
222 TODO: There has to be some better way to handle static objects than to
\r
223 send them all the time. This affects signs and item objects.
\r
224 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
225 need an additional metadata field for the texts
\r
226 - This is also needed for item container chests
\r
228 Block object server side:
\r
229 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
230 - For all blocks in the buffer, objects are stepped(). This
\r
231 means they are active.
\r
232 - TODO: A global active buffer is needed for the server
\r
233 - TODO: A timestamp to blocks
\r
234 - TODO: All blocks going in and out of the buffer are recorded.
\r
235 - TODO: For outgoing blocks, timestamp is written.
\r
236 - TODO: For incoming blocks, time difference is calculated and
\r
237 objects are stepped according to it.
\r
242 TODO: Mineral and ground material properties
\r
243 - This way mineral ground toughness can be calculated with just
\r
244 some formula, as well as tool strengths
\r
246 TODO: Flowing water to actually contain flow direction information
\r
248 TODO: Remove duplicate lighting implementation from Map (leave
\r
249 VoxelManipulator, which is faster)
\r
251 FEATURE: Create a system that allows a huge amount of different "map
\r
252 generator modules/filters"
\r
254 FEATURE: Erosion simulation at map generation time
\r
255 - Simulate water flows, which would carve out dirt fast and
\r
256 then turn stone into gravel and sand and relocate it.
\r
257 - How about relocating minerals, too? Coal and gold in
\r
258 downstream sand and gravel would be kind of cool
\r
259 - This would need a better way of handling minerals, mainly
\r
260 to have mineral content as a separate field. the first
\r
261 parameter field is free for this.
\r
262 - Simulate rock falling from cliffs when water has removed
\r
263 enough solid rock from the bottom
\r
265 Doing now (most important at the top):
\r
266 --------------------------------------
\r
270 === Stuff being done
\r
271 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
273 === Stuff to do before release
\r
274 * Save the new mapgen stuff
\r
275 - map/meta.txt, which should contain only plain text, something like this:
\r
276 seed = 7ff1bafcd7118800
\r
278 - map/chunks.dat: chunk positions and flags in binary format
\r
279 * Make server find the spawning place from the real map data, not from
\r
281 - But the changing borders of chunk have to be avoided, because
\r
282 there is time to generate only one chunk.
\r
283 * Make the generator to run in background and not blocking block
\r
284 placement and transfer
\r
285 * only_from_disk might not work anymore - check and fix it.
\r
286 * Check the fixmes in the list above
\r
288 === Stuff to do after release
\r
289 * Add some kind of erosion and other stuff that now is possible
\r
290 * Make client to fetch stuff asynchronously
\r
291 - Needs method SyncProcessData
\r
292 * Fix the problem with the server constantly saving one or a few
\r
293 blocks? List the first saved block, maybe it explains.
\r
294 - It is probably caused by oscillating water
\r
295 * Water doesn't start flowing after map generation like it should
\r
296 - Are there still problems?
\r
297 * Better water generation (spread it to underwater caverns but don't
\r
298 fill dungeons that don't touch big water masses)
\r
299 * When generating a chunk and the neighboring chunk doesn't have mud
\r
300 and stuff yet and the ground is fairly flat, the mud will flow to
\r
301 the other chunk making nasty straight walls when the other chunk
\r
302 is generated. Fix it.
\r
303 * Make a small history check to transformLiquids to detect and log
\r
304 continuous oscillations, in such detail that they can be fixed.
\r
306 ======================================================================
\r
311 Setting this to 1 enables a special camera mode that forces
\r
312 the renderers to think that the camera statically points from
\r
313 the starting place to a static direction.
\r
315 This allows one to move around with the player and see what
\r
316 is actually drawn behind solid things and behind the player.
\r
318 #define FIELD_OF_VIEW_TEST 0
\r
322 #pragma message ("Disabling unit tests")
\r
324 #warning "Disabling unit tests"
\r
326 // Disable unit tests
\r
327 #define ENABLE_TESTS 0
\r
329 // Enable unit tests
\r
330 #define ENABLE_TESTS 1
\r
334 #pragma comment(lib, "Irrlicht.lib")
\r
335 //#pragma comment(lib, "jthread.lib")
\r
336 #pragma comment(lib, "zlibwapi.lib")
\r
337 #pragma comment(lib, "Shell32.lib")
\r
338 // This would get rid of the console window
\r
339 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
342 #include <iostream>
\r
344 #include <jmutexautolock.h>
\r
345 #include <locale.h>
\r
346 #include "common_irrlicht.h"
\r
349 #include "player.h"
\r
352 #include "environment.h"
\r
353 #include "server.h"
\r
354 #include "client.h"
\r
355 #include "serialization.h"
\r
356 #include "constants.h"
\r
357 #include "strfnd.h"
\r
358 #include "porting.h"
\r
359 #include "irrlichtwrapper.h"
\r
360 #include "gettime.h"
\r
361 #include "porting.h"
\r
362 #include "guiPauseMenu.h"
\r
363 #include "guiInventoryMenu.h"
\r
364 #include "guiTextInputMenu.h"
\r
365 #include "materials.h"
\r
366 #include "guiMessageMenu.h"
\r
367 #include "filesys.h"
\r
368 #include "config.h"
\r
369 #include "guiMainMenu.h"
\r
370 #include "mineral.h"
\r
374 // TODO: Remove this
\r
375 IrrlichtWrapper *g_irrlicht = NULL;
\r
377 // This makes textures
\r
378 TextureSource *g_texturesource = NULL;
\r
380 MapDrawControl draw_control;
\r
384 These are loaded from the config file.
\r
387 Settings g_settings;
\r
389 extern void set_default_settings();
\r
395 IrrlichtDevice *g_device = NULL;
\r
396 Client *g_client = NULL;
\r
402 gui::IGUIEnvironment* guienv = NULL;
\r
403 gui::IGUIStaticText *guiroot = NULL;
\r
405 class MainMenuManager : public IMenuManager
\r
408 virtual void createdMenu(GUIModalMenu *menu)
\r
410 for(core::list<GUIModalMenu*>::Iterator
\r
411 i = m_stack.begin();
\r
412 i != m_stack.end(); i++)
\r
414 assert(*i != menu);
\r
417 if(m_stack.size() != 0)
\r
418 (*m_stack.getLast())->setVisible(false);
\r
419 m_stack.push_back(menu);
\r
422 virtual void deletingMenu(GUIModalMenu *menu)
\r
424 // Remove all entries if there are duplicates
\r
425 bool removed_entry;
\r
427 removed_entry = false;
\r
428 for(core::list<GUIModalMenu*>::Iterator
\r
429 i = m_stack.begin();
\r
430 i != m_stack.end(); i++)
\r
435 removed_entry = true;
\r
439 }while(removed_entry);
\r
441 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
442 assert(*i == menu);
\r
443 m_stack.erase(i);*/
\r
445 if(m_stack.size() != 0)
\r
446 (*m_stack.getLast())->setVisible(true);
\r
451 return m_stack.size();
\r
454 core::list<GUIModalMenu*> m_stack;
\r
457 MainMenuManager g_menumgr;
\r
459 bool noMenuActive()
\r
461 return (g_menumgr.menuCount() == 0);
\r
464 bool g_disconnect_requested = false;
\r
466 class MainGameCallback : public IGameCallback
\r
469 virtual void exitToOS()
\r
471 g_device->closeDevice();
\r
474 virtual void disconnect()
\r
476 g_disconnect_requested = true;
\r
480 MainGameCallback g_gamecallback;
\r
482 // Inventory actions from the menu are buffered here before sending
\r
483 Queue<InventoryAction*> inventory_action_queue;
\r
484 // This is a copy of the inventory that the client's environment has
\r
485 Inventory local_inventory;
\r
487 u16 g_selected_item = 0;
\r
494 std::ostream *dout_con_ptr = &dummyout;
\r
495 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
496 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
497 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
498 //std::ostream *dout_con_ptr = &dstream;
\r
499 //std::ostream *derr_con_ptr = &dstream;
\r
502 std::ostream *dout_server_ptr = &dstream;
\r
503 std::ostream *derr_server_ptr = &dstream;
\r
506 std::ostream *dout_client_ptr = &dstream;
\r
507 std::ostream *derr_client_ptr = &dstream;
\r
510 gettime.h implementation
\r
516 Use irrlicht because it is more precise than porting.h's
\r
519 if(g_irrlicht == NULL)
\r
521 return g_irrlicht->getTime();
\r
528 struct TextDestSign : public TextDest
\r
530 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
532 m_blockpos = blockpos;
\r
536 void gotText(std::wstring text)
\r
538 std::string ntext = wide_to_narrow(text);
\r
539 dstream<<"Changing text of a sign object: "
\r
540 <<ntext<<std::endl;
\r
541 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
549 struct TextDestChat : public TextDest
\r
551 TextDestChat(Client *client)
\r
555 void gotText(std::wstring text)
\r
557 m_client->sendChatMessage(text);
\r
558 m_client->addChatMessage(text);
\r
564 class MyEventReceiver : public IEventReceiver
\r
567 // This is the one method that we have to implement
\r
568 virtual bool OnEvent(const SEvent& event)
\r
571 React to nothing here if a menu is active
\r
573 if(noMenuActive() == false)
\r
579 // Remember whether each key is down or up
\r
580 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
582 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
584 if(event.KeyInput.PressedDown)
\r
586 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
592 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
594 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
596 dstream<<DTIME<<"MyEventReceiver: "
\r
597 <<"Launching pause menu"<<std::endl;
\r
598 // It will delete itself by itself
\r
599 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
600 &g_menumgr))->drop();
\r
603 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
605 dstream<<DTIME<<"MyEventReceiver: "
\r
606 <<"Launching inventory"<<std::endl;
\r
607 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
608 &local_inventory, &inventory_action_queue,
\r
609 &g_menumgr))->drop();
\r
612 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
614 TextDest *dest = new TextDestChat(g_client);
\r
616 (new GUITextInputMenu(guienv, guiroot, -1,
\r
622 // Material selection
\r
623 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
625 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
628 g_selected_item = 0;
\r
629 dstream<<DTIME<<"Selected item: "
\r
630 <<g_selected_item<<std::endl;
\r
633 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
634 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
636 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
637 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
639 if(s1 < PLAYER_INVENTORY_SIZE)
\r
640 g_selected_item = s1-1;
\r
641 dstream<<DTIME<<"Selected item: "
\r
642 <<g_selected_item<<std::endl;
\r
645 // Viewing range selection
\r
646 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
648 if(draw_control.range_all)
\r
650 draw_control.range_all = false;
\r
651 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
655 draw_control.range_all = true;
\r
656 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
660 // Print debug stacks
\r
661 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
663 dstream<<"-----------------------------------------"
\r
665 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
666 dstream<<"-----------------------------------------"
\r
668 debug_stacks_print();
\r
673 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
675 if(noMenuActive() == false)
\r
677 left_active = false;
\r
678 middle_active = false;
\r
679 right_active = false;
\r
683 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
684 left_active = event.MouseInput.isLeftPressed();
\r
685 middle_active = event.MouseInput.isMiddlePressed();
\r
686 right_active = event.MouseInput.isRightPressed();
\r
688 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
690 leftclicked = true;
\r
692 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
694 rightclicked = true;
\r
696 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
698 leftreleased = true;
\r
700 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
702 rightreleased = true;
\r
704 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
706 /*dstream<<"event.MouseInput.Wheel="
\r
707 <<event.MouseInput.Wheel<<std::endl;*/
\r
708 if(event.MouseInput.Wheel < 0)
\r
710 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
713 g_selected_item = 0;
\r
715 else if(event.MouseInput.Wheel > 0)
\r
717 if(g_selected_item > 0)
\r
720 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
729 // This is used to check whether a key is being held down
\r
730 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
732 return keyIsDown[keyCode];
\r
737 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
738 keyIsDown[i] = false;
\r
740 leftclicked = false;
\r
741 rightclicked = false;
\r
742 leftreleased = false;
\r
743 rightreleased = false;
\r
745 left_active = false;
\r
746 middle_active = false;
\r
747 right_active = false;
\r
758 bool rightreleased;
\r
761 bool middle_active;
\r
765 // We use this array to store the current state of each key
\r
766 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
769 IrrlichtDevice *m_device;
\r
778 virtual ~InputHandler()
\r
782 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
784 virtual v2s32 getMousePos() = 0;
\r
785 virtual void setMousePos(s32 x, s32 y) = 0;
\r
787 virtual bool getLeftState() = 0;
\r
788 virtual bool getRightState() = 0;
\r
790 virtual bool getLeftClicked() = 0;
\r
791 virtual bool getRightClicked() = 0;
\r
792 virtual void resetLeftClicked() = 0;
\r
793 virtual void resetRightClicked() = 0;
\r
795 virtual bool getLeftReleased() = 0;
\r
796 virtual bool getRightReleased() = 0;
\r
797 virtual void resetLeftReleased() = 0;
\r
798 virtual void resetRightReleased() = 0;
\r
800 virtual void step(float dtime) {};
\r
802 virtual void clear() {};
\r
805 InputHandler *g_input = NULL;
\r
807 class RealInputHandler : public InputHandler
\r
810 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
812 m_receiver(receiver)
\r
815 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
817 return m_receiver->IsKeyDown(keyCode);
\r
819 virtual v2s32 getMousePos()
\r
821 return m_device->getCursorControl()->getPosition();
\r
823 virtual void setMousePos(s32 x, s32 y)
\r
825 m_device->getCursorControl()->setPosition(x, y);
\r
828 virtual bool getLeftState()
\r
830 return m_receiver->left_active;
\r
832 virtual bool getRightState()
\r
834 return m_receiver->right_active;
\r
837 virtual bool getLeftClicked()
\r
839 return m_receiver->leftclicked;
\r
841 virtual bool getRightClicked()
\r
843 return m_receiver->rightclicked;
\r
845 virtual void resetLeftClicked()
\r
847 m_receiver->leftclicked = false;
\r
849 virtual void resetRightClicked()
\r
851 m_receiver->rightclicked = false;
\r
854 virtual bool getLeftReleased()
\r
856 return m_receiver->leftreleased;
\r
858 virtual bool getRightReleased()
\r
860 return m_receiver->rightreleased;
\r
862 virtual void resetLeftReleased()
\r
864 m_receiver->leftreleased = false;
\r
866 virtual void resetRightReleased()
\r
868 m_receiver->rightreleased = false;
\r
873 resetRightClicked();
\r
874 resetLeftClicked();
\r
877 IrrlichtDevice *m_device;
\r
878 MyEventReceiver *m_receiver;
\r
881 class RandomInputHandler : public InputHandler
\r
884 RandomInputHandler()
\r
886 leftclicked = false;
\r
887 rightclicked = false;
\r
888 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
889 keydown[i] = false;
\r
891 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
893 return keydown[keyCode];
\r
895 virtual v2s32 getMousePos()
\r
899 virtual void setMousePos(s32 x, s32 y)
\r
901 mousepos = v2s32(x,y);
\r
904 virtual bool getLeftState()
\r
908 virtual bool getRightState()
\r
913 virtual bool getLeftClicked()
\r
915 return leftclicked;
\r
917 virtual bool getRightClicked()
\r
919 return rightclicked;
\r
921 virtual void resetLeftClicked()
\r
923 leftclicked = false;
\r
925 virtual void resetRightClicked()
\r
927 rightclicked = false;
\r
930 virtual bool getLeftReleased()
\r
934 virtual bool getRightReleased()
\r
938 virtual void resetLeftReleased()
\r
941 virtual void resetRightReleased()
\r
945 virtual void step(float dtime)
\r
948 static float counter1 = 0;
\r
952 counter1 = 0.1*Rand(1,10);
\r
953 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
954 g_selected_material++;
\r
956 g_selected_material = 0;*/
\r
957 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
960 g_selected_item = 0;
\r
964 static float counter1 = 0;
\r
968 counter1 = 0.1*Rand(1, 40);
\r
969 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
973 static float counter1 = 0;
\r
977 counter1 = 0.1*Rand(1, 40);
\r
978 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
982 static float counter1 = 0;
\r
986 counter1 = 0.1*Rand(1, 40);
\r
987 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
991 static float counter1 = 0;
\r
995 counter1 = 0.1*Rand(1, 40);
\r
996 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1000 static float counter1 = 0;
\r
1001 counter1 -= dtime;
\r
1002 if(counter1 < 0.0)
\r
1004 counter1 = 0.1*Rand(1, 20);
\r
1005 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1009 static float counter1 = 0;
\r
1010 counter1 -= dtime;
\r
1011 if(counter1 < 0.0)
\r
1013 counter1 = 0.1*Rand(1, 30);
\r
1014 leftclicked = true;
\r
1018 static float counter1 = 0;
\r
1019 counter1 -= dtime;
\r
1020 if(counter1 < 0.0)
\r
1022 counter1 = 0.1*Rand(1, 20);
\r
1023 rightclicked = true;
\r
1026 mousepos += mousespeed;
\r
1029 s32 Rand(s32 min, s32 max)
\r
1031 return (myrand()%(max-min+1))+min;
\r
1034 bool keydown[KEY_KEY_CODES_COUNT];
\r
1038 bool rightclicked;
\r
1041 void updateViewingRange(f32 frametime_in, Client *client)
\r
1043 if(draw_control.range_all == true)
\r
1046 static f32 added_frametime = 0;
\r
1047 static s16 added_frames = 0;
\r
1049 added_frametime += frametime_in;
\r
1050 added_frames += 1;
\r
1052 // Actually this counter kind of sucks because frametime is busytime
\r
1053 static f32 counter = 0;
\r
1054 counter -= frametime_in;
\r
1060 /*dstream<<__FUNCTION_NAME
\r
1061 <<": Collected "<<added_frames<<" frames, total of "
\r
1062 <<added_frametime<<"s."<<std::endl;*/
\r
1064 /*dstream<<"draw_control.blocks_drawn="
\r
1065 <<draw_control.blocks_drawn
\r
1066 <<", draw_control.blocks_would_have_drawn="
\r
1067 <<draw_control.blocks_would_have_drawn
\r
1070 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1071 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1073 draw_control.wanted_min_range = range_min;
\r
1074 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1076 float block_draw_ratio = 1.0;
\r
1077 if(draw_control.blocks_would_have_drawn != 0)
\r
1079 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1080 / (float)draw_control.blocks_would_have_drawn;
\r
1083 // Calculate the average frametime in the case that all wanted
\r
1084 // blocks had been drawn
\r
1085 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1087 added_frametime = 0.0;
\r
1090 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1091 float wanted_frametime = 1.0 / wanted_fps;
\r
1093 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1094 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1096 // If needed frametime change is very small, just return
\r
1097 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1099 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1103 float range = draw_control.wanted_range;
\r
1104 float new_range = range;
\r
1106 static s16 range_old = 0;
\r
1107 static f32 frametime_old = 0;
\r
1109 float d_range = range - range_old;
\r
1110 f32 d_frametime = frametime - frametime_old;
\r
1111 // A sane default of 30ms per 50 nodes of range
\r
1112 static f32 time_per_range = 30. / 50;
\r
1115 time_per_range = d_frametime / d_range;
\r
1118 // The minimum allowed calculated frametime-range derivative:
\r
1119 // Practically this sets the maximum speed of changing the range.
\r
1120 // The lower this value, the higher the maximum changing speed.
\r
1121 // A low value here results in wobbly range (0.001)
\r
1122 // A high value here results in slow changing range (0.0025)
\r
1123 // SUGG: This could be dynamically adjusted so that when
\r
1124 // the camera is turning, this is lower
\r
1125 //float min_time_per_range = 0.0015;
\r
1126 float min_time_per_range = 0.0010;
\r
1127 //float min_time_per_range = 0.05 / range;
\r
1128 if(time_per_range < min_time_per_range)
\r
1130 time_per_range = min_time_per_range;
\r
1131 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1135 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1138 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1139 // Dampen the change a bit to kill oscillations
\r
1140 //wanted_range_change *= 0.9;
\r
1141 //wanted_range_change *= 0.75;
\r
1142 wanted_range_change *= 0.5;
\r
1143 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1145 // If needed range change is very small, just return
\r
1146 if(fabs(wanted_range_change) < 0.001)
\r
1148 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1152 new_range += wanted_range_change;
\r
1153 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1155 //float new_range_unclamped = new_range;
\r
1156 if(new_range < range_min)
\r
1157 new_range = range_min;
\r
1158 if(new_range > range_max)
\r
1159 new_range = range_max;
\r
1161 /*if(new_range != new_range_unclamped)
\r
1162 dstream<<", clamped to "<<new_range<<std::endl;
\r
1164 dstream<<std::endl;*/
\r
1166 draw_control.wanted_range = new_range;
\r
1168 range_old = new_range;
\r
1169 frametime_old = frametime;
\r
1172 class GUIQuickInventory : public IEventReceiver
\r
1175 GUIQuickInventory(
\r
1176 gui::IGUIEnvironment* env,
\r
1177 gui::IGUIElement* parent,
\r
1180 Inventory *inventory):
\r
1181 m_itemcount(itemcount),
\r
1182 m_inventory(inventory)
\r
1184 core::rect<s32> imgsize(0,0,48,48);
\r
1185 core::rect<s32> textsize(0,0,48,16);
\r
1186 v2s32 spacing(0, 64);
\r
1187 for(s32 i=0; i<m_itemcount; i++)
\r
1189 m_images.push_back(env->addImage(
\r
1190 imgsize + pos + spacing*i
\r
1192 m_images[i]->setScaleImage(true);
\r
1193 m_texts.push_back(env->addStaticText(
\r
1195 textsize + pos + spacing*i,
\r
1198 m_texts[i]->setBackgroundColor(
\r
1199 video::SColor(128,0,0,0));
\r
1200 m_texts[i]->setTextAlignment(
\r
1201 gui::EGUIA_CENTER,
\r
1202 gui::EGUIA_UPPERLEFT);
\r
1206 ~GUIQuickInventory()
\r
1208 for(u32 i=0; i<m_texts.size(); i++)
\r
1210 m_texts[i]->remove();
\r
1212 for(u32 i=0; i<m_images.size(); i++)
\r
1214 m_images[i]->remove();
\r
1218 virtual bool OnEvent(const SEvent& event)
\r
1223 void setSelection(s32 i)
\r
1232 start = m_selection - m_itemcount / 2;
\r
1234 InventoryList *mainlist = m_inventory->getList("main");
\r
1236 for(s32 i=0; i<m_itemcount; i++)
\r
1238 s32 j = i + start;
\r
1240 if(j > (s32)mainlist->getSize() - 1)
\r
1241 j -= mainlist->getSize();
\r
1243 j += mainlist->getSize();
\r
1245 InventoryItem *item = mainlist->getItem(j);
\r
1249 m_images[i]->setImage(NULL);
\r
1252 if(m_selection == j)
\r
1253 swprintf(t, 10, L"<-");
\r
1255 swprintf(t, 10, L"");
\r
1256 m_texts[i]->setText(t);
\r
1258 // The next ifs will segfault with a NULL pointer
\r
1263 m_images[i]->setImage(item->getImage());
\r
1266 if(m_selection == j)
\r
1267 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1269 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1270 m_texts[i]->setText(t);
\r
1276 core::array<gui::IGUIStaticText*> m_texts;
\r
1277 core::array<gui::IGUIImage*> m_images;
\r
1278 Inventory *m_inventory;
\r
1289 ChatLine(const std::wstring &a_text):
\r
1295 std::wstring text;
\r
1298 // These are defined global so that they're not optimized too much.
\r
1299 // Can't change them to volatile.
\r
1304 std::string tempstring;
\r
1305 std::string tempstring2;
\r
1310 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1311 TimeTaker timer("Testing std::string speed");
\r
1312 const u32 jj = 10000;
\r
1313 for(u32 j=0; j<jj; j++)
\r
1317 const u32 ii = 10;
\r
1318 for(u32 i=0; i<ii; i++){
\r
1319 tempstring2 += "asd";
\r
1321 for(u32 i=0; i<ii+1; i++){
\r
1322 tempstring += "asd";
\r
1323 if(tempstring == tempstring2)
\r
1329 dstream<<"All of the following tests should take around 100ms each."
\r
1333 TimeTaker timer("Testing floating-point conversion speed");
\r
1335 for(u32 i=0; i<4000000; i++){
\r
1342 TimeTaker timer("Testing floating-point vector speed");
\r
1344 tempv3f1 = v3f(1,2,3);
\r
1345 tempv3f2 = v3f(4,5,6);
\r
1346 for(u32 i=0; i<10000000; i++){
\r
1347 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1348 tempv3f2 += v3f(7,8,9);
\r
1353 TimeTaker timer("Testing core::map speed");
\r
1355 core::map<v2s16, f32> map1;
\r
1358 for(s16 y=0; y<ii; y++){
\r
1359 for(s16 x=0; x<ii; x++){
\r
1360 map1.insert(v2s16(x,y), tempf);
\r
1364 for(s16 y=ii-1; y>=0; y--){
\r
1365 for(s16 x=0; x<ii; x++){
\r
1366 tempf = map1[v2s16(x,y)];
\r
1372 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1373 TimeTaker timer("Testing mutex speed");
\r
1386 // Do at least 10ms
\r
1387 while(timer.getTime() < 10);
\r
1389 u32 dtime = timer.stop();
\r
1390 u32 per_ms = n / dtime;
\r
1391 std::cout<<"Done. "<<dtime<<"ms, "
\r
1392 <<per_ms<<"/ms"<<std::endl;
\r
1396 int main(int argc, char *argv[])
\r
1399 Parse command line
\r
1402 // List all allowed options
\r
1403 core::map<std::string, ValueSpec> allowed_options;
\r
1404 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1405 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1406 "Run server directly"));
\r
1407 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1408 "Load configuration from specified file"));
\r
1409 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1410 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1411 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1412 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1413 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1414 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1416 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1418 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1420 Settings cmd_args;
\r
1422 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1424 if(ret == false || cmd_args.getFlag("help"))
\r
1426 dstream<<"Allowed options:"<<std::endl;
\r
1427 for(core::map<std::string, ValueSpec>::Iterator
\r
1428 i = allowed_options.getIterator();
\r
1429 i.atEnd() == false; i++)
\r
1431 dstream<<" --"<<i.getNode()->getKey();
\r
1432 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1437 dstream<<" <value>";
\r
1439 dstream<<std::endl;
\r
1441 if(i.getNode()->getValue().help != NULL)
\r
1443 dstream<<" "<<i.getNode()->getValue().help
\r
1448 return cmd_args.getFlag("help") ? 0 : 1;
\r
1452 Low-level initialization
\r
1455 bool disable_stderr = false;
\r
1457 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1458 disable_stderr = true;
\r
1461 // Initialize debug streams
\r
1462 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1463 // Initialize debug stacks
\r
1464 debug_stacks_init();
\r
1466 DSTACK(__FUNCTION_NAME);
\r
1468 porting::initializePaths();
\r
1469 // Create user data directory
\r
1470 fs::CreateDir(porting::path_userdata);
\r
1472 // C-style stuff initialization
\r
1473 initializeMaterialProperties();
\r
1476 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1478 // Print startup message
\r
1479 dstream<<DTIME<<"minetest-c55"
\r
1480 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1481 <<", "<<BUILD_INFO
\r
1485 Basic initialization
\r
1488 // Initialize default settings
\r
1489 set_default_settings();
\r
1491 // Set locale. This is for forcing '.' as the decimal point.
\r
1492 std::locale::global(std::locale("C"));
\r
1493 // This enables printing all characters in bitmap font
\r
1494 setlocale(LC_CTYPE, "en_US");
\r
1496 // Initialize sockets
\r
1498 atexit(sockets_cleanup);
\r
1508 // Path of configuration file in use
\r
1509 std::string configpath = "";
\r
1511 if(cmd_args.exists("config"))
\r
1513 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1516 dstream<<"Could not read configuration from \""
\r
1517 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1520 configpath = cmd_args.get("config");
\r
1524 core::array<std::string> filenames;
\r
1525 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1526 #ifdef RUN_IN_PLACE
\r
1527 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1530 for(u32 i=0; i<filenames.size(); i++)
\r
1532 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1535 configpath = filenames[i];
\r
1541 // Initialize random seed
\r
1546 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1548 These are needed for unit tests at least.
\r
1551 IIrrlichtWrapper irrlicht_dummy;
\r
1553 init_mapnode(&irrlicht_dummy);
\r
1559 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1560 || cmd_args.getFlag("enable-unittests") == true)
\r
1565 /*for(s16 y=-100; y<100; y++)
\r
1566 for(s16 x=-100; x<100; x++)
\r
1568 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1578 if(cmd_args.exists("port"))
\r
1579 port = cmd_args.getU16("port");
\r
1580 else if(cmd_args.exists("port"))
\r
1581 port = g_settings.getU16("port");
\r
1584 std::string map_dir = porting::path_userdata+"/map";
\r
1585 if(cmd_args.exists("map-dir"))
\r
1586 map_dir = cmd_args.get("map-dir");
\r
1587 else if(g_settings.exists("map-dir"))
\r
1588 map_dir = g_settings.get("map-dir");
\r
1590 // Run dedicated server if asked to
\r
1591 if(cmd_args.getFlag("server"))
\r
1593 DSTACK("Dedicated server branch");
\r
1596 Server server(map_dir.c_str());
\r
1597 server.start(port);
\r
1600 dedicated_server_loop(server);
\r
1609 // Address to connect to
\r
1610 std::string address = "";
\r
1612 if(cmd_args.exists("address"))
\r
1614 address = cmd_args.get("address");
\r
1618 address = g_settings.get("address");
\r
1621 std::string playername = g_settings.get("name");
\r
1624 Resolution selection
\r
1627 bool fullscreen = false;
\r
1628 u16 screenW = g_settings.getU16("screenW");
\r
1629 u16 screenH = g_settings.getU16("screenH");
\r
1633 MyEventReceiver receiver;
\r
1635 video::E_DRIVER_TYPE driverType;
\r
1638 //driverType = video::EDT_DIRECT3D9;
\r
1639 driverType = video::EDT_OPENGL;
\r
1641 driverType = video::EDT_OPENGL;
\r
1642 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1645 // create device and exit if creation failed
\r
1647 IrrlichtDevice *device;
\r
1648 device = createDevice(driverType,
\r
1649 core::dimension2d<u32>(screenW, screenH),
\r
1650 16, fullscreen, false, false, &receiver);
\r
1653 return 1; // could not create selected driver.
\r
1655 g_device = device;
\r
1656 g_irrlicht = new IrrlichtWrapper(device);
\r
1657 g_texturesource = new TextureSource(device);
\r
1660 Speed tests (done after irrlicht is loaded to get timer)
\r
1662 if(cmd_args.getFlag("speedtests"))
\r
1664 dstream<<"Running speed tests"<<std::endl;
\r
1669 device->setResizable(true);
\r
1671 bool random_input = g_settings.getBool("random_input")
\r
1672 || cmd_args.getFlag("random-input");
\r
1674 g_input = new RandomInputHandler();
\r
1676 g_input = new RealInputHandler(device, &receiver);
\r
1679 Continue initialization
\r
1682 video::IVideoDriver* driver = device->getVideoDriver();
\r
1685 This changes the minimum allowed number of vertices in a VBO.
\r
1688 //driver->setMinHardwareBufferVertexCount(50);
\r
1690 scene::ISceneManager* smgr = device->getSceneManager();
\r
1692 guienv = device->getGUIEnvironment();
\r
1693 gui::IGUISkin* skin = guienv->getSkin();
\r
1694 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1696 skin->setFont(font);
\r
1698 dstream<<"WARNING: Font file was not found."
\r
1699 " Using default font."<<std::endl;
\r
1700 // If font was not found, this will get us one
\r
1701 font = skin->getFont();
\r
1704 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1705 dstream<<"text_height="<<text_height<<std::endl;
\r
1707 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1708 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1709 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1710 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1711 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1712 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1715 Preload some textures and stuff
\r
1718 init_content_inventory_texture_paths();
\r
1719 init_mapnode(g_irrlicht);
\r
1720 init_mineral(g_irrlicht);
\r
1727 We need some kind of a root node to be able to add
\r
1728 custom gui elements directly on the screen.
\r
1729 Otherwise they won't be automatically drawn.
\r
1731 guiroot = guienv->addStaticText(L"",
\r
1732 core::rect<s32>(0, 0, 10000, 10000));
\r
1734 // First line of debug text
\r
1735 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1737 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1739 // Second line of debug text
\r
1740 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1742 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1745 // At the middle of the screen
\r
1746 // Object infos are shown in this
\r
1747 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1749 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1753 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1755 core::rect<s32>(0,0,0,0),
\r
1757 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1758 core::list<ChatLine> chat_lines;
\r
1761 If an error occurs, this is set to something and the
\r
1762 menu-game loop is restarted. It is then displayed before
\r
1765 std::wstring error_message = L"";
\r
1770 while(g_device->run())
\r
1773 // This is used for catching disconnects
\r
1778 Out-of-game menu loop.
\r
1780 Loop quits when menu returns proper parameters.
\r
1784 // Cursor can be non-visible when coming from the game
\r
1785 device->getCursorControl()->setVisible(true);
\r
1786 // Some stuff are left to scene manager when coming from the game
\r
1787 // (map at least?)
\r
1789 // Reset or hide the debug gui texts
\r
1790 guitext->setText(L"Minetest-c55");
\r
1791 guitext2->setVisible(false);
\r
1792 guitext_info->setVisible(false);
\r
1793 guitext_chat->setVisible(false);
\r
1795 // Initialize menu data
\r
1796 MainMenuData menudata;
\r
1797 menudata.address = narrow_to_wide(address);
\r
1798 menudata.name = narrow_to_wide(playername);
\r
1799 menudata.port = narrow_to_wide(itos(port));
\r
1800 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1802 GUIMainMenu *menu =
\r
1803 new GUIMainMenu(guienv, guiroot, -1,
\r
1804 &g_menumgr, &menudata, &g_gamecallback);
\r
1805 menu->allowFocusRemoval(true);
\r
1807 if(error_message != L"")
\r
1809 GUIMessageMenu *menu2 =
\r
1810 new GUIMessageMenu(guienv, guiroot, -1,
\r
1811 &g_menumgr, error_message.c_str());
\r
1813 error_message = L"";
\r
1816 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1818 dstream<<"Created main menu"<<std::endl;
\r
1820 while(g_device->run())
\r
1822 // Run global IrrlichtWrapper's main thread processing stuff
\r
1823 g_irrlicht->Run();
\r
1825 if(menu->getStatus() == true)
\r
1828 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1829 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1830 guienv->drawAll();
\r
1831 driver->endScene();
\r
1834 // Break out of menu-game loop to shut down cleanly
\r
1835 if(g_device->run() == false)
\r
1838 dstream<<"Dropping main menu"<<std::endl;
\r
1842 // Delete map if requested
\r
1843 if(menudata.delete_map)
\r
1845 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1847 error_message = L"Delete failed";
\r
1851 playername = wide_to_narrow(menudata.name);
\r
1852 address = wide_to_narrow(menudata.address);
\r
1853 port = stoi(wide_to_narrow(menudata.port));
\r
1854 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1856 // Check for valid parameters, restart menu if invalid.
\r
1857 if(playername == "")
\r
1859 error_message = L"Name required.";
\r
1864 g_settings.set("name", playername);
\r
1865 g_settings.set("address", address);
\r
1866 g_settings.set("port", itos(port));
\r
1867 // Update configuration file
\r
1868 if(configpath != "")
\r
1869 g_settings.updateConfigFile(configpath.c_str());
\r
1871 // Continue to game
\r
1875 // Break out of menu-game loop to shut down cleanly
\r
1876 if(g_device->run() == false)
\r
1880 Make a scope here so that the client and the server and other
\r
1881 stuff gets removed when disconnected or the irrlicht device
\r
1886 // This is set to true at the end of the scope
\r
1887 g_irrlicht->Shutdown(false);
\r
1890 Draw "Loading" screen
\r
1892 const wchar_t *text = L"Loading and connecting...";
\r
1893 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1894 core::vector2d<s32> textsize(300, text_height);
\r
1895 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1897 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1898 text, textrect, false, false);
\r
1899 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1901 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1902 guienv->drawAll();
\r
1903 driver->endScene();
\r
1905 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1909 SharedPtr will delete it when it goes out of scope.
\r
1911 SharedPtr<Server> server;
\r
1912 if(address == ""){
\r
1913 server = new Server(map_dir);
\r
1914 server->start(port);
\r
1921 Client client(device, playername.c_str(), draw_control);
\r
1923 g_client = &client;
\r
1925 Address connect_address(0,0,0,0, port);
\r
1928 //connect_address.Resolve("localhost");
\r
1929 connect_address.setAddress(127,0,0,1);
\r
1931 connect_address.Resolve(address.c_str());
\r
1933 catch(ResolveError &e)
\r
1935 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1937 error_message = L"Couldn't resolve address";
\r
1938 gui_loadingtext->remove();
\r
1942 dstream<<DTIME<<"Connecting to server at ";
\r
1943 connect_address.print(&dstream);
\r
1944 dstream<<std::endl;
\r
1945 client.connect(connect_address);
\r
1948 while(client.connectedAndInitialized() == false)
\r
1951 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1952 guienv->drawAll();
\r
1953 driver->endScene();
\r
1955 // Update client and server
\r
1959 if(server != NULL)
\r
1960 server->step(0.1);
\r
1966 catch(con::PeerNotFoundException &e)
\r
1968 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1970 error_message = L"Connection timed out.";
\r
1971 gui_loadingtext->remove();
\r
1978 /*scene::ISceneNode* skybox;
\r
1979 skybox = smgr->addSkyBoxSceneNode(
\r
1980 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1981 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1982 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1983 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1984 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1985 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1988 Create the camera node
\r
1991 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1992 0, // Camera parent
\r
1993 v3f(BS*100, BS*2, BS*100), // Look from
\r
1994 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1998 if(camera == NULL)
\r
2001 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2003 camera->setFOV(FOV_ANGLE);
\r
2005 // Just so big a value that everything rendered is visible
\r
2006 camera->setFarValue(100000*BS);
\r
2008 f32 camera_yaw = 0; // "right/left"
\r
2009 f32 camera_pitch = 0; // "up/down"
\r
2015 gui_loadingtext->remove();
\r
2018 Add some gui stuff
\r
2021 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2022 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2024 // Test the text input system
\r
2025 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2027 /*GUIMessageMenu *menu =
\r
2028 new GUIMessageMenu(guienv, guiroot, -1,
\r
2033 // Launch pause menu
\r
2034 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2035 &g_menumgr))->drop();
\r
2038 guitext2->setVisible(true);
\r
2039 guitext_info->setVisible(true);
\r
2040 guitext_chat->setVisible(true);
\r
2043 Some statistics are collected in these
\r
2046 u32 beginscenetime = 0;
\r
2047 u32 scenetime = 0;
\r
2048 u32 endscenetime = 0;
\r
2051 //throw con::PeerNotFoundException("lol");
\r
2053 core::list<float> frametime_log;
\r
2059 bool first_loop_after_window_activation = true;
\r
2061 // Time is in milliseconds
\r
2062 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2063 // NOTE: So we have to use getTime() and call run()s between them
\r
2064 u32 lasttime = device->getTimer()->getTime();
\r
2066 while(device->run())
\r
2068 if(g_disconnect_requested)
\r
2070 g_disconnect_requested = false;
\r
2075 Run global IrrlichtWrapper's main thread processing stuff
\r
2077 g_irrlicht->Run();
\r
2080 Process TextureSource's queue
\r
2082 g_texturesource->processQueue();
\r
2085 Random calculations
\r
2087 v2u32 screensize = driver->getScreenSize();
\r
2088 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2090 // Hilight boxes collected during the loop and displayed
\r
2091 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2094 std::wstring infotext;
\r
2096 //TimeTaker //timer1("//timer1");
\r
2098 // Time of frame without fps limit
\r
2102 // not using getRealTime is necessary for wine
\r
2103 u32 time = device->getTimer()->getTime();
\r
2104 if(time > lasttime)
\r
2105 busytime_u32 = time - lasttime;
\r
2108 busytime = busytime_u32 / 1000.0;
\r
2111 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2113 // Absolutelu necessary for wine!
\r
2120 updateViewingRange(busytime, &client);
\r
2127 float fps_max = g_settings.getFloat("fps_max");
\r
2128 u32 frametime_min = 1000./fps_max;
\r
2130 if(busytime_u32 < frametime_min)
\r
2132 u32 sleeptime = frametime_min - busytime_u32;
\r
2133 device->sleep(sleeptime);
\r
2137 // Absolutelu necessary for wine!
\r
2141 Time difference calculation
\r
2143 f32 dtime; // in seconds
\r
2145 u32 time = device->getTimer()->getTime();
\r
2146 if(time > lasttime)
\r
2147 dtime = (time - lasttime) / 1000.0;
\r
2153 Log frametime for visualization
\r
2155 frametime_log.push_back(dtime);
\r
2156 if(frametime_log.size() > 100)
\r
2158 core::list<float>::Iterator i = frametime_log.begin();
\r
2159 frametime_log.erase(i);
\r
2163 Visualize frametime in terminal
\r
2165 /*for(u32 i=0; i<dtime*400; i++)
\r
2167 std::cout<<std::endl;*/
\r
2170 Time average and jitter calculation
\r
2173 static f32 dtime_avg1 = 0.0;
\r
2174 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2175 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2177 static f32 dtime_jitter1_max_sample = 0.0;
\r
2178 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2180 static f32 jitter1_max = 0.0;
\r
2181 static f32 counter = 0.0;
\r
2182 if(dtime_jitter1 > jitter1_max)
\r
2183 jitter1_max = dtime_jitter1;
\r
2188 dtime_jitter1_max_sample = jitter1_max;
\r
2189 dtime_jitter1_max_fraction
\r
2190 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2191 jitter1_max = 0.0;
\r
2196 Busytime average and jitter calculation
\r
2199 static f32 busytime_avg1 = 0.0;
\r
2200 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2201 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2203 static f32 busytime_jitter1_max_sample = 0.0;
\r
2204 static f32 busytime_jitter1_min_sample = 0.0;
\r
2206 static f32 jitter1_max = 0.0;
\r
2207 static f32 jitter1_min = 0.0;
\r
2208 static f32 counter = 0.0;
\r
2209 if(busytime_jitter1 > jitter1_max)
\r
2210 jitter1_max = busytime_jitter1;
\r
2211 if(busytime_jitter1 < jitter1_min)
\r
2212 jitter1_min = busytime_jitter1;
\r
2214 if(counter > 0.0){
\r
2216 busytime_jitter1_max_sample = jitter1_max;
\r
2217 busytime_jitter1_min_sample = jitter1_min;
\r
2218 jitter1_max = 0.0;
\r
2219 jitter1_min = 0.0;
\r
2224 Debug info for client
\r
2227 static float counter = 0.0;
\r
2232 client.printDebugInfo(std::cout);
\r
2237 Input handler step()
\r
2239 g_input->step(dtime);
\r
2242 Player speed control
\r
2251 bool a_superspeed,
\r
2254 PlayerControl control(
\r
2255 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2256 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2257 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2258 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2259 g_input->isKeyDown(irr::KEY_SPACE),
\r
2260 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2264 client.setPlayerControl(control);
\r
2268 Process environment
\r
2272 //TimeTaker timer("client.step(dtime)");
\r
2273 client.step(dtime);
\r
2274 //client.step(dtime_avg1);
\r
2277 if(server != NULL)
\r
2279 //TimeTaker timer("server->step(dtime)");
\r
2280 server->step(dtime);
\r
2283 v3f player_position = client.getPlayerPosition();
\r
2285 //TimeTaker //timer2("//timer2");
\r
2288 Mouse and camera control
\r
2291 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2294 device->getCursorControl()->setVisible(false);
\r
2296 if(first_loop_after_window_activation){
\r
2297 //std::cout<<"window active, first loop"<<std::endl;
\r
2298 first_loop_after_window_activation = false;
\r
2301 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2302 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2303 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2304 camera_yaw -= dx*0.2;
\r
2305 camera_pitch += dy*0.2;
\r
2306 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2307 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2309 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2312 device->getCursorControl()->setVisible(true);
\r
2314 //std::cout<<"window inactive"<<std::endl;
\r
2315 first_loop_after_window_activation = true;
\r
2318 camera_yaw = wrapDegrees(camera_yaw);
\r
2319 camera_pitch = wrapDegrees(camera_pitch);
\r
2321 v3f camera_direction = v3f(0,0,1);
\r
2322 camera_direction.rotateYZBy(camera_pitch);
\r
2323 camera_direction.rotateXZBy(camera_yaw);
\r
2325 // This is at the height of the eyes of the current figure
\r
2326 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2327 // This is more like in minecraft
\r
2328 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2330 camera->setPosition(camera_position);
\r
2331 // *100.0 helps in large map coordinates
\r
2332 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2334 if(FIELD_OF_VIEW_TEST){
\r
2335 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2336 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2339 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2340 //TimeTaker timer("client.updateCamera");
\r
2341 client.updateCamera(camera_position, camera_direction);
\r
2345 //TimeTaker //timer3("//timer3");
\r
2348 Calculate what block is the crosshair pointing to
\r
2351 //u32 t1 = device->getTimer()->getRealTime();
\r
2353 //f32 d = 4; // max. distance
\r
2354 f32 d = 4; // max. distance
\r
2355 core::line3d<f32> shootline(camera_position,
\r
2356 camera_position + camera_direction * BS * (d+1));
\r
2358 MapBlockObject *selected_object = client.getSelectedObject
\r
2359 (d*BS, camera_position, shootline);
\r
2362 If it's pointing to a MapBlockObject
\r
2365 if(selected_object != NULL)
\r
2367 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2369 core::aabbox3d<f32> box_on_map
\r
2370 = selected_object->getSelectionBoxOnMap();
\r
2372 hilightboxes.push_back(box_on_map);
\r
2374 infotext = narrow_to_wide(selected_object->infoText());
\r
2376 if(g_input->getLeftClicked())
\r
2378 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2379 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2380 selected_object->getId(), g_selected_item);
\r
2382 else if(g_input->getRightClicked())
\r
2384 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2386 Check if we want to modify the object ourselves
\r
2388 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2390 dstream<<"Sign object right-clicked"<<std::endl;
\r
2392 if(random_input == false)
\r
2394 // Get a new text for it
\r
2396 TextDest *dest = new TextDestSign(
\r
2397 selected_object->getBlock()->getPos(),
\r
2398 selected_object->getId(),
\r
2401 SignObject *sign_object = (SignObject*)selected_object;
\r
2403 std::wstring wtext =
\r
2404 narrow_to_wide(sign_object->getText());
\r
2406 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2412 Otherwise pass the event to the server as-is
\r
2416 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2417 selected_object->getId(), g_selected_item);
\r
2421 else // selected_object == NULL
\r
2425 Find out which node we are pointing at
\r
2428 bool nodefound = false;
\r
2430 v3s16 neighbourpos;
\r
2431 core::aabbox3d<f32> nodehilightbox;
\r
2432 f32 mindistance = BS * 1001;
\r
2434 v3s16 pos_i = floatToInt(player_position);
\r
2436 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2440 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2441 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2442 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2443 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2444 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2445 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2447 for(s16 y = ystart; y <= yend; y++)
\r
2448 for(s16 z = zstart; z <= zend; z++)
\r
2449 for(s16 x = xstart; x <= xend; x++)
\r
2454 n = client.getNode(v3s16(x,y,z));
\r
2455 if(content_pointable(n.d) == false)
\r
2458 catch(InvalidPositionException &e)
\r
2464 v3f npf = intToFloat(np);
\r
2469 v3s16(0,0,1), // back
\r
2470 v3s16(0,1,0), // top
\r
2471 v3s16(1,0,0), // right
\r
2472 v3s16(0,0,-1), // front
\r
2473 v3s16(0,-1,0), // bottom
\r
2474 v3s16(-1,0,0), // left
\r
2480 if(n.d == CONTENT_TORCH)
\r
2482 v3s16 dir = unpackDir(n.dir);
\r
2483 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2484 dir_f *= BS/2 - BS/6 - BS/20;
\r
2485 v3f cpf = npf + dir_f;
\r
2486 f32 distance = (cpf - camera_position).getLength();
\r
2488 core::aabbox3d<f32> box;
\r
2491 if(dir == v3s16(0,-1,0))
\r
2493 box = core::aabbox3d<f32>(
\r
2494 npf - v3f(BS/6, BS/2, BS/6),
\r
2495 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2499 else if(dir == v3s16(0,1,0))
\r
2501 box = core::aabbox3d<f32>(
\r
2502 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2503 npf + v3f(BS/6, BS/2, BS/6)
\r
2509 box = core::aabbox3d<f32>(
\r
2510 cpf - v3f(BS/6, BS/3, BS/6),
\r
2511 cpf + v3f(BS/6, BS/3, BS/6)
\r
2515 if(distance < mindistance)
\r
2517 if(box.intersectsWithLine(shootline))
\r
2521 neighbourpos = np;
\r
2522 mindistance = distance;
\r
2523 nodehilightbox = box;
\r
2532 for(u16 i=0; i<6; i++)
\r
2534 v3f dir_f = v3f(dirs[i].X,
\r
2535 dirs[i].Y, dirs[i].Z);
\r
2536 v3f centerpoint = npf + dir_f * BS/2;
\r
2538 (centerpoint - camera_position).getLength();
\r
2540 if(distance < mindistance)
\r
2542 core::CMatrix4<f32> m;
\r
2543 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2545 // This is the back face
\r
2546 v3f corners[2] = {
\r
2547 v3f(BS/2, BS/2, BS/2),
\r
2548 v3f(-BS/2, -BS/2, BS/2+d)
\r
2551 for(u16 j=0; j<2; j++)
\r
2553 m.rotateVect(corners[j]);
\r
2554 corners[j] += npf;
\r
2557 core::aabbox3d<f32> facebox(corners[0]);
\r
2558 facebox.addInternalPoint(corners[1]);
\r
2560 if(facebox.intersectsWithLine(shootline))
\r
2564 neighbourpos = np + dirs[i];
\r
2565 mindistance = distance;
\r
2567 //nodehilightbox = facebox;
\r
2569 const float d = 0.502;
\r
2570 core::aabbox3d<f32> nodebox
\r
2571 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2572 v3f nodepos_f = intToFloat(nodepos);
\r
2573 nodebox.MinEdge += nodepos_f;
\r
2574 nodebox.MaxEdge += nodepos_f;
\r
2575 nodehilightbox = nodebox;
\r
2577 } // if distance < mindistance
\r
2579 } // regular block
\r
2582 static float nodig_delay_counter = 0.0;
\r
2586 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2588 static float dig_time = 0.0;
\r
2589 static u16 dig_index = 0;
\r
2591 // Visualize selection
\r
2593 hilightboxes.push_back(nodehilightbox);
\r
2597 if(g_input->getLeftReleased())
\r
2599 client.clearTempMod(nodepos);
\r
2603 if(nodig_delay_counter > 0.0)
\r
2605 nodig_delay_counter -= dtime;
\r
2609 if(nodepos != nodepos_old)
\r
2611 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2612 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2614 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2616 client.clearTempMod(nodepos_old);
\r
2621 if(g_input->getLeftClicked() ||
\r
2622 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2624 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2625 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2627 if(g_input->getLeftClicked())
\r
2629 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2631 if(g_input->getLeftState())
\r
2633 MapNode n = client.getNode(nodepos);
\r
2635 // Get tool name. Default is "" = bare hands
\r
2636 std::string toolname = "";
\r
2637 InventoryList *mlist = local_inventory.getList("main");
\r
2640 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2641 if(item && (std::string)item->getName() == "ToolItem")
\r
2643 ToolItem *titem = (ToolItem*)item;
\r
2644 toolname = titem->getToolName();
\r
2648 // Get digging properties for material and tool
\r
2649 u8 material = n.d;
\r
2650 DiggingProperties prop =
\r
2651 getDiggingProperties(material, toolname);
\r
2653 float dig_time_complete = 0.0;
\r
2655 if(prop.diggable == false)
\r
2657 /*dstream<<"Material "<<(int)material
\r
2658 <<" not diggable with \""
\r
2659 <<toolname<<"\""<<std::endl;*/
\r
2660 // I guess nobody will wait for this long
\r
2661 dig_time_complete = 10000000.0;
\r
2665 dig_time_complete = prop.time;
\r
2668 if(dig_time_complete >= 0.001)
\r
2670 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2671 * dig_time/dig_time_complete);
\r
2673 // This is for torches
\r
2676 dig_index = CRACK_ANIMATION_LENGTH;
\r
2679 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2681 //TimeTaker timer("client.setTempMod");
\r
2682 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2683 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2687 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2688 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2689 client.clearTempMod(nodepos);
\r
2690 client.removeNode(nodepos);
\r
2694 nodig_delay_counter = dig_time_complete
\r
2695 / (float)CRACK_ANIMATION_LENGTH;
\r
2697 // We don't want a corresponding delay to
\r
2698 // very time consuming nodes
\r
2699 if(nodig_delay_counter > 0.5)
\r
2701 nodig_delay_counter = 0.5;
\r
2703 // We want a slight delay to very little
\r
2704 // time consuming nodes
\r
2705 float mindelay = 0.15;
\r
2706 if(nodig_delay_counter < mindelay)
\r
2708 nodig_delay_counter = mindelay;
\r
2712 dig_time += dtime;
\r
2716 if(g_input->getRightClicked())
\r
2718 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2719 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2722 nodepos_old = nodepos;
\r
2727 } // selected_object == NULL
\r
2729 g_input->resetLeftClicked();
\r
2730 g_input->resetRightClicked();
\r
2732 if(g_input->getLeftReleased())
\r
2734 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2736 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2738 if(g_input->getRightReleased())
\r
2740 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2744 g_input->resetLeftReleased();
\r
2745 g_input->resetRightReleased();
\r
2748 Calculate stuff for drawing
\r
2751 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2753 u32 daynight_ratio = client.getDayNightRatio();
\r
2754 /*video::SColor bgcolor = video::SColor(
\r
2756 skycolor.getRed() * daynight_ratio / 1000,
\r
2757 skycolor.getGreen() * daynight_ratio / 1000,
\r
2758 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2760 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2761 video::SColor bgcolor = video::SColor(
\r
2763 skycolor.getRed() * l / 255,
\r
2764 skycolor.getGreen() * l / 255,
\r
2765 skycolor.getBlue() * l / 255);
\r
2771 if(g_settings.getBool("enable_fog") == true)
\r
2773 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2774 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*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
3006 for(core::list<float>::Iterator
\r
3007 i = frametime_log.begin();
\r
3008 i != frametime_log.end();
\r
3011 driver->draw2DLine(v2s32(x,50),
\r
3012 v2s32(x,50+(*i)*1000),
\r
3013 video::SColor(255,255,255,255));
\r
3021 //TimeTaker //timer11("//timer11");
\r
3027 guienv->drawAll();
\r
3031 TimeTaker timer("endScene");
\r
3032 driver->endScene();
\r
3033 endscenetime = timer.stop(true);
\r
3036 drawtime = drawtimer.stop(true);
\r
3042 static s16 lastFPS = 0;
\r
3043 //u16 fps = driver->getFPS();
\r
3044 u16 fps = (1.0/dtime_avg1);
\r
3046 if (lastFPS != fps)
\r
3048 core::stringw str = L"Minetest [";
\r
3049 str += driver->getName();
\r
3053 device->setWindowCaption(str.c_str());
\r
3059 device->yield();*/
\r
3062 delete quick_inventory;
\r
3065 Disable texture fetches and other stuff that is queued
\r
3066 to be processed by the main loop.
\r
3068 This has to be done before client goes out of scope.
\r
3070 g_irrlicht->Shutdown(true);
\r
3072 } // client and server are deleted at this point
\r
3075 catch(con::PeerNotFoundException &e)
\r
3077 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3078 error_message = L"Connection timed out.";
\r
3081 } // Menu-game loop
\r
3086 In the end, delete the Irrlicht device.
\r
3091 Update configuration file
\r
3093 /*if(configpath != "")
\r
3095 g_settings.updateConfigFile(configpath.c_str());
\r
3098 END_DEBUG_EXCEPTION_HANDLER
\r
3100 debugstreams_deinit();
\r