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
2057 bool first_loop_after_window_activation = true;
\r
2059 // Time is in milliseconds
\r
2060 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2061 // NOTE: So we have to use getTime() and call run()s between them
\r
2062 u32 lasttime = device->getTimer()->getTime();
\r
2064 while(device->run())
\r
2066 if(g_disconnect_requested)
\r
2068 g_disconnect_requested = false;
\r
2073 Run global IrrlichtWrapper's main thread processing stuff
\r
2075 g_irrlicht->Run();
\r
2078 Process TextureSource's queue
\r
2080 g_texturesource->processQueue();
\r
2083 Random calculations
\r
2085 v2u32 screensize = driver->getScreenSize();
\r
2086 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2088 // Hilight boxes collected during the loop and displayed
\r
2089 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2092 std::wstring infotext;
\r
2094 //TimeTaker //timer1("//timer1");
\r
2096 // Time of frame without fps limit
\r
2100 // not using getRealTime is necessary for wine
\r
2101 u32 time = device->getTimer()->getTime();
\r
2102 if(time > lasttime)
\r
2103 busytime_u32 = time - lasttime;
\r
2106 busytime = busytime_u32 / 1000.0;
\r
2109 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2111 // Absolutelu necessary for wine!
\r
2118 updateViewingRange(busytime, &client);
\r
2125 float fps_max = g_settings.getFloat("fps_max");
\r
2126 u32 frametime_min = 1000./fps_max;
\r
2128 if(busytime_u32 < frametime_min)
\r
2130 u32 sleeptime = frametime_min - busytime_u32;
\r
2131 device->sleep(sleeptime);
\r
2135 // Absolutelu necessary for wine!
\r
2139 Time difference calculation
\r
2141 f32 dtime; // in seconds
\r
2143 u32 time = device->getTimer()->getTime();
\r
2144 if(time > lasttime)
\r
2145 dtime = (time - lasttime) / 1000.0;
\r
2151 Time average and jitter calculation
\r
2154 static f32 dtime_avg1 = 0.0;
\r
2155 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2156 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2158 static f32 dtime_jitter1_max_sample = 0.0;
\r
2159 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2161 static f32 jitter1_max = 0.0;
\r
2162 static f32 counter = 0.0;
\r
2163 if(dtime_jitter1 > jitter1_max)
\r
2164 jitter1_max = dtime_jitter1;
\r
2169 dtime_jitter1_max_sample = jitter1_max;
\r
2170 dtime_jitter1_max_fraction
\r
2171 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2172 jitter1_max = 0.0;
\r
2177 Busytime average and jitter calculation
\r
2180 static f32 busytime_avg1 = 0.0;
\r
2181 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2182 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2184 static f32 busytime_jitter1_max_sample = 0.0;
\r
2185 static f32 busytime_jitter1_min_sample = 0.0;
\r
2187 static f32 jitter1_max = 0.0;
\r
2188 static f32 jitter1_min = 0.0;
\r
2189 static f32 counter = 0.0;
\r
2190 if(busytime_jitter1 > jitter1_max)
\r
2191 jitter1_max = busytime_jitter1;
\r
2192 if(busytime_jitter1 < jitter1_min)
\r
2193 jitter1_min = busytime_jitter1;
\r
2195 if(counter > 0.0){
\r
2197 busytime_jitter1_max_sample = jitter1_max;
\r
2198 busytime_jitter1_min_sample = jitter1_min;
\r
2199 jitter1_max = 0.0;
\r
2200 jitter1_min = 0.0;
\r
2205 Debug info for client
\r
2208 static float counter = 0.0;
\r
2213 client.printDebugInfo(std::cout);
\r
2218 Input handler step()
\r
2220 g_input->step(dtime);
\r
2223 Player speed control
\r
2232 bool a_superspeed,
\r
2235 PlayerControl control(
\r
2236 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2237 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2238 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2239 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2240 g_input->isKeyDown(irr::KEY_SPACE),
\r
2241 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2245 client.setPlayerControl(control);
\r
2249 Process environment
\r
2253 //TimeTaker timer("client.step(dtime)");
\r
2254 client.step(dtime);
\r
2255 //client.step(dtime_avg1);
\r
2258 if(server != NULL)
\r
2260 //TimeTaker timer("server->step(dtime)");
\r
2261 server->step(dtime);
\r
2264 v3f player_position = client.getPlayerPosition();
\r
2266 //TimeTaker //timer2("//timer2");
\r
2269 Mouse and camera control
\r
2272 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2275 device->getCursorControl()->setVisible(false);
\r
2277 if(first_loop_after_window_activation){
\r
2278 //std::cout<<"window active, first loop"<<std::endl;
\r
2279 first_loop_after_window_activation = false;
\r
2282 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2283 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2284 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2285 camera_yaw -= dx*0.2;
\r
2286 camera_pitch += dy*0.2;
\r
2287 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2288 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2290 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2293 device->getCursorControl()->setVisible(true);
\r
2295 //std::cout<<"window inactive"<<std::endl;
\r
2296 first_loop_after_window_activation = true;
\r
2299 camera_yaw = wrapDegrees(camera_yaw);
\r
2300 camera_pitch = wrapDegrees(camera_pitch);
\r
2302 v3f camera_direction = v3f(0,0,1);
\r
2303 camera_direction.rotateYZBy(camera_pitch);
\r
2304 camera_direction.rotateXZBy(camera_yaw);
\r
2306 // This is at the height of the eyes of the current figure
\r
2307 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2308 // This is more like in minecraft
\r
2309 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2311 camera->setPosition(camera_position);
\r
2312 // *100.0 helps in large map coordinates
\r
2313 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2315 if(FIELD_OF_VIEW_TEST){
\r
2316 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2317 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2320 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2321 //TimeTaker timer("client.updateCamera");
\r
2322 client.updateCamera(camera_position, camera_direction);
\r
2326 //TimeTaker //timer3("//timer3");
\r
2329 Calculate what block is the crosshair pointing to
\r
2332 //u32 t1 = device->getTimer()->getRealTime();
\r
2334 //f32 d = 4; // max. distance
\r
2335 f32 d = 4; // max. distance
\r
2336 core::line3d<f32> shootline(camera_position,
\r
2337 camera_position + camera_direction * BS * (d+1));
\r
2339 MapBlockObject *selected_object = client.getSelectedObject
\r
2340 (d*BS, camera_position, shootline);
\r
2343 If it's pointing to a MapBlockObject
\r
2346 if(selected_object != NULL)
\r
2348 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2350 core::aabbox3d<f32> box_on_map
\r
2351 = selected_object->getSelectionBoxOnMap();
\r
2353 hilightboxes.push_back(box_on_map);
\r
2355 infotext = narrow_to_wide(selected_object->infoText());
\r
2357 if(g_input->getLeftClicked())
\r
2359 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2360 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2361 selected_object->getId(), g_selected_item);
\r
2363 else if(g_input->getRightClicked())
\r
2365 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2367 Check if we want to modify the object ourselves
\r
2369 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2371 dstream<<"Sign object right-clicked"<<std::endl;
\r
2373 if(random_input == false)
\r
2375 // Get a new text for it
\r
2377 TextDest *dest = new TextDestSign(
\r
2378 selected_object->getBlock()->getPos(),
\r
2379 selected_object->getId(),
\r
2382 SignObject *sign_object = (SignObject*)selected_object;
\r
2384 std::wstring wtext =
\r
2385 narrow_to_wide(sign_object->getText());
\r
2387 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2393 Otherwise pass the event to the server as-is
\r
2397 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2398 selected_object->getId(), g_selected_item);
\r
2402 else // selected_object == NULL
\r
2406 Find out which node we are pointing at
\r
2409 bool nodefound = false;
\r
2411 v3s16 neighbourpos;
\r
2412 core::aabbox3d<f32> nodehilightbox;
\r
2413 f32 mindistance = BS * 1001;
\r
2415 v3s16 pos_i = floatToInt(player_position);
\r
2417 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2421 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2422 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2423 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2424 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2425 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2426 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2428 for(s16 y = ystart; y <= yend; y++)
\r
2429 for(s16 z = zstart; z <= zend; z++)
\r
2430 for(s16 x = xstart; x <= xend; x++)
\r
2435 n = client.getNode(v3s16(x,y,z));
\r
2436 if(content_pointable(n.d) == false)
\r
2439 catch(InvalidPositionException &e)
\r
2445 v3f npf = intToFloat(np);
\r
2450 v3s16(0,0,1), // back
\r
2451 v3s16(0,1,0), // top
\r
2452 v3s16(1,0,0), // right
\r
2453 v3s16(0,0,-1), // front
\r
2454 v3s16(0,-1,0), // bottom
\r
2455 v3s16(-1,0,0), // left
\r
2461 if(n.d == CONTENT_TORCH)
\r
2463 v3s16 dir = unpackDir(n.dir);
\r
2464 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2465 dir_f *= BS/2 - BS/6 - BS/20;
\r
2466 v3f cpf = npf + dir_f;
\r
2467 f32 distance = (cpf - camera_position).getLength();
\r
2469 core::aabbox3d<f32> box;
\r
2472 if(dir == v3s16(0,-1,0))
\r
2474 box = core::aabbox3d<f32>(
\r
2475 npf - v3f(BS/6, BS/2, BS/6),
\r
2476 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2480 else if(dir == v3s16(0,1,0))
\r
2482 box = core::aabbox3d<f32>(
\r
2483 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2484 npf + v3f(BS/6, BS/2, BS/6)
\r
2490 box = core::aabbox3d<f32>(
\r
2491 cpf - v3f(BS/6, BS/3, BS/6),
\r
2492 cpf + v3f(BS/6, BS/3, BS/6)
\r
2496 if(distance < mindistance)
\r
2498 if(box.intersectsWithLine(shootline))
\r
2502 neighbourpos = np;
\r
2503 mindistance = distance;
\r
2504 nodehilightbox = box;
\r
2513 for(u16 i=0; i<6; i++)
\r
2515 v3f dir_f = v3f(dirs[i].X,
\r
2516 dirs[i].Y, dirs[i].Z);
\r
2517 v3f centerpoint = npf + dir_f * BS/2;
\r
2519 (centerpoint - camera_position).getLength();
\r
2521 if(distance < mindistance)
\r
2523 core::CMatrix4<f32> m;
\r
2524 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2526 // This is the back face
\r
2527 v3f corners[2] = {
\r
2528 v3f(BS/2, BS/2, BS/2),
\r
2529 v3f(-BS/2, -BS/2, BS/2+d)
\r
2532 for(u16 j=0; j<2; j++)
\r
2534 m.rotateVect(corners[j]);
\r
2535 corners[j] += npf;
\r
2538 core::aabbox3d<f32> facebox(corners[0]);
\r
2539 facebox.addInternalPoint(corners[1]);
\r
2541 if(facebox.intersectsWithLine(shootline))
\r
2545 neighbourpos = np + dirs[i];
\r
2546 mindistance = distance;
\r
2548 //nodehilightbox = facebox;
\r
2550 const float d = 0.502;
\r
2551 core::aabbox3d<f32> nodebox
\r
2552 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2553 v3f nodepos_f = intToFloat(nodepos);
\r
2554 nodebox.MinEdge += nodepos_f;
\r
2555 nodebox.MaxEdge += nodepos_f;
\r
2556 nodehilightbox = nodebox;
\r
2558 } // if distance < mindistance
\r
2560 } // regular block
\r
2563 static float nodig_delay_counter = 0.0;
\r
2567 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2569 static float dig_time = 0.0;
\r
2570 static u16 dig_index = 0;
\r
2572 // Visualize selection
\r
2574 hilightboxes.push_back(nodehilightbox);
\r
2578 if(g_input->getLeftReleased())
\r
2580 client.clearTempMod(nodepos);
\r
2584 if(nodig_delay_counter > 0.0)
\r
2586 nodig_delay_counter -= dtime;
\r
2590 if(nodepos != nodepos_old)
\r
2592 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2593 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2595 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2597 client.clearTempMod(nodepos_old);
\r
2602 if(g_input->getLeftClicked() ||
\r
2603 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2605 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2606 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2608 if(g_input->getLeftClicked())
\r
2610 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2612 if(g_input->getLeftState())
\r
2614 MapNode n = client.getNode(nodepos);
\r
2616 // Get tool name. Default is "" = bare hands
\r
2617 std::string toolname = "";
\r
2618 InventoryList *mlist = local_inventory.getList("main");
\r
2621 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2622 if(item && (std::string)item->getName() == "ToolItem")
\r
2624 ToolItem *titem = (ToolItem*)item;
\r
2625 toolname = titem->getToolName();
\r
2629 // Get digging properties for material and tool
\r
2630 u8 material = n.d;
\r
2631 DiggingProperties prop =
\r
2632 getDiggingProperties(material, toolname);
\r
2634 float dig_time_complete = 0.0;
\r
2636 if(prop.diggable == false)
\r
2638 /*dstream<<"Material "<<(int)material
\r
2639 <<" not diggable with \""
\r
2640 <<toolname<<"\""<<std::endl;*/
\r
2641 // I guess nobody will wait for this long
\r
2642 dig_time_complete = 10000000.0;
\r
2646 dig_time_complete = prop.time;
\r
2649 if(dig_time_complete >= 0.001)
\r
2651 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2652 * dig_time/dig_time_complete);
\r
2654 // This is for torches
\r
2657 dig_index = CRACK_ANIMATION_LENGTH;
\r
2660 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2662 //TimeTaker timer("client.setTempMod");
\r
2663 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2664 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2668 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2669 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2670 client.clearTempMod(nodepos);
\r
2671 client.removeNode(nodepos);
\r
2675 nodig_delay_counter = dig_time_complete
\r
2676 / (float)CRACK_ANIMATION_LENGTH;
\r
2678 // We don't want a corresponding delay to
\r
2679 // very time consuming nodes
\r
2680 if(nodig_delay_counter > 0.5)
\r
2682 nodig_delay_counter = 0.5;
\r
2684 // We want a slight delay to very little
\r
2685 // time consuming nodes
\r
2686 float mindelay = 0.15;
\r
2687 if(nodig_delay_counter < mindelay)
\r
2689 nodig_delay_counter = mindelay;
\r
2693 dig_time += dtime;
\r
2697 if(g_input->getRightClicked())
\r
2699 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2700 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2703 nodepos_old = nodepos;
\r
2708 } // selected_object == NULL
\r
2710 g_input->resetLeftClicked();
\r
2711 g_input->resetRightClicked();
\r
2713 if(g_input->getLeftReleased())
\r
2715 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2717 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2719 if(g_input->getRightReleased())
\r
2721 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2725 g_input->resetLeftReleased();
\r
2726 g_input->resetRightReleased();
\r
2729 Calculate stuff for drawing
\r
2732 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2734 u32 daynight_ratio = client.getDayNightRatio();
\r
2735 /*video::SColor bgcolor = video::SColor(
\r
2737 skycolor.getRed() * daynight_ratio / 1000,
\r
2738 skycolor.getGreen() * daynight_ratio / 1000,
\r
2739 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2741 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2742 video::SColor bgcolor = video::SColor(
\r
2744 skycolor.getRed() * l / 255,
\r
2745 skycolor.getGreen() * l / 255,
\r
2746 skycolor.getBlue() * l / 255);
\r
2752 if(g_settings.getBool("enable_fog") == true)
\r
2754 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2755 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2756 if(draw_control.range_all)
\r
2757 range = 100000*BS;
\r
2761 video::EFT_FOG_LINEAR,
\r
2765 false, // pixel fog
\r
2766 false // range fog
\r
2772 Update gui stuff (0ms)
\r
2775 //TimeTaker guiupdatetimer("Gui updating");
\r
2778 wchar_t temptext[150];
\r
2780 static float drawtime_avg = 0;
\r
2781 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2782 static float beginscenetime_avg = 0;
\r
2783 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2784 static float scenetime_avg = 0;
\r
2785 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2786 static float endscenetime_avg = 0;
\r
2787 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2789 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2791 L", R: range_all=%i"
\r
2793 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2795 draw_control.range_all,
\r
2797 beginscenetime_avg,
\r
2802 guitext->setText(temptext);
\r
2806 wchar_t temptext[150];
\r
2807 swprintf(temptext, 150,
\r
2808 L"(% .1f, % .1f, % .1f)"
\r
2809 L" (% .3f < btime_jitter < % .3f"
\r
2810 L", dtime_jitter = % .1f %%"
\r
2811 L", v_range = %.1f)",
\r
2812 player_position.X/BS,
\r
2813 player_position.Y/BS,
\r
2814 player_position.Z/BS,
\r
2815 busytime_jitter1_min_sample,
\r
2816 busytime_jitter1_max_sample,
\r
2817 dtime_jitter1_max_fraction * 100.0,
\r
2818 draw_control.wanted_range
\r
2821 guitext2->setText(temptext);
\r
2825 guitext_info->setText(infotext.c_str());
\r
2829 Get chat messages from client
\r
2832 // Get new messages
\r
2833 std::wstring message;
\r
2834 while(client.getChatMessage(message))
\r
2836 chat_lines.push_back(ChatLine(message));
\r
2837 /*if(chat_lines.size() > 6)
\r
2839 core::list<ChatLine>::Iterator
\r
2840 i = chat_lines.begin();
\r
2841 chat_lines.erase(i);
\r
2844 // Append them to form the whole static text and throw
\r
2845 // it to the gui element
\r
2846 std::wstring whole;
\r
2847 // This will correspond to the line number counted from
\r
2848 // top to bottom, from size-1 to 0
\r
2849 s16 line_number = chat_lines.size();
\r
2850 // Count of messages to be removed from the top
\r
2851 u16 to_be_removed_count = 0;
\r
2852 for(core::list<ChatLine>::Iterator
\r
2853 i = chat_lines.begin();
\r
2854 i != chat_lines.end(); i++)
\r
2856 // After this, line number is valid for this loop
\r
2859 (*i).age += dtime;
\r
2861 This results in a maximum age of 60*6 to the
\r
2862 lowermost line and a maximum of 6 lines
\r
2864 float allowed_age = (6-line_number) * 60.0;
\r
2866 if((*i).age > allowed_age)
\r
2868 to_be_removed_count++;
\r
2871 whole += (*i).text + L'\n';
\r
2873 for(u16 i=0; i<to_be_removed_count; i++)
\r
2875 core::list<ChatLine>::Iterator
\r
2876 it = chat_lines.begin();
\r
2877 chat_lines.erase(it);
\r
2879 guitext_chat->setText(whole.c_str());
\r
2880 // Update gui element size and position
\r
2881 core::rect<s32> rect(
\r
2883 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2884 screensize.X - 10,
\r
2887 guitext_chat->setRelativePosition(rect);
\r
2889 if(chat_lines.size() == 0)
\r
2890 guitext_chat->setVisible(false);
\r
2892 guitext_chat->setVisible(true);
\r
2899 static u16 old_selected_item = 65535;
\r
2900 if(client.getLocalInventoryUpdated()
\r
2901 || g_selected_item != old_selected_item)
\r
2903 old_selected_item = g_selected_item;
\r
2904 //std::cout<<"Updating local inventory"<<std::endl;
\r
2905 client.getLocalInventory(local_inventory);
\r
2906 quick_inventory->setSelection(g_selected_item);
\r
2907 quick_inventory->update();
\r
2911 Send actions returned by the inventory menu
\r
2913 while(inventory_action_queue.size() != 0)
\r
2915 InventoryAction *a = inventory_action_queue.pop_front();
\r
2917 client.sendInventoryAction(a);
\r
2926 TimeTaker drawtimer("Drawing");
\r
2930 TimeTaker timer("beginScene");
\r
2931 driver->beginScene(true, true, bgcolor);
\r
2932 //driver->beginScene(false, true, bgcolor);
\r
2933 beginscenetime = timer.stop(true);
\r
2938 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2941 TimeTaker timer("smgr");
\r
2943 scenetime = timer.stop(true);
\r
2947 //TimeTaker timer9("auxiliary drawings");
\r
2951 //TimeTaker //timer10("//timer10");
\r
2953 video::SMaterial m;
\r
2954 //m.Thickness = 10;
\r
2956 m.Lighting = false;
\r
2957 driver->setMaterial(m);
\r
2959 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2961 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2962 i != hilightboxes.end(); i++)
\r
2964 /*std::cout<<"hilightbox min="
\r
2965 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2967 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2969 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2975 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2976 displaycenter + core::vector2d<s32>(10,0),
\r
2977 video::SColor(255,255,255,255));
\r
2978 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2979 displaycenter + core::vector2d<s32>(0,10),
\r
2980 video::SColor(255,255,255,255));
\r
2985 //TimeTaker //timer11("//timer11");
\r
2991 guienv->drawAll();
\r
2995 TimeTaker timer("endScene");
\r
2996 driver->endScene();
\r
2997 endscenetime = timer.stop(true);
\r
3000 drawtime = drawtimer.stop(true);
\r
3006 static s16 lastFPS = 0;
\r
3007 //u16 fps = driver->getFPS();
\r
3008 u16 fps = (1.0/dtime_avg1);
\r
3010 if (lastFPS != fps)
\r
3012 core::stringw str = L"Minetest [";
\r
3013 str += driver->getName();
\r
3017 device->setWindowCaption(str.c_str());
\r
3023 device->yield();*/
\r
3026 delete quick_inventory;
\r
3029 Disable texture fetches and other stuff that is queued
\r
3030 to be processed by the main loop.
\r
3032 This has to be done before client goes out of scope.
\r
3034 g_irrlicht->Shutdown(true);
\r
3036 } // client and server are deleted at this point
\r
3039 catch(con::PeerNotFoundException &e)
\r
3041 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3042 error_message = L"Connection timed out.";
\r
3045 } // Menu-game loop
\r
3050 In the end, delete the Irrlicht device.
\r
3055 Update configuration file
\r
3057 /*if(configpath != "")
\r
3059 g_settings.updateConfigFile(configpath.c_str());
\r
3062 END_DEBUG_EXCEPTION_HANDLER
\r
3064 debugstreams_deinit();
\r