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
103 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
105 - The player could go faster by a crafting a boat, or riding an animal
\r
107 - Random NPC traders. what else?
\r
112 Build system / running:
\r
113 -----------------------
\r
115 FIXME: Some network errors on Windows that cause local game to not work
\r
116 - See siggjen's emails.
\r
117 - Is this the famous "windows 7 problem"?
\r
118 - Apparently there might be other errors too
\r
119 - There is some problem with the menu system, something like the
\r
120 .Parent of guiPauseMenu to end up being 0xfeeefeee
\r
122 Networking and serialization:
\r
123 -----------------------------
\r
125 TODO: Get rid of GotSplitPacketException
\r
130 TODO: Add gui option to remove map
\r
132 TODO: Configuration menu, at least for keys
\r
137 TODO: Optimize day/night mesh updating somehow
\r
138 - create copies of all textures for all lighting values and only
\r
139 change texture for material?
\r
140 - Umm... the collecting of the faces is the slow part
\r
141 -> what about just changing the color values of the existing
\r
142 meshbuffers? It should go quite fast.
\r
143 - This is not easy; There'd need to be a buffer somewhere
\r
144 that would contain the night and day lighting values.
\r
145 - Actually if FastFaces would be stored, they could
\r
148 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
150 - That is >500 vertices
\r
151 - This is not easy; all the MapBlocks close to the player would
\r
152 still need to be drawn separately and combining the blocks
\r
153 would have to happen in a background thread
\r
155 TODO: Make fetching sector's blocks more efficient when rendering
\r
156 sectors that have very large amounts of blocks (on client)
\r
157 - Is this necessary at all?
\r
159 TODO: Flowing water animation
\r
161 NOTE(FIXED): A lock condition is possible:
\r
162 1) MapBlock::updateMesh() is called from client asynchronously:
\r
163 - AsyncProcessData() -> Map::updateMeshes()
\r
164 2) Asynchronous locks m_temp_mods_mutex
\r
165 3) MapBlock::updateMesh() is called from client synchronously:
\r
166 - Client::step() -> Environment::step()
\r
167 4) Synchronous starts waiting for m_temp_mods_mutex
\r
168 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
173 TODO: Make the video backend selectable
\r
178 TODO: Untie client network operations from framerate
\r
179 - Needs some input queues or something
\r
181 TODO: Make morning and evening transition more smooth and maybe shorter
\r
183 TODO: Don't update all meshes always on single node changes, but
\r
184 check which ones should be updated
\r
185 - implement Map::updateNodeMeshes()
\r
190 TODO: When player dies, throw items on map
\r
192 TODO: Make an option to the server to disable building and digging near
\r
193 the starting position
\r
195 TODO: Save players with inventories to disk
\r
196 TODO: Players to be saved as text in map/players/<name>
\r
198 TODO: Copy the text of the last picked sign to inventory in creative
\r
201 TODO: Check what goes wrong with caching map to disk (Kray)
\r
204 TODO: When server sees that client is removing an inexistent block to
\r
205 an existent position, resend the MapBlock.
\r
207 FIXME: Server went into some infinite PeerNotFoundException loop
\r
212 TODO: Better handling of objects and mobs
\r
214 - There has to be some way to do it with less messy code
\r
215 - Make separate classes for client and server
\r
216 - Client should not discriminate between blocks, server should
\r
217 - Make other players utilize the same framework
\r
218 - This is also needed for objects that don't get sent to client
\r
219 but are used for triggers etc
\r
221 TODO: There has to be some better way to handle static objects than to
\r
222 send them all the time. This affects signs and item objects.
\r
223 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
224 need an additional metadata field for the texts
\r
225 - This is also needed for item container chests
\r
227 Block object server side:
\r
228 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
229 - For all blocks in the buffer, objects are stepped(). This
\r
230 means they are active.
\r
231 - TODO: A global active buffer is needed for the server
\r
232 - TODO: A timestamp to blocks
\r
233 - TODO: All blocks going in and out of the buffer are recorded.
\r
234 - TODO: For outgoing blocks, timestamp is written.
\r
235 - TODO: For incoming blocks, time difference is calculated and
\r
236 objects are stepped according to it.
\r
241 TODO: Mineral and ground material properties
\r
242 - This way mineral ground toughness can be calculated with just
\r
243 some formula, as well as tool strengths
\r
245 TODO: Flowing water to actually contain flow direction information
\r
247 TODO: Remove duplicate lighting implementation from Map (leave
\r
248 VoxelManipulator, which is faster)
\r
250 FEATURE: Create a system that allows a huge amount of different "map
\r
251 generator modules/filters"
\r
253 FEATURE: Erosion simulation at map generation time
\r
254 - Simulate water flows, which would carve out dirt fast and
\r
255 then turn stone into gravel and sand and relocate it.
\r
256 - How about relocating minerals, too? Coal and gold in
\r
257 downstream sand and gravel would be kind of cool
\r
258 - This would need a better way of handling minerals, mainly
\r
259 to have mineral content as a separate field. the first
\r
260 parameter field is free for this.
\r
261 - Simulate rock falling from cliffs when water has removed
\r
262 enough solid rock from the bottom
\r
264 Doing now (most important at the top):
\r
265 --------------------------------------
\r
269 === Stuff to do before release
\r
270 * Save the new mapgen stuff
\r
271 - map/meta.txt, which should contain only plain text, something like this:
\r
272 seed = 7ff1bafcd7118800
\r
274 - map/chunks.dat: chunk positions and flags in binary format
\r
275 * Make server find the spawning place from the real map data, not from
\r
277 - But the changing borders of chunk have to be avoided, because
\r
278 there is time to generate only one chunk.
\r
279 * Make the generator to run in background and not blocking block
\r
280 placement and transfer
\r
281 * only_from_disk might not work anymore - check and fix it.
\r
282 * Check the fixmes in the list above
\r
284 === Stuff to do after release
\r
285 * Add some kind of erosion and other stuff that now is possible
\r
286 * Make client to fetch stuff asynchronously
\r
287 - Needs method SyncProcessData
\r
288 * Fix the problem with the server constantly saving one or a few
\r
289 blocks? List the first saved block, maybe it explains.
\r
290 - It is probably caused by oscillating water
\r
291 * Water doesn't start flowing after map generation like it should
\r
292 - Are there still problems?
\r
293 * Better water generation (spread it to underwater caverns but don't
\r
294 fill dungeons that don't touch big water masses)
\r
295 * When generating a chunk and the neighboring chunk doesn't have mud
\r
296 and stuff yet and the ground is fairly flat, the mud will flow to
\r
297 the other chunk making nasty straight walls when the other chunk
\r
298 is generated. Fix it.
\r
299 * Make a small history check to transformLiquids to detect and log
\r
300 continuous oscillations, in such detail that they can be fixed.
\r
302 ======================================================================
\r
307 Setting this to 1 enables a special camera mode that forces
\r
308 the renderers to think that the camera statically points from
\r
309 the starting place to a static direction.
\r
311 This allows one to move around with the player and see what
\r
312 is actually drawn behind solid things and behind the player.
\r
314 #define FIELD_OF_VIEW_TEST 0
\r
318 #pragma message ("Disabling unit tests")
\r
320 #warning "Disabling unit tests"
\r
322 // Disable unit tests
\r
323 #define ENABLE_TESTS 0
\r
325 // Enable unit tests
\r
326 #define ENABLE_TESTS 1
\r
330 #pragma comment(lib, "Irrlicht.lib")
\r
331 //#pragma comment(lib, "jthread.lib")
\r
332 #pragma comment(lib, "zlibwapi.lib")
\r
333 #pragma comment(lib, "Shell32.lib")
\r
334 // This would get rid of the console window
\r
335 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
338 #include <iostream>
\r
340 #include <jmutexautolock.h>
\r
341 #include <locale.h>
\r
342 #include "common_irrlicht.h"
\r
345 #include "player.h"
\r
348 #include "environment.h"
\r
349 #include "server.h"
\r
350 #include "client.h"
\r
351 #include "serialization.h"
\r
352 #include "constants.h"
\r
353 #include "strfnd.h"
\r
354 #include "porting.h"
\r
355 #include "irrlichtwrapper.h"
\r
356 #include "gettime.h"
\r
357 #include "porting.h"
\r
358 #include "guiPauseMenu.h"
\r
359 #include "guiInventoryMenu.h"
\r
360 #include "guiTextInputMenu.h"
\r
361 #include "materials.h"
\r
362 #include "guiMessageMenu.h"
\r
363 #include "filesys.h"
\r
364 #include "config.h"
\r
365 #include "guiMainMenu.h"
\r
366 #include "mineral.h"
\r
369 IrrlichtWrapper *g_irrlicht;
\r
371 MapDrawControl draw_control;
\r
375 These are loaded from the config file.
\r
378 Settings g_settings;
\r
380 extern void set_default_settings();
\r
386 IrrlichtDevice *g_device = NULL;
\r
387 Client *g_client = NULL;
\r
393 gui::IGUIEnvironment* guienv = NULL;
\r
394 gui::IGUIStaticText *guiroot = NULL;
\r
396 class MainMenuManager : public IMenuManager
\r
399 virtual void createdMenu(GUIModalMenu *menu)
\r
401 for(core::list<GUIModalMenu*>::Iterator
\r
402 i = m_stack.begin();
\r
403 i != m_stack.end(); i++)
\r
405 assert(*i != menu);
\r
408 if(m_stack.size() != 0)
\r
409 (*m_stack.getLast())->setVisible(false);
\r
410 m_stack.push_back(menu);
\r
413 virtual void deletingMenu(GUIModalMenu *menu)
\r
415 // Remove all entries if there are duplicates
\r
416 bool removed_entry;
\r
418 removed_entry = false;
\r
419 for(core::list<GUIModalMenu*>::Iterator
\r
420 i = m_stack.begin();
\r
421 i != m_stack.end(); i++)
\r
426 removed_entry = true;
\r
430 }while(removed_entry);
\r
432 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
433 assert(*i == menu);
\r
434 m_stack.erase(i);*/
\r
436 if(m_stack.size() != 0)
\r
437 (*m_stack.getLast())->setVisible(true);
\r
442 return m_stack.size();
\r
445 core::list<GUIModalMenu*> m_stack;
\r
448 MainMenuManager g_menumgr;
\r
450 bool noMenuActive()
\r
452 return (g_menumgr.menuCount() == 0);
\r
455 bool g_disconnect_requested = false;
\r
457 class MainGameCallback : public IGameCallback
\r
460 virtual void exitToOS()
\r
462 g_device->closeDevice();
\r
465 virtual void disconnect()
\r
467 g_disconnect_requested = true;
\r
471 MainGameCallback g_gamecallback;
\r
473 // Inventory actions from the menu are buffered here before sending
\r
474 Queue<InventoryAction*> inventory_action_queue;
\r
475 // This is a copy of the inventory that the client's environment has
\r
476 Inventory local_inventory;
\r
478 u16 g_selected_item = 0;
\r
485 std::ostream *dout_con_ptr = &dummyout;
\r
486 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
487 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
488 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
489 //std::ostream *dout_con_ptr = &dstream;
\r
490 //std::ostream *derr_con_ptr = &dstream;
\r
493 std::ostream *dout_server_ptr = &dstream;
\r
494 std::ostream *derr_server_ptr = &dstream;
\r
497 std::ostream *dout_client_ptr = &dstream;
\r
498 std::ostream *derr_client_ptr = &dstream;
\r
501 gettime.h implementation
\r
507 Use irrlicht because it is more precise than porting.h's
\r
510 if(g_irrlicht == NULL)
\r
512 return g_irrlicht->getTime();
\r
519 struct TextDestSign : public TextDest
\r
521 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
523 m_blockpos = blockpos;
\r
527 void gotText(std::wstring text)
\r
529 std::string ntext = wide_to_narrow(text);
\r
530 dstream<<"Changing text of a sign object: "
\r
531 <<ntext<<std::endl;
\r
532 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
540 struct TextDestChat : public TextDest
\r
542 TextDestChat(Client *client)
\r
546 void gotText(std::wstring text)
\r
548 m_client->sendChatMessage(text);
\r
549 m_client->addChatMessage(text);
\r
555 class MyEventReceiver : public IEventReceiver
\r
558 // This is the one method that we have to implement
\r
559 virtual bool OnEvent(const SEvent& event)
\r
562 React to nothing here if a menu is active
\r
564 if(noMenuActive() == false)
\r
570 // Remember whether each key is down or up
\r
571 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
573 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
575 if(event.KeyInput.PressedDown)
\r
577 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
583 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
585 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
587 dstream<<DTIME<<"MyEventReceiver: "
\r
588 <<"Launching pause menu"<<std::endl;
\r
589 // It will delete itself by itself
\r
590 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
591 &g_menumgr))->drop();
\r
594 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
596 dstream<<DTIME<<"MyEventReceiver: "
\r
597 <<"Launching inventory"<<std::endl;
\r
598 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
599 &local_inventory, &inventory_action_queue,
\r
600 &g_menumgr))->drop();
\r
603 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
605 TextDest *dest = new TextDestChat(g_client);
\r
607 (new GUITextInputMenu(guienv, guiroot, -1,
\r
613 // Material selection
\r
614 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
616 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
619 g_selected_item = 0;
\r
620 dstream<<DTIME<<"Selected item: "
\r
621 <<g_selected_item<<std::endl;
\r
624 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
625 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
627 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
628 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
630 if(s1 < PLAYER_INVENTORY_SIZE)
\r
631 g_selected_item = s1-1;
\r
632 dstream<<DTIME<<"Selected item: "
\r
633 <<g_selected_item<<std::endl;
\r
636 // Viewing range selection
\r
637 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
639 if(draw_control.range_all)
\r
641 draw_control.range_all = false;
\r
642 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
646 draw_control.range_all = true;
\r
647 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
651 // Print debug stacks
\r
652 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
654 dstream<<"-----------------------------------------"
\r
656 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
657 dstream<<"-----------------------------------------"
\r
659 debug_stacks_print();
\r
664 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
666 if(noMenuActive() == false)
\r
668 left_active = false;
\r
669 middle_active = false;
\r
670 right_active = false;
\r
674 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
675 left_active = event.MouseInput.isLeftPressed();
\r
676 middle_active = event.MouseInput.isMiddlePressed();
\r
677 right_active = event.MouseInput.isRightPressed();
\r
679 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
681 leftclicked = true;
\r
683 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
685 rightclicked = true;
\r
687 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
689 leftreleased = true;
\r
691 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
693 rightreleased = true;
\r
695 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
697 /*dstream<<"event.MouseInput.Wheel="
\r
698 <<event.MouseInput.Wheel<<std::endl;*/
\r
699 if(event.MouseInput.Wheel < 0)
\r
701 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
704 g_selected_item = 0;
\r
706 else if(event.MouseInput.Wheel > 0)
\r
708 if(g_selected_item > 0)
\r
711 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
720 // This is used to check whether a key is being held down
\r
721 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
723 return keyIsDown[keyCode];
\r
728 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
729 keyIsDown[i] = false;
\r
731 leftclicked = false;
\r
732 rightclicked = false;
\r
733 leftreleased = false;
\r
734 rightreleased = false;
\r
736 left_active = false;
\r
737 middle_active = false;
\r
738 right_active = false;
\r
749 bool rightreleased;
\r
752 bool middle_active;
\r
756 // We use this array to store the current state of each key
\r
757 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
760 IrrlichtDevice *m_device;
\r
769 virtual ~InputHandler()
\r
773 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
775 virtual v2s32 getMousePos() = 0;
\r
776 virtual void setMousePos(s32 x, s32 y) = 0;
\r
778 virtual bool getLeftState() = 0;
\r
779 virtual bool getRightState() = 0;
\r
781 virtual bool getLeftClicked() = 0;
\r
782 virtual bool getRightClicked() = 0;
\r
783 virtual void resetLeftClicked() = 0;
\r
784 virtual void resetRightClicked() = 0;
\r
786 virtual bool getLeftReleased() = 0;
\r
787 virtual bool getRightReleased() = 0;
\r
788 virtual void resetLeftReleased() = 0;
\r
789 virtual void resetRightReleased() = 0;
\r
791 virtual void step(float dtime) {};
\r
793 virtual void clear() {};
\r
796 InputHandler *g_input = NULL;
\r
798 class RealInputHandler : public InputHandler
\r
801 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
803 m_receiver(receiver)
\r
806 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
808 return m_receiver->IsKeyDown(keyCode);
\r
810 virtual v2s32 getMousePos()
\r
812 return m_device->getCursorControl()->getPosition();
\r
814 virtual void setMousePos(s32 x, s32 y)
\r
816 m_device->getCursorControl()->setPosition(x, y);
\r
819 virtual bool getLeftState()
\r
821 return m_receiver->left_active;
\r
823 virtual bool getRightState()
\r
825 return m_receiver->right_active;
\r
828 virtual bool getLeftClicked()
\r
830 return m_receiver->leftclicked;
\r
832 virtual bool getRightClicked()
\r
834 return m_receiver->rightclicked;
\r
836 virtual void resetLeftClicked()
\r
838 m_receiver->leftclicked = false;
\r
840 virtual void resetRightClicked()
\r
842 m_receiver->rightclicked = false;
\r
845 virtual bool getLeftReleased()
\r
847 return m_receiver->leftreleased;
\r
849 virtual bool getRightReleased()
\r
851 return m_receiver->rightreleased;
\r
853 virtual void resetLeftReleased()
\r
855 m_receiver->leftreleased = false;
\r
857 virtual void resetRightReleased()
\r
859 m_receiver->rightreleased = false;
\r
864 resetRightClicked();
\r
865 resetLeftClicked();
\r
868 IrrlichtDevice *m_device;
\r
869 MyEventReceiver *m_receiver;
\r
872 class RandomInputHandler : public InputHandler
\r
875 RandomInputHandler()
\r
877 leftclicked = false;
\r
878 rightclicked = false;
\r
879 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
880 keydown[i] = false;
\r
882 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
884 return keydown[keyCode];
\r
886 virtual v2s32 getMousePos()
\r
890 virtual void setMousePos(s32 x, s32 y)
\r
892 mousepos = v2s32(x,y);
\r
895 virtual bool getLeftState()
\r
899 virtual bool getRightState()
\r
904 virtual bool getLeftClicked()
\r
906 return leftclicked;
\r
908 virtual bool getRightClicked()
\r
910 return rightclicked;
\r
912 virtual void resetLeftClicked()
\r
914 leftclicked = false;
\r
916 virtual void resetRightClicked()
\r
918 rightclicked = false;
\r
921 virtual bool getLeftReleased()
\r
925 virtual bool getRightReleased()
\r
929 virtual void resetLeftReleased()
\r
932 virtual void resetRightReleased()
\r
936 virtual void step(float dtime)
\r
939 static float counter1 = 0;
\r
943 counter1 = 0.1*Rand(1,10);
\r
944 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
945 g_selected_material++;
\r
947 g_selected_material = 0;*/
\r
948 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
951 g_selected_item = 0;
\r
955 static float counter1 = 0;
\r
959 counter1 = 0.1*Rand(1, 40);
\r
960 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
964 static float counter1 = 0;
\r
968 counter1 = 0.1*Rand(1, 40);
\r
969 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
973 static float counter1 = 0;
\r
977 counter1 = 0.1*Rand(1, 40);
\r
978 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
982 static float counter1 = 0;
\r
986 counter1 = 0.1*Rand(1, 40);
\r
987 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
991 static float counter1 = 0;
\r
995 counter1 = 0.1*Rand(1, 20);
\r
996 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1000 static float counter1 = 0;
\r
1001 counter1 -= dtime;
\r
1002 if(counter1 < 0.0)
\r
1004 counter1 = 0.1*Rand(1, 30);
\r
1005 leftclicked = true;
\r
1009 static float counter1 = 0;
\r
1010 counter1 -= dtime;
\r
1011 if(counter1 < 0.0)
\r
1013 counter1 = 0.1*Rand(1, 20);
\r
1014 rightclicked = true;
\r
1017 mousepos += mousespeed;
\r
1020 s32 Rand(s32 min, s32 max)
\r
1022 return (myrand()%(max-min+1))+min;
\r
1025 bool keydown[KEY_KEY_CODES_COUNT];
\r
1029 bool rightclicked;
\r
1032 void updateViewingRange(f32 frametime_in, Client *client)
\r
1034 if(draw_control.range_all == true)
\r
1037 static f32 added_frametime = 0;
\r
1038 static s16 added_frames = 0;
\r
1040 added_frametime += frametime_in;
\r
1041 added_frames += 1;
\r
1043 // Actually this counter kind of sucks because frametime is busytime
\r
1044 static f32 counter = 0;
\r
1045 counter -= frametime_in;
\r
1051 /*dstream<<__FUNCTION_NAME
\r
1052 <<": Collected "<<added_frames<<" frames, total of "
\r
1053 <<added_frametime<<"s."<<std::endl;*/
\r
1055 /*dstream<<"draw_control.blocks_drawn="
\r
1056 <<draw_control.blocks_drawn
\r
1057 <<", draw_control.blocks_would_have_drawn="
\r
1058 <<draw_control.blocks_would_have_drawn
\r
1061 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1062 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1064 draw_control.wanted_min_range = range_min;
\r
1065 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1067 float block_draw_ratio = 1.0;
\r
1068 if(draw_control.blocks_would_have_drawn != 0)
\r
1070 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1071 / (float)draw_control.blocks_would_have_drawn;
\r
1074 // Calculate the average frametime in the case that all wanted
\r
1075 // blocks had been drawn
\r
1076 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1078 added_frametime = 0.0;
\r
1081 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1082 float wanted_frametime = 1.0 / wanted_fps;
\r
1084 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1085 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1087 // If needed frametime change is very small, just return
\r
1088 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1090 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1094 float range = draw_control.wanted_range;
\r
1095 float new_range = range;
\r
1097 static s16 range_old = 0;
\r
1098 static f32 frametime_old = 0;
\r
1100 float d_range = range - range_old;
\r
1101 f32 d_frametime = frametime - frametime_old;
\r
1102 // A sane default of 30ms per 50 nodes of range
\r
1103 static f32 time_per_range = 30. / 50;
\r
1106 time_per_range = d_frametime / d_range;
\r
1109 // The minimum allowed calculated frametime-range derivative:
\r
1110 // Practically this sets the maximum speed of changing the range.
\r
1111 // The lower this value, the higher the maximum changing speed.
\r
1112 // A low value here results in wobbly range (0.001)
\r
1113 // A high value here results in slow changing range (0.0025)
\r
1114 // SUGG: This could be dynamically adjusted so that when
\r
1115 // the camera is turning, this is lower
\r
1116 //float min_time_per_range = 0.0015;
\r
1117 float min_time_per_range = 0.0010;
\r
1118 //float min_time_per_range = 0.05 / range;
\r
1119 if(time_per_range < min_time_per_range)
\r
1121 time_per_range = min_time_per_range;
\r
1122 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1126 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1129 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1130 // Dampen the change a bit to kill oscillations
\r
1131 //wanted_range_change *= 0.9;
\r
1132 //wanted_range_change *= 0.75;
\r
1133 wanted_range_change *= 0.5;
\r
1134 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1136 // If needed range change is very small, just return
\r
1137 if(fabs(wanted_range_change) < 0.001)
\r
1139 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1143 new_range += wanted_range_change;
\r
1144 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1146 //float new_range_unclamped = new_range;
\r
1147 if(new_range < range_min)
\r
1148 new_range = range_min;
\r
1149 if(new_range > range_max)
\r
1150 new_range = range_max;
\r
1152 /*if(new_range != new_range_unclamped)
\r
1153 dstream<<", clamped to "<<new_range<<std::endl;
\r
1155 dstream<<std::endl;*/
\r
1157 draw_control.wanted_range = new_range;
\r
1159 range_old = new_range;
\r
1160 frametime_old = frametime;
\r
1163 class GUIQuickInventory : public IEventReceiver
\r
1166 GUIQuickInventory(
\r
1167 gui::IGUIEnvironment* env,
\r
1168 gui::IGUIElement* parent,
\r
1171 Inventory *inventory):
\r
1172 m_itemcount(itemcount),
\r
1173 m_inventory(inventory)
\r
1175 core::rect<s32> imgsize(0,0,48,48);
\r
1176 core::rect<s32> textsize(0,0,48,16);
\r
1177 v2s32 spacing(0, 64);
\r
1178 for(s32 i=0; i<m_itemcount; i++)
\r
1180 m_images.push_back(env->addImage(
\r
1181 imgsize + pos + spacing*i
\r
1183 m_images[i]->setScaleImage(true);
\r
1184 m_texts.push_back(env->addStaticText(
\r
1186 textsize + pos + spacing*i,
\r
1189 m_texts[i]->setBackgroundColor(
\r
1190 video::SColor(128,0,0,0));
\r
1191 m_texts[i]->setTextAlignment(
\r
1192 gui::EGUIA_CENTER,
\r
1193 gui::EGUIA_UPPERLEFT);
\r
1197 ~GUIQuickInventory()
\r
1199 for(u32 i=0; i<m_texts.size(); i++)
\r
1201 m_texts[i]->remove();
\r
1203 for(u32 i=0; i<m_images.size(); i++)
\r
1205 m_images[i]->remove();
\r
1209 virtual bool OnEvent(const SEvent& event)
\r
1214 void setSelection(s32 i)
\r
1223 start = m_selection - m_itemcount / 2;
\r
1225 InventoryList *mainlist = m_inventory->getList("main");
\r
1227 for(s32 i=0; i<m_itemcount; i++)
\r
1229 s32 j = i + start;
\r
1231 if(j > (s32)mainlist->getSize() - 1)
\r
1232 j -= mainlist->getSize();
\r
1234 j += mainlist->getSize();
\r
1236 InventoryItem *item = mainlist->getItem(j);
\r
1240 m_images[i]->setImage(NULL);
\r
1243 if(m_selection == j)
\r
1244 swprintf(t, 10, L"<-");
\r
1246 swprintf(t, 10, L"");
\r
1247 m_texts[i]->setText(t);
\r
1249 // The next ifs will segfault with a NULL pointer
\r
1254 m_images[i]->setImage(item->getImage());
\r
1257 if(m_selection == j)
\r
1258 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1260 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1261 m_texts[i]->setText(t);
\r
1267 core::array<gui::IGUIStaticText*> m_texts;
\r
1268 core::array<gui::IGUIImage*> m_images;
\r
1269 Inventory *m_inventory;
\r
1280 ChatLine(const std::wstring &a_text):
\r
1286 std::wstring text;
\r
1289 // These are defined global so that they're not optimized too much.
\r
1290 // Can't change them to volatile.
\r
1295 std::string tempstring;
\r
1296 std::string tempstring2;
\r
1301 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1302 TimeTaker timer("Testing std::string speed");
\r
1303 const u32 jj = 10000;
\r
1304 for(u32 j=0; j<jj; j++)
\r
1308 const u32 ii = 10;
\r
1309 for(u32 i=0; i<ii; i++){
\r
1310 tempstring2 += "asd";
\r
1312 for(u32 i=0; i<ii+1; i++){
\r
1313 tempstring += "asd";
\r
1314 if(tempstring == tempstring2)
\r
1320 dstream<<"All of the following tests should take around 100ms each."
\r
1324 TimeTaker timer("Testing floating-point conversion speed");
\r
1326 for(u32 i=0; i<4000000; i++){
\r
1333 TimeTaker timer("Testing floating-point vector speed");
\r
1335 tempv3f1 = v3f(1,2,3);
\r
1336 tempv3f2 = v3f(4,5,6);
\r
1337 for(u32 i=0; i<10000000; i++){
\r
1338 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1339 tempv3f2 += v3f(7,8,9);
\r
1344 TimeTaker timer("Testing core::map speed");
\r
1346 core::map<v2s16, f32> map1;
\r
1349 for(s16 y=0; y<ii; y++){
\r
1350 for(s16 x=0; x<ii; x++){
\r
1351 map1.insert(v2s16(x,y), tempf);
\r
1355 for(s16 y=ii-1; y>=0; y--){
\r
1356 for(s16 x=0; x<ii; x++){
\r
1357 tempf = map1[v2s16(x,y)];
\r
1363 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1364 TimeTaker timer("Testing mutex speed");
\r
1377 // Do at least 10ms
\r
1378 while(timer.getTime() < 10);
\r
1380 u32 dtime = timer.stop();
\r
1381 u32 per_ms = n / dtime;
\r
1382 std::cout<<"Done. "<<dtime<<"ms, "
\r
1383 <<per_ms<<"/ms"<<std::endl;
\r
1387 int main(int argc, char *argv[])
\r
1390 Parse command line
\r
1393 // List all allowed options
\r
1394 core::map<std::string, ValueSpec> allowed_options;
\r
1395 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1396 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1397 "Run server directly"));
\r
1398 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1399 "Load configuration from specified file"));
\r
1400 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1401 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1402 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1403 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1404 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1405 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1407 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1409 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1411 Settings cmd_args;
\r
1413 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1415 if(ret == false || cmd_args.getFlag("help"))
\r
1417 dstream<<"Allowed options:"<<std::endl;
\r
1418 for(core::map<std::string, ValueSpec>::Iterator
\r
1419 i = allowed_options.getIterator();
\r
1420 i.atEnd() == false; i++)
\r
1422 dstream<<" --"<<i.getNode()->getKey();
\r
1423 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1428 dstream<<" <value>";
\r
1430 dstream<<std::endl;
\r
1432 if(i.getNode()->getValue().help != NULL)
\r
1434 dstream<<" "<<i.getNode()->getValue().help
\r
1439 return cmd_args.getFlag("help") ? 0 : 1;
\r
1443 Low-level initialization
\r
1446 bool disable_stderr = false;
\r
1448 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1449 disable_stderr = true;
\r
1452 // Initialize debug streams
\r
1453 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1454 // Initialize debug stacks
\r
1455 debug_stacks_init();
\r
1457 DSTACK(__FUNCTION_NAME);
\r
1459 porting::initializePaths();
\r
1460 // Create user data directory
\r
1461 fs::CreateDir(porting::path_userdata);
\r
1463 // C-style stuff initialization
\r
1464 initializeMaterialProperties();
\r
1467 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1469 // Print startup message
\r
1470 dstream<<DTIME<<"minetest-c55"
\r
1471 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1472 <<", "<<BUILD_INFO
\r
1476 Basic initialization
\r
1479 // Initialize default settings
\r
1480 set_default_settings();
\r
1482 // Set locale. This is for forcing '.' as the decimal point.
\r
1483 std::locale::global(std::locale("C"));
\r
1484 // This enables printing all characters in bitmap font
\r
1485 setlocale(LC_CTYPE, "en_US");
\r
1487 // Initialize sockets
\r
1489 atexit(sockets_cleanup);
\r
1499 // Path of configuration file in use
\r
1500 std::string configpath = "";
\r
1502 if(cmd_args.exists("config"))
\r
1504 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1507 dstream<<"Could not read configuration from \""
\r
1508 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1511 configpath = cmd_args.get("config");
\r
1515 core::array<std::string> filenames;
\r
1516 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1517 #ifdef RUN_IN_PLACE
\r
1518 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1521 for(u32 i=0; i<filenames.size(); i++)
\r
1523 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1526 configpath = filenames[i];
\r
1532 // Initialize random seed
\r
1537 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1539 These are needed for unit tests at least.
\r
1542 IIrrlichtWrapper irrlicht_dummy;
\r
1544 init_mapnode(&irrlicht_dummy);
\r
1550 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1551 || cmd_args.getFlag("enable-unittests") == true)
\r
1556 /*for(s16 y=-100; y<100; y++)
\r
1557 for(s16 x=-100; x<100; x++)
\r
1559 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1569 if(cmd_args.exists("port"))
\r
1570 port = cmd_args.getU16("port");
\r
1571 else if(cmd_args.exists("port"))
\r
1572 port = g_settings.getU16("port");
\r
1575 std::string map_dir = porting::path_userdata+"/map";
\r
1576 if(cmd_args.exists("map-dir"))
\r
1577 map_dir = cmd_args.get("map-dir");
\r
1578 else if(g_settings.exists("map-dir"))
\r
1579 map_dir = g_settings.get("map-dir");
\r
1581 // Run dedicated server if asked to
\r
1582 if(cmd_args.getFlag("server"))
\r
1584 DSTACK("Dedicated server branch");
\r
1587 Server server(map_dir.c_str());
\r
1588 server.start(port);
\r
1591 dedicated_server_loop(server);
\r
1600 // Address to connect to
\r
1601 std::string address = "";
\r
1603 if(cmd_args.exists("address"))
\r
1605 address = cmd_args.get("address");
\r
1609 address = g_settings.get("address");
\r
1612 std::string playername = g_settings.get("name");
\r
1615 Resolution selection
\r
1618 bool fullscreen = false;
\r
1619 u16 screenW = g_settings.getU16("screenW");
\r
1620 u16 screenH = g_settings.getU16("screenH");
\r
1624 MyEventReceiver receiver;
\r
1626 video::E_DRIVER_TYPE driverType;
\r
1629 //driverType = video::EDT_DIRECT3D9;
\r
1630 driverType = video::EDT_OPENGL;
\r
1632 driverType = video::EDT_OPENGL;
\r
1633 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1636 // create device and exit if creation failed
\r
1638 IrrlichtDevice *device;
\r
1639 device = createDevice(driverType,
\r
1640 core::dimension2d<u32>(screenW, screenH),
\r
1641 16, fullscreen, false, false, &receiver);
\r
1644 return 1; // could not create selected driver.
\r
1646 g_device = device;
\r
1647 g_irrlicht = new IrrlichtWrapper(device);
\r
1650 Speed tests (done after irrlicht is loaded to get timer)
\r
1652 if(cmd_args.getFlag("speedtests"))
\r
1654 dstream<<"Running speed tests"<<std::endl;
\r
1659 device->setResizable(true);
\r
1661 bool random_input = g_settings.getBool("random_input")
\r
1662 || cmd_args.getFlag("random-input");
\r
1664 g_input = new RandomInputHandler();
\r
1666 g_input = new RealInputHandler(device, &receiver);
\r
1669 Continue initialization
\r
1672 video::IVideoDriver* driver = device->getVideoDriver();
\r
1675 This changes the minimum allowed number of vertices in a VBO
\r
1677 //driver->setMinHardwareBufferVertexCount(50);
\r
1679 scene::ISceneManager* smgr = device->getSceneManager();
\r
1681 guienv = device->getGUIEnvironment();
\r
1682 gui::IGUISkin* skin = guienv->getSkin();
\r
1683 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1685 skin->setFont(font);
\r
1687 dstream<<"WARNING: Font file was not found."
\r
1688 " Using default font."<<std::endl;
\r
1689 // If font was not found, this will get us one
\r
1690 font = skin->getFont();
\r
1693 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1694 dstream<<"text_height="<<text_height<<std::endl;
\r
1696 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1697 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1698 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1699 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1700 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1701 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1704 Preload some textures and stuff
\r
1707 init_content_inventory_texture_paths();
\r
1708 init_mapnode(g_irrlicht);
\r
1709 init_mineral(g_irrlicht);
\r
1716 We need some kind of a root node to be able to add
\r
1717 custom gui elements directly on the screen.
\r
1718 Otherwise they won't be automatically drawn.
\r
1720 guiroot = guienv->addStaticText(L"",
\r
1721 core::rect<s32>(0, 0, 10000, 10000));
\r
1723 // First line of debug text
\r
1724 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1726 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1728 // Second line of debug text
\r
1729 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1731 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1734 // At the middle of the screen
\r
1735 // Object infos are shown in this
\r
1736 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1738 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1742 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1744 core::rect<s32>(0,0,0,0),
\r
1746 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1747 core::list<ChatLine> chat_lines;
\r
1750 If an error occurs, this is set to something and the
\r
1751 menu-game loop is restarted. It is then displayed before
\r
1754 std::wstring error_message = L"";
\r
1759 while(g_device->run())
\r
1762 // This is used for catching disconnects
\r
1767 Out-of-game menu loop.
\r
1769 Loop quits when menu returns proper parameters.
\r
1773 // Cursor can be non-visible when coming from the game
\r
1774 device->getCursorControl()->setVisible(true);
\r
1775 // Some stuff are left to scene manager when coming from the game
\r
1776 // (map at least?)
\r
1778 // Reset or hide the debug gui texts
\r
1779 guitext->setText(L"Minetest-c55");
\r
1780 guitext2->setVisible(false);
\r
1781 guitext_info->setVisible(false);
\r
1782 guitext_chat->setVisible(false);
\r
1784 // Initialize menu data
\r
1785 MainMenuData menudata;
\r
1786 menudata.address = narrow_to_wide(address);
\r
1787 menudata.name = narrow_to_wide(playername);
\r
1788 menudata.port = narrow_to_wide(itos(port));
\r
1789 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1791 GUIMainMenu *menu =
\r
1792 new GUIMainMenu(guienv, guiroot, -1,
\r
1793 &g_menumgr, &menudata, &g_gamecallback);
\r
1794 menu->allowFocusRemoval(true);
\r
1796 if(error_message != L"")
\r
1798 GUIMessageMenu *menu2 =
\r
1799 new GUIMessageMenu(guienv, guiroot, -1,
\r
1800 &g_menumgr, error_message.c_str());
\r
1802 error_message = L"";
\r
1805 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1807 dstream<<"Created main menu"<<std::endl;
\r
1809 while(g_device->run())
\r
1811 // Run global IrrlichtWrapper's main thread processing stuff
\r
1812 g_irrlicht->Run();
\r
1814 if(menu->getStatus() == true)
\r
1817 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1818 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1819 guienv->drawAll();
\r
1820 driver->endScene();
\r
1823 // Break out of menu-game loop to shut down cleanly
\r
1824 if(g_device->run() == false)
\r
1827 dstream<<"Dropping main menu"<<std::endl;
\r
1831 // Delete map if requested
\r
1832 if(menudata.delete_map)
\r
1834 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1836 error_message = L"Delete failed";
\r
1840 playername = wide_to_narrow(menudata.name);
\r
1841 address = wide_to_narrow(menudata.address);
\r
1842 port = stoi(wide_to_narrow(menudata.port));
\r
1843 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1845 // Check for valid parameters, restart menu if invalid.
\r
1846 if(playername == "")
\r
1848 error_message = L"Name required.";
\r
1853 g_settings.set("name", playername);
\r
1854 g_settings.set("address", address);
\r
1855 g_settings.set("port", itos(port));
\r
1856 // Update configuration file
\r
1857 if(configpath != "")
\r
1858 g_settings.updateConfigFile(configpath.c_str());
\r
1860 // Continue to game
\r
1864 // Break out of menu-game loop to shut down cleanly
\r
1865 if(g_device->run() == false)
\r
1869 Make a scope here so that the client and the server and other
\r
1870 stuff gets removed when disconnected or the irrlicht device
\r
1875 // This is set to true at the end of the scope
\r
1876 g_irrlicht->Shutdown(false);
\r
1879 Draw "Loading" screen
\r
1881 const wchar_t *text = L"Loading and connecting...";
\r
1882 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1883 core::vector2d<s32> textsize(300, text_height);
\r
1884 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1886 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1887 text, textrect, false, false);
\r
1888 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1890 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1891 guienv->drawAll();
\r
1892 driver->endScene();
\r
1894 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1898 SharedPtr will delete it when it goes out of scope.
\r
1900 SharedPtr<Server> server;
\r
1901 if(address == ""){
\r
1902 server = new Server(map_dir);
\r
1903 server->start(port);
\r
1910 Client client(device, playername.c_str(), draw_control);
\r
1912 g_client = &client;
\r
1914 Address connect_address(0,0,0,0, port);
\r
1917 connect_address.Resolve("localhost");
\r
1919 connect_address.Resolve(address.c_str());
\r
1921 catch(ResolveError &e)
\r
1923 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1925 error_message = L"Couldn't resolve address";
\r
1926 gui_loadingtext->remove();
\r
1930 dstream<<DTIME<<"Connecting to server at ";
\r
1931 connect_address.print(&dstream);
\r
1932 dstream<<std::endl;
\r
1933 client.connect(connect_address);
\r
1936 while(client.connectedAndInitialized() == false)
\r
1939 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1940 guienv->drawAll();
\r
1941 driver->endScene();
\r
1943 // Update client and server
\r
1947 if(server != NULL)
\r
1948 server->step(0.1);
\r
1954 catch(con::PeerNotFoundException &e)
\r
1956 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1958 error_message = L"Connection timed out.";
\r
1959 gui_loadingtext->remove();
\r
1966 /*scene::ISceneNode* skybox;
\r
1967 skybox = smgr->addSkyBoxSceneNode(
\r
1968 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1969 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1970 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1971 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1972 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1973 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1976 Create the camera node
\r
1979 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1980 0, // Camera parent
\r
1981 v3f(BS*100, BS*2, BS*100), // Look from
\r
1982 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1986 if(camera == NULL)
\r
1989 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1991 camera->setFOV(FOV_ANGLE);
\r
1993 // Just so big a value that everything rendered is visible
\r
1994 camera->setFarValue(100000*BS);
\r
1996 f32 camera_yaw = 0; // "right/left"
\r
1997 f32 camera_pitch = 0; // "up/down"
\r
2003 gui_loadingtext->remove();
\r
2006 Add some gui stuff
\r
2009 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2010 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2012 // Test the text input system
\r
2013 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2015 /*GUIMessageMenu *menu =
\r
2016 new GUIMessageMenu(guienv, guiroot, -1,
\r
2021 // Launch pause menu
\r
2022 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2023 &g_menumgr))->drop();
\r
2026 guitext2->setVisible(true);
\r
2027 guitext_info->setVisible(true);
\r
2028 guitext_chat->setVisible(true);
\r
2031 Some statistics are collected in these
\r
2034 u32 beginscenetime = 0;
\r
2035 u32 scenetime = 0;
\r
2036 u32 endscenetime = 0;
\r
2039 //throw con::PeerNotFoundException("lol");
\r
2045 bool first_loop_after_window_activation = true;
\r
2047 // Time is in milliseconds
\r
2048 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2049 // NOTE: So we have to use getTime() and call run()s between them
\r
2050 u32 lasttime = device->getTimer()->getTime();
\r
2052 while(device->run())
\r
2054 if(g_disconnect_requested)
\r
2056 g_disconnect_requested = false;
\r
2061 Run global IrrlichtWrapper's main thread processing stuff
\r
2063 g_irrlicht->Run();
\r
2066 Random calculations
\r
2068 v2u32 screensize = driver->getScreenSize();
\r
2069 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2071 // Hilight boxes collected during the loop and displayed
\r
2072 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2075 std::wstring infotext;
\r
2077 //TimeTaker //timer1("//timer1");
\r
2079 // Time of frame without fps limit
\r
2083 // not using getRealTime is necessary for wine
\r
2084 u32 time = device->getTimer()->getTime();
\r
2085 if(time > lasttime)
\r
2086 busytime_u32 = time - lasttime;
\r
2089 busytime = busytime_u32 / 1000.0;
\r
2092 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2094 // Absolutelu necessary for wine!
\r
2101 updateViewingRange(busytime, &client);
\r
2108 float fps_max = g_settings.getFloat("fps_max");
\r
2109 u32 frametime_min = 1000./fps_max;
\r
2111 if(busytime_u32 < frametime_min)
\r
2113 u32 sleeptime = frametime_min - busytime_u32;
\r
2114 device->sleep(sleeptime);
\r
2118 // Absolutelu necessary for wine!
\r
2122 Time difference calculation
\r
2124 f32 dtime; // in seconds
\r
2126 u32 time = device->getTimer()->getTime();
\r
2127 if(time > lasttime)
\r
2128 dtime = (time - lasttime) / 1000.0;
\r
2134 Time average and jitter calculation
\r
2137 static f32 dtime_avg1 = 0.0;
\r
2138 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2139 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2141 static f32 dtime_jitter1_max_sample = 0.0;
\r
2142 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2144 static f32 jitter1_max = 0.0;
\r
2145 static f32 counter = 0.0;
\r
2146 if(dtime_jitter1 > jitter1_max)
\r
2147 jitter1_max = dtime_jitter1;
\r
2152 dtime_jitter1_max_sample = jitter1_max;
\r
2153 dtime_jitter1_max_fraction
\r
2154 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2155 jitter1_max = 0.0;
\r
2160 Busytime average and jitter calculation
\r
2163 static f32 busytime_avg1 = 0.0;
\r
2164 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2165 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2167 static f32 busytime_jitter1_max_sample = 0.0;
\r
2168 static f32 busytime_jitter1_min_sample = 0.0;
\r
2170 static f32 jitter1_max = 0.0;
\r
2171 static f32 jitter1_min = 0.0;
\r
2172 static f32 counter = 0.0;
\r
2173 if(busytime_jitter1 > jitter1_max)
\r
2174 jitter1_max = busytime_jitter1;
\r
2175 if(busytime_jitter1 < jitter1_min)
\r
2176 jitter1_min = busytime_jitter1;
\r
2178 if(counter > 0.0){
\r
2180 busytime_jitter1_max_sample = jitter1_max;
\r
2181 busytime_jitter1_min_sample = jitter1_min;
\r
2182 jitter1_max = 0.0;
\r
2183 jitter1_min = 0.0;
\r
2188 Debug info for client
\r
2191 static float counter = 0.0;
\r
2196 client.printDebugInfo(std::cout);
\r
2201 Input handler step()
\r
2203 g_input->step(dtime);
\r
2206 Player speed control
\r
2215 bool a_superspeed,
\r
2218 PlayerControl control(
\r
2219 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2220 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2221 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2222 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2223 g_input->isKeyDown(irr::KEY_SPACE),
\r
2224 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2228 client.setPlayerControl(control);
\r
2232 Process environment
\r
2236 //TimeTaker timer("client.step(dtime)");
\r
2237 client.step(dtime);
\r
2238 //client.step(dtime_avg1);
\r
2241 if(server != NULL)
\r
2243 //TimeTaker timer("server->step(dtime)");
\r
2244 server->step(dtime);
\r
2247 v3f player_position = client.getPlayerPosition();
\r
2249 //TimeTaker //timer2("//timer2");
\r
2252 Mouse and camera control
\r
2255 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2258 device->getCursorControl()->setVisible(false);
\r
2260 if(first_loop_after_window_activation){
\r
2261 //std::cout<<"window active, first loop"<<std::endl;
\r
2262 first_loop_after_window_activation = false;
\r
2265 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2266 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2267 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2268 camera_yaw -= dx*0.2;
\r
2269 camera_pitch += dy*0.2;
\r
2270 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2271 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2273 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2276 device->getCursorControl()->setVisible(true);
\r
2278 //std::cout<<"window inactive"<<std::endl;
\r
2279 first_loop_after_window_activation = true;
\r
2282 camera_yaw = wrapDegrees(camera_yaw);
\r
2283 camera_pitch = wrapDegrees(camera_pitch);
\r
2285 v3f camera_direction = v3f(0,0,1);
\r
2286 camera_direction.rotateYZBy(camera_pitch);
\r
2287 camera_direction.rotateXZBy(camera_yaw);
\r
2289 // This is at the height of the eyes of the current figure
\r
2290 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2291 // This is more like in minecraft
\r
2292 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2294 camera->setPosition(camera_position);
\r
2295 // *100.0 helps in large map coordinates
\r
2296 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2298 if(FIELD_OF_VIEW_TEST){
\r
2299 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2300 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2303 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2304 //TimeTaker timer("client.updateCamera");
\r
2305 client.updateCamera(camera_position, camera_direction);
\r
2309 //TimeTaker //timer3("//timer3");
\r
2312 Calculate what block is the crosshair pointing to
\r
2315 //u32 t1 = device->getTimer()->getRealTime();
\r
2317 //f32 d = 4; // max. distance
\r
2318 f32 d = 4; // max. distance
\r
2319 core::line3d<f32> shootline(camera_position,
\r
2320 camera_position + camera_direction * BS * (d+1));
\r
2322 MapBlockObject *selected_object = client.getSelectedObject
\r
2323 (d*BS, camera_position, shootline);
\r
2326 If it's pointing to a MapBlockObject
\r
2329 if(selected_object != NULL)
\r
2331 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2333 core::aabbox3d<f32> box_on_map
\r
2334 = selected_object->getSelectionBoxOnMap();
\r
2336 hilightboxes.push_back(box_on_map);
\r
2338 infotext = narrow_to_wide(selected_object->infoText());
\r
2340 if(g_input->getLeftClicked())
\r
2342 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2343 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2344 selected_object->getId(), g_selected_item);
\r
2346 else if(g_input->getRightClicked())
\r
2348 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2350 Check if we want to modify the object ourselves
\r
2352 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2354 dstream<<"Sign object right-clicked"<<std::endl;
\r
2356 if(random_input == false)
\r
2358 // Get a new text for it
\r
2360 TextDest *dest = new TextDestSign(
\r
2361 selected_object->getBlock()->getPos(),
\r
2362 selected_object->getId(),
\r
2365 SignObject *sign_object = (SignObject*)selected_object;
\r
2367 std::wstring wtext =
\r
2368 narrow_to_wide(sign_object->getText());
\r
2370 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2376 Otherwise pass the event to the server as-is
\r
2380 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2381 selected_object->getId(), g_selected_item);
\r
2385 else // selected_object == NULL
\r
2389 Find out which node we are pointing at
\r
2392 bool nodefound = false;
\r
2394 v3s16 neighbourpos;
\r
2395 core::aabbox3d<f32> nodehilightbox;
\r
2396 f32 mindistance = BS * 1001;
\r
2398 v3s16 pos_i = floatToInt(player_position);
\r
2400 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2404 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2405 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2406 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2407 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2408 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2409 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2411 for(s16 y = ystart; y <= yend; y++)
\r
2412 for(s16 z = zstart; z <= zend; z++)
\r
2413 for(s16 x = xstart; x <= xend; x++)
\r
2418 n = client.getNode(v3s16(x,y,z));
\r
2419 if(content_pointable(n.d) == false)
\r
2422 catch(InvalidPositionException &e)
\r
2428 v3f npf = intToFloat(np);
\r
2433 v3s16(0,0,1), // back
\r
2434 v3s16(0,1,0), // top
\r
2435 v3s16(1,0,0), // right
\r
2436 v3s16(0,0,-1), // front
\r
2437 v3s16(0,-1,0), // bottom
\r
2438 v3s16(-1,0,0), // left
\r
2444 if(n.d == CONTENT_TORCH)
\r
2446 v3s16 dir = unpackDir(n.dir);
\r
2447 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2448 dir_f *= BS/2 - BS/6 - BS/20;
\r
2449 v3f cpf = npf + dir_f;
\r
2450 f32 distance = (cpf - camera_position).getLength();
\r
2452 core::aabbox3d<f32> box;
\r
2455 if(dir == v3s16(0,-1,0))
\r
2457 box = core::aabbox3d<f32>(
\r
2458 npf - v3f(BS/6, BS/2, BS/6),
\r
2459 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2463 else if(dir == v3s16(0,1,0))
\r
2465 box = core::aabbox3d<f32>(
\r
2466 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2467 npf + v3f(BS/6, BS/2, BS/6)
\r
2473 box = core::aabbox3d<f32>(
\r
2474 cpf - v3f(BS/6, BS/3, BS/6),
\r
2475 cpf + v3f(BS/6, BS/3, BS/6)
\r
2479 if(distance < mindistance)
\r
2481 if(box.intersectsWithLine(shootline))
\r
2485 neighbourpos = np;
\r
2486 mindistance = distance;
\r
2487 nodehilightbox = box;
\r
2496 for(u16 i=0; i<6; i++)
\r
2498 v3f dir_f = v3f(dirs[i].X,
\r
2499 dirs[i].Y, dirs[i].Z);
\r
2500 v3f centerpoint = npf + dir_f * BS/2;
\r
2502 (centerpoint - camera_position).getLength();
\r
2504 if(distance < mindistance)
\r
2506 core::CMatrix4<f32> m;
\r
2507 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2509 // This is the back face
\r
2510 v3f corners[2] = {
\r
2511 v3f(BS/2, BS/2, BS/2),
\r
2512 v3f(-BS/2, -BS/2, BS/2+d)
\r
2515 for(u16 j=0; j<2; j++)
\r
2517 m.rotateVect(corners[j]);
\r
2518 corners[j] += npf;
\r
2521 core::aabbox3d<f32> facebox(corners[0]);
\r
2522 facebox.addInternalPoint(corners[1]);
\r
2524 if(facebox.intersectsWithLine(shootline))
\r
2528 neighbourpos = np + dirs[i];
\r
2529 mindistance = distance;
\r
2531 //nodehilightbox = facebox;
\r
2533 const float d = 0.502;
\r
2534 core::aabbox3d<f32> nodebox
\r
2535 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2536 v3f nodepos_f = intToFloat(nodepos);
\r
2537 nodebox.MinEdge += nodepos_f;
\r
2538 nodebox.MaxEdge += nodepos_f;
\r
2539 nodehilightbox = nodebox;
\r
2541 } // if distance < mindistance
\r
2543 } // regular block
\r
2546 static float nodig_delay_counter = 0.0;
\r
2550 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2552 static float dig_time = 0.0;
\r
2553 static u16 dig_index = 0;
\r
2555 // Visualize selection
\r
2557 hilightboxes.push_back(nodehilightbox);
\r
2561 if(g_input->getLeftReleased())
\r
2563 client.clearTempMod(nodepos);
\r
2567 if(nodig_delay_counter > 0.0)
\r
2569 nodig_delay_counter -= dtime;
\r
2573 if(nodepos != nodepos_old)
\r
2575 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2576 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2578 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2580 client.clearTempMod(nodepos_old);
\r
2585 if(g_input->getLeftClicked() ||
\r
2586 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2588 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2589 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2591 if(g_input->getLeftClicked())
\r
2593 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2595 if(g_input->getLeftState())
\r
2597 MapNode n = client.getNode(nodepos);
\r
2599 // Get tool name. Default is "" = bare hands
\r
2600 std::string toolname = "";
\r
2601 InventoryList *mlist = local_inventory.getList("main");
\r
2604 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2605 if(item && (std::string)item->getName() == "ToolItem")
\r
2607 ToolItem *titem = (ToolItem*)item;
\r
2608 toolname = titem->getToolName();
\r
2612 // Get digging properties for material and tool
\r
2613 u8 material = n.d;
\r
2614 DiggingProperties prop =
\r
2615 getDiggingProperties(material, toolname);
\r
2617 float dig_time_complete = 0.0;
\r
2619 if(prop.diggable == false)
\r
2621 /*dstream<<"Material "<<(int)material
\r
2622 <<" not diggable with \""
\r
2623 <<toolname<<"\""<<std::endl;*/
\r
2624 // I guess nobody will wait for this long
\r
2625 dig_time_complete = 10000000.0;
\r
2629 dig_time_complete = prop.time;
\r
2632 if(dig_time_complete >= 0.001)
\r
2634 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2635 * dig_time/dig_time_complete);
\r
2637 // This is for torches
\r
2640 dig_index = CRACK_ANIMATION_LENGTH;
\r
2643 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2645 //TimeTaker timer("client.setTempMod");
\r
2646 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2647 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2651 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2652 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2653 client.clearTempMod(nodepos);
\r
2654 client.removeNode(nodepos);
\r
2658 nodig_delay_counter = dig_time_complete
\r
2659 / (float)CRACK_ANIMATION_LENGTH;
\r
2661 // We don't want a corresponding delay to
\r
2662 // very time consuming nodes
\r
2663 if(nodig_delay_counter > 0.5)
\r
2665 nodig_delay_counter = 0.5;
\r
2667 // We want a slight delay to very little
\r
2668 // time consuming nodes
\r
2669 float mindelay = 0.15;
\r
2670 if(nodig_delay_counter < mindelay)
\r
2672 nodig_delay_counter = mindelay;
\r
2676 dig_time += dtime;
\r
2680 if(g_input->getRightClicked())
\r
2682 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2683 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2686 nodepos_old = nodepos;
\r
2691 } // selected_object == NULL
\r
2693 g_input->resetLeftClicked();
\r
2694 g_input->resetRightClicked();
\r
2696 if(g_input->getLeftReleased())
\r
2698 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2700 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2702 if(g_input->getRightReleased())
\r
2704 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2708 g_input->resetLeftReleased();
\r
2709 g_input->resetRightReleased();
\r
2712 Calculate stuff for drawing
\r
2715 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2717 u32 daynight_ratio = client.getDayNightRatio();
\r
2718 /*video::SColor bgcolor = video::SColor(
\r
2720 skycolor.getRed() * daynight_ratio / 1000,
\r
2721 skycolor.getGreen() * daynight_ratio / 1000,
\r
2722 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2724 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2725 video::SColor bgcolor = video::SColor(
\r
2727 skycolor.getRed() * l / 255,
\r
2728 skycolor.getGreen() * l / 255,
\r
2729 skycolor.getBlue() * l / 255);
\r
2735 if(g_settings.getBool("enable_fog") == true)
\r
2737 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2738 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2739 if(draw_control.range_all)
\r
2740 range = 100000*BS;
\r
2744 video::EFT_FOG_LINEAR,
\r
2748 false, // pixel fog
\r
2749 false // range fog
\r
2755 Update gui stuff (0ms)
\r
2758 //TimeTaker guiupdatetimer("Gui updating");
\r
2761 wchar_t temptext[150];
\r
2763 static float drawtime_avg = 0;
\r
2764 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2765 static float beginscenetime_avg = 0;
\r
2766 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2767 static float scenetime_avg = 0;
\r
2768 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2769 static float endscenetime_avg = 0;
\r
2770 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2772 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2774 L", R: range_all=%i"
\r
2776 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2778 draw_control.range_all,
\r
2780 beginscenetime_avg,
\r
2785 guitext->setText(temptext);
\r
2789 wchar_t temptext[150];
\r
2790 swprintf(temptext, 150,
\r
2791 L"(% .1f, % .1f, % .1f)"
\r
2792 L" (% .3f < btime_jitter < % .3f"
\r
2793 L", dtime_jitter = % .1f %%"
\r
2794 L", v_range = %.1f)",
\r
2795 player_position.X/BS,
\r
2796 player_position.Y/BS,
\r
2797 player_position.Z/BS,
\r
2798 busytime_jitter1_min_sample,
\r
2799 busytime_jitter1_max_sample,
\r
2800 dtime_jitter1_max_fraction * 100.0,
\r
2801 draw_control.wanted_range
\r
2804 guitext2->setText(temptext);
\r
2808 guitext_info->setText(infotext.c_str());
\r
2812 Get chat messages from client
\r
2815 // Get new messages
\r
2816 std::wstring message;
\r
2817 while(client.getChatMessage(message))
\r
2819 chat_lines.push_back(ChatLine(message));
\r
2820 /*if(chat_lines.size() > 6)
\r
2822 core::list<ChatLine>::Iterator
\r
2823 i = chat_lines.begin();
\r
2824 chat_lines.erase(i);
\r
2827 // Append them to form the whole static text and throw
\r
2828 // it to the gui element
\r
2829 std::wstring whole;
\r
2830 // This will correspond to the line number counted from
\r
2831 // top to bottom, from size-1 to 0
\r
2832 s16 line_number = chat_lines.size();
\r
2833 // Count of messages to be removed from the top
\r
2834 u16 to_be_removed_count = 0;
\r
2835 for(core::list<ChatLine>::Iterator
\r
2836 i = chat_lines.begin();
\r
2837 i != chat_lines.end(); i++)
\r
2839 // After this, line number is valid for this loop
\r
2842 (*i).age += dtime;
\r
2844 This results in a maximum age of 60*6 to the
\r
2845 lowermost line and a maximum of 6 lines
\r
2847 float allowed_age = (6-line_number) * 60.0;
\r
2849 if((*i).age > allowed_age)
\r
2851 to_be_removed_count++;
\r
2854 whole += (*i).text + L'\n';
\r
2856 for(u16 i=0; i<to_be_removed_count; i++)
\r
2858 core::list<ChatLine>::Iterator
\r
2859 it = chat_lines.begin();
\r
2860 chat_lines.erase(it);
\r
2862 guitext_chat->setText(whole.c_str());
\r
2863 // Update gui element size and position
\r
2864 core::rect<s32> rect(
\r
2866 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2867 screensize.X - 10,
\r
2870 guitext_chat->setRelativePosition(rect);
\r
2872 if(chat_lines.size() == 0)
\r
2873 guitext_chat->setVisible(false);
\r
2875 guitext_chat->setVisible(true);
\r
2882 static u16 old_selected_item = 65535;
\r
2883 if(client.getLocalInventoryUpdated()
\r
2884 || g_selected_item != old_selected_item)
\r
2886 old_selected_item = g_selected_item;
\r
2887 //std::cout<<"Updating local inventory"<<std::endl;
\r
2888 client.getLocalInventory(local_inventory);
\r
2889 quick_inventory->setSelection(g_selected_item);
\r
2890 quick_inventory->update();
\r
2894 Send actions returned by the inventory menu
\r
2896 while(inventory_action_queue.size() != 0)
\r
2898 InventoryAction *a = inventory_action_queue.pop_front();
\r
2900 client.sendInventoryAction(a);
\r
2909 TimeTaker drawtimer("Drawing");
\r
2913 TimeTaker timer("beginScene");
\r
2914 driver->beginScene(true, true, bgcolor);
\r
2915 //driver->beginScene(false, true, bgcolor);
\r
2916 beginscenetime = timer.stop(true);
\r
2921 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2924 TimeTaker timer("smgr");
\r
2926 scenetime = timer.stop(true);
\r
2930 //TimeTaker timer9("auxiliary drawings");
\r
2934 //TimeTaker //timer10("//timer10");
\r
2936 video::SMaterial m;
\r
2937 //m.Thickness = 10;
\r
2939 m.Lighting = false;
\r
2940 driver->setMaterial(m);
\r
2942 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2944 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2945 i != hilightboxes.end(); i++)
\r
2947 /*std::cout<<"hilightbox min="
\r
2948 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2950 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2952 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2958 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2959 displaycenter + core::vector2d<s32>(10,0),
\r
2960 video::SColor(255,255,255,255));
\r
2961 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2962 displaycenter + core::vector2d<s32>(0,10),
\r
2963 video::SColor(255,255,255,255));
\r
2968 //TimeTaker //timer11("//timer11");
\r
2974 guienv->drawAll();
\r
2978 TimeTaker timer("endScene");
\r
2979 driver->endScene();
\r
2980 endscenetime = timer.stop(true);
\r
2983 drawtime = drawtimer.stop(true);
\r
2989 static s16 lastFPS = 0;
\r
2990 //u16 fps = driver->getFPS();
\r
2991 u16 fps = (1.0/dtime_avg1);
\r
2993 if (lastFPS != fps)
\r
2995 core::stringw str = L"Minetest [";
\r
2996 str += driver->getName();
\r
3000 device->setWindowCaption(str.c_str());
\r
3006 device->yield();*/
\r
3009 delete quick_inventory;
\r
3012 Disable texture fetches and other stuff that is queued
\r
3013 to be processed by the main loop.
\r
3015 This has to be done before client goes out of scope.
\r
3017 g_irrlicht->Shutdown(true);
\r
3019 } // client and server are deleted at this point
\r
3022 catch(con::PeerNotFoundException &e)
\r
3024 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3025 error_message = L"Connection timed out.";
\r
3028 } // Menu-game loop
\r
3033 In the end, delete the Irrlicht device.
\r
3038 Update configuration file
\r
3040 /*if(configpath != "")
\r
3042 g_settings.updateConfigFile(configpath.c_str());
\r
3045 END_DEBUG_EXCEPTION_HANDLER
\r
3047 debugstreams_deinit();
\r