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 to do before release
\r
271 * Save the new mapgen stuff
\r
272 - map/meta.txt, which should contain only plain text, something like this:
\r
273 seed = 7ff1bafcd7118800
\r
275 - map/chunks.dat: chunk positions and flags in binary format
\r
276 * Make server find the spawning place from the real map data, not from
\r
278 - But the changing borders of chunk have to be avoided, because
\r
279 there is time to generate only one chunk.
\r
280 * Make the generator to run in background and not blocking block
\r
281 placement and transfer
\r
282 * only_from_disk might not work anymore - check and fix it.
\r
283 * Check the fixmes in the list above
\r
285 === Making it more portable
\r
286 * MinGW: Switch away from swprintf; mingw has a bad version of it.
\r
287 Use snprintf + narrow_to_wide or (w)ostringstream
\r
288 * Some MSVC: std::sto* are defined without a namespace and collide
\r
289 with the ones in utility.h
\r
291 === Stuff to do after release
\r
292 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
293 - Or maybe move content_features to material.{h,cpp}?
\r
294 * Add some kind of erosion and other stuff that now is possible
\r
295 * Make client to fetch stuff asynchronously
\r
296 - Needs method SyncProcessData
\r
297 * Fix the problem with the server constantly saving one or a few
\r
298 blocks? List the first saved block, maybe it explains.
\r
299 - It is probably caused by oscillating water
\r
300 * Water doesn't start flowing after map generation like it should
\r
301 - Are there still problems?
\r
302 * Better water generation (spread it to underwater caverns but don't
\r
303 fill dungeons that don't touch big water masses)
\r
304 * When generating a chunk and the neighboring chunk doesn't have mud
\r
305 and stuff yet and the ground is fairly flat, the mud will flow to
\r
306 the other chunk making nasty straight walls when the other chunk
\r
307 is generated. Fix it.
\r
308 * Make a small history check to transformLiquids to detect and log
\r
309 continuous oscillations, in such detail that they can be fixed.
\r
310 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
312 ======================================================================
\r
317 Setting this to 1 enables a special camera mode that forces
\r
318 the renderers to think that the camera statically points from
\r
319 the starting place to a static direction.
\r
321 This allows one to move around with the player and see what
\r
322 is actually drawn behind solid things and behind the player.
\r
324 #define FIELD_OF_VIEW_TEST 0
\r
328 #pragma message ("Disabling unit tests")
\r
330 #warning "Disabling unit tests"
\r
332 // Disable unit tests
\r
333 #define ENABLE_TESTS 0
\r
335 // Enable unit tests
\r
336 #define ENABLE_TESTS 1
\r
340 #pragma comment(lib, "Irrlicht.lib")
\r
341 //#pragma comment(lib, "jthread.lib")
\r
342 #pragma comment(lib, "zlibwapi.lib")
\r
343 #pragma comment(lib, "Shell32.lib")
\r
344 // This would get rid of the console window
\r
345 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
348 #include <iostream>
\r
350 #include <jmutexautolock.h>
\r
351 #include <locale.h>
\r
352 #include "common_irrlicht.h"
\r
355 #include "player.h"
\r
358 #include "environment.h"
\r
359 #include "server.h"
\r
360 #include "client.h"
\r
361 #include "serialization.h"
\r
362 #include "constants.h"
\r
363 #include "strfnd.h"
\r
364 #include "porting.h"
\r
365 #include "irrlichtwrapper.h"
\r
366 #include "gettime.h"
\r
367 #include "porting.h"
\r
368 #include "guiPauseMenu.h"
\r
369 #include "guiInventoryMenu.h"
\r
370 #include "guiTextInputMenu.h"
\r
371 #include "materials.h"
\r
372 #include "guiMessageMenu.h"
\r
373 #include "filesys.h"
\r
374 #include "config.h"
\r
375 #include "guiMainMenu.h"
\r
376 #include "mineral.h"
\r
380 // TODO: Remove this
\r
381 IrrlichtWrapper *g_irrlicht = NULL;
\r
383 // This makes textures
\r
384 ITextureSource *g_texturesource = NULL;
\r
386 MapDrawControl draw_control;
\r
390 These are loaded from the config file.
\r
393 Settings g_settings;
\r
395 extern void set_default_settings();
\r
401 IrrlichtDevice *g_device = NULL;
\r
402 Client *g_client = NULL;
\r
408 gui::IGUIEnvironment* guienv = NULL;
\r
409 gui::IGUIStaticText *guiroot = NULL;
\r
411 class MainMenuManager : public IMenuManager
\r
414 virtual void createdMenu(GUIModalMenu *menu)
\r
416 for(core::list<GUIModalMenu*>::Iterator
\r
417 i = m_stack.begin();
\r
418 i != m_stack.end(); i++)
\r
420 assert(*i != menu);
\r
423 if(m_stack.size() != 0)
\r
424 (*m_stack.getLast())->setVisible(false);
\r
425 m_stack.push_back(menu);
\r
428 virtual void deletingMenu(GUIModalMenu *menu)
\r
430 // Remove all entries if there are duplicates
\r
431 bool removed_entry;
\r
433 removed_entry = false;
\r
434 for(core::list<GUIModalMenu*>::Iterator
\r
435 i = m_stack.begin();
\r
436 i != m_stack.end(); i++)
\r
441 removed_entry = true;
\r
445 }while(removed_entry);
\r
447 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
448 assert(*i == menu);
\r
449 m_stack.erase(i);*/
\r
451 if(m_stack.size() != 0)
\r
452 (*m_stack.getLast())->setVisible(true);
\r
457 return m_stack.size();
\r
460 core::list<GUIModalMenu*> m_stack;
\r
463 MainMenuManager g_menumgr;
\r
465 bool noMenuActive()
\r
467 return (g_menumgr.menuCount() == 0);
\r
470 bool g_disconnect_requested = false;
\r
472 class MainGameCallback : public IGameCallback
\r
475 virtual void exitToOS()
\r
477 g_device->closeDevice();
\r
480 virtual void disconnect()
\r
482 g_disconnect_requested = true;
\r
486 MainGameCallback g_gamecallback;
\r
488 // Inventory actions from the menu are buffered here before sending
\r
489 Queue<InventoryAction*> inventory_action_queue;
\r
490 // This is a copy of the inventory that the client's environment has
\r
491 Inventory local_inventory;
\r
493 u16 g_selected_item = 0;
\r
500 std::ostream *dout_con_ptr = &dummyout;
\r
501 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
502 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
503 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
504 //std::ostream *dout_con_ptr = &dstream;
\r
505 //std::ostream *derr_con_ptr = &dstream;
\r
508 std::ostream *dout_server_ptr = &dstream;
\r
509 std::ostream *derr_server_ptr = &dstream;
\r
512 std::ostream *dout_client_ptr = &dstream;
\r
513 std::ostream *derr_client_ptr = &dstream;
\r
516 gettime.h implementation
\r
522 Use irrlicht because it is more precise than porting.h's
\r
525 if(g_irrlicht == NULL)
\r
527 return g_irrlicht->getTime();
\r
534 struct TextDestSign : public TextDest
\r
536 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
538 m_blockpos = blockpos;
\r
542 void gotText(std::wstring text)
\r
544 std::string ntext = wide_to_narrow(text);
\r
545 dstream<<"Changing text of a sign object: "
\r
546 <<ntext<<std::endl;
\r
547 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
555 struct TextDestChat : public TextDest
\r
557 TextDestChat(Client *client)
\r
561 void gotText(std::wstring text)
\r
563 m_client->sendChatMessage(text);
\r
564 m_client->addChatMessage(text);
\r
570 class MyEventReceiver : public IEventReceiver
\r
573 // This is the one method that we have to implement
\r
574 virtual bool OnEvent(const SEvent& event)
\r
577 React to nothing here if a menu is active
\r
579 if(noMenuActive() == false)
\r
585 // Remember whether each key is down or up
\r
586 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
588 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
590 if(event.KeyInput.PressedDown)
\r
592 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
598 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
600 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
602 dstream<<DTIME<<"MyEventReceiver: "
\r
603 <<"Launching pause menu"<<std::endl;
\r
604 // It will delete itself by itself
\r
605 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
606 &g_menumgr))->drop();
\r
609 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
611 dstream<<DTIME<<"MyEventReceiver: "
\r
612 <<"Launching inventory"<<std::endl;
\r
613 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
614 &local_inventory, &inventory_action_queue,
\r
615 &g_menumgr))->drop();
\r
618 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
620 TextDest *dest = new TextDestChat(g_client);
\r
622 (new GUITextInputMenu(guienv, guiroot, -1,
\r
628 // Material selection
\r
629 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
631 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
634 g_selected_item = 0;
\r
635 dstream<<DTIME<<"Selected item: "
\r
636 <<g_selected_item<<std::endl;
\r
639 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
640 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
642 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
643 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
645 if(s1 < PLAYER_INVENTORY_SIZE)
\r
646 g_selected_item = s1-1;
\r
647 dstream<<DTIME<<"Selected item: "
\r
648 <<g_selected_item<<std::endl;
\r
651 // Viewing range selection
\r
652 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
654 if(draw_control.range_all)
\r
656 draw_control.range_all = false;
\r
657 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
661 draw_control.range_all = true;
\r
662 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
666 // Print debug stacks
\r
667 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
669 dstream<<"-----------------------------------------"
\r
671 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
672 dstream<<"-----------------------------------------"
\r
674 debug_stacks_print();
\r
679 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
681 if(noMenuActive() == false)
\r
683 left_active = false;
\r
684 middle_active = false;
\r
685 right_active = false;
\r
689 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
690 left_active = event.MouseInput.isLeftPressed();
\r
691 middle_active = event.MouseInput.isMiddlePressed();
\r
692 right_active = event.MouseInput.isRightPressed();
\r
694 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
696 leftclicked = true;
\r
698 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
700 rightclicked = true;
\r
702 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
704 leftreleased = true;
\r
706 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
708 rightreleased = true;
\r
710 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
712 /*dstream<<"event.MouseInput.Wheel="
\r
713 <<event.MouseInput.Wheel<<std::endl;*/
\r
714 if(event.MouseInput.Wheel < 0)
\r
716 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
719 g_selected_item = 0;
\r
721 else if(event.MouseInput.Wheel > 0)
\r
723 if(g_selected_item > 0)
\r
726 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
735 // This is used to check whether a key is being held down
\r
736 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
738 return keyIsDown[keyCode];
\r
743 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
744 keyIsDown[i] = false;
\r
746 leftclicked = false;
\r
747 rightclicked = false;
\r
748 leftreleased = false;
\r
749 rightreleased = false;
\r
751 left_active = false;
\r
752 middle_active = false;
\r
753 right_active = false;
\r
764 bool rightreleased;
\r
767 bool middle_active;
\r
771 // We use this array to store the current state of each key
\r
772 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
775 IrrlichtDevice *m_device;
\r
784 virtual ~InputHandler()
\r
788 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
790 virtual v2s32 getMousePos() = 0;
\r
791 virtual void setMousePos(s32 x, s32 y) = 0;
\r
793 virtual bool getLeftState() = 0;
\r
794 virtual bool getRightState() = 0;
\r
796 virtual bool getLeftClicked() = 0;
\r
797 virtual bool getRightClicked() = 0;
\r
798 virtual void resetLeftClicked() = 0;
\r
799 virtual void resetRightClicked() = 0;
\r
801 virtual bool getLeftReleased() = 0;
\r
802 virtual bool getRightReleased() = 0;
\r
803 virtual void resetLeftReleased() = 0;
\r
804 virtual void resetRightReleased() = 0;
\r
806 virtual void step(float dtime) {};
\r
808 virtual void clear() {};
\r
811 InputHandler *g_input = NULL;
\r
813 class RealInputHandler : public InputHandler
\r
816 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
818 m_receiver(receiver)
\r
821 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
823 return m_receiver->IsKeyDown(keyCode);
\r
825 virtual v2s32 getMousePos()
\r
827 return m_device->getCursorControl()->getPosition();
\r
829 virtual void setMousePos(s32 x, s32 y)
\r
831 m_device->getCursorControl()->setPosition(x, y);
\r
834 virtual bool getLeftState()
\r
836 return m_receiver->left_active;
\r
838 virtual bool getRightState()
\r
840 return m_receiver->right_active;
\r
843 virtual bool getLeftClicked()
\r
845 return m_receiver->leftclicked;
\r
847 virtual bool getRightClicked()
\r
849 return m_receiver->rightclicked;
\r
851 virtual void resetLeftClicked()
\r
853 m_receiver->leftclicked = false;
\r
855 virtual void resetRightClicked()
\r
857 m_receiver->rightclicked = false;
\r
860 virtual bool getLeftReleased()
\r
862 return m_receiver->leftreleased;
\r
864 virtual bool getRightReleased()
\r
866 return m_receiver->rightreleased;
\r
868 virtual void resetLeftReleased()
\r
870 m_receiver->leftreleased = false;
\r
872 virtual void resetRightReleased()
\r
874 m_receiver->rightreleased = false;
\r
879 resetRightClicked();
\r
880 resetLeftClicked();
\r
883 IrrlichtDevice *m_device;
\r
884 MyEventReceiver *m_receiver;
\r
887 class RandomInputHandler : public InputHandler
\r
890 RandomInputHandler()
\r
892 leftclicked = false;
\r
893 rightclicked = false;
\r
894 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
895 keydown[i] = false;
\r
897 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
899 return keydown[keyCode];
\r
901 virtual v2s32 getMousePos()
\r
905 virtual void setMousePos(s32 x, s32 y)
\r
907 mousepos = v2s32(x,y);
\r
910 virtual bool getLeftState()
\r
914 virtual bool getRightState()
\r
919 virtual bool getLeftClicked()
\r
921 return leftclicked;
\r
923 virtual bool getRightClicked()
\r
925 return rightclicked;
\r
927 virtual void resetLeftClicked()
\r
929 leftclicked = false;
\r
931 virtual void resetRightClicked()
\r
933 rightclicked = false;
\r
936 virtual bool getLeftReleased()
\r
940 virtual bool getRightReleased()
\r
944 virtual void resetLeftReleased()
\r
947 virtual void resetRightReleased()
\r
951 virtual void step(float dtime)
\r
954 static float counter1 = 0;
\r
958 counter1 = 0.1*Rand(1,10);
\r
959 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
960 g_selected_material++;
\r
962 g_selected_material = 0;*/
\r
963 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
966 g_selected_item = 0;
\r
970 static float counter1 = 0;
\r
974 counter1 = 0.1*Rand(1, 40);
\r
975 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
979 static float counter1 = 0;
\r
983 counter1 = 0.1*Rand(1, 40);
\r
984 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
988 static float counter1 = 0;
\r
992 counter1 = 0.1*Rand(1, 40);
\r
993 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
997 static float counter1 = 0;
\r
1001 counter1 = 0.1*Rand(1, 40);
\r
1002 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1006 static float counter1 = 0;
\r
1007 counter1 -= dtime;
\r
1008 if(counter1 < 0.0)
\r
1010 counter1 = 0.1*Rand(1, 20);
\r
1011 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1015 static float counter1 = 0;
\r
1016 counter1 -= dtime;
\r
1017 if(counter1 < 0.0)
\r
1019 counter1 = 0.1*Rand(1, 30);
\r
1020 leftclicked = true;
\r
1024 static float counter1 = 0;
\r
1025 counter1 -= dtime;
\r
1026 if(counter1 < 0.0)
\r
1028 counter1 = 0.1*Rand(1, 20);
\r
1029 rightclicked = true;
\r
1032 mousepos += mousespeed;
\r
1035 s32 Rand(s32 min, s32 max)
\r
1037 return (myrand()%(max-min+1))+min;
\r
1040 bool keydown[KEY_KEY_CODES_COUNT];
\r
1044 bool rightclicked;
\r
1047 void updateViewingRange(f32 frametime_in, Client *client)
\r
1049 if(draw_control.range_all == true)
\r
1052 static f32 added_frametime = 0;
\r
1053 static s16 added_frames = 0;
\r
1055 added_frametime += frametime_in;
\r
1056 added_frames += 1;
\r
1058 // Actually this counter kind of sucks because frametime is busytime
\r
1059 static f32 counter = 0;
\r
1060 counter -= frametime_in;
\r
1066 /*dstream<<__FUNCTION_NAME
\r
1067 <<": Collected "<<added_frames<<" frames, total of "
\r
1068 <<added_frametime<<"s."<<std::endl;*/
\r
1070 /*dstream<<"draw_control.blocks_drawn="
\r
1071 <<draw_control.blocks_drawn
\r
1072 <<", draw_control.blocks_would_have_drawn="
\r
1073 <<draw_control.blocks_would_have_drawn
\r
1076 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1077 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1079 draw_control.wanted_min_range = range_min;
\r
1080 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1082 float block_draw_ratio = 1.0;
\r
1083 if(draw_control.blocks_would_have_drawn != 0)
\r
1085 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1086 / (float)draw_control.blocks_would_have_drawn;
\r
1089 // Calculate the average frametime in the case that all wanted
\r
1090 // blocks had been drawn
\r
1091 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1093 added_frametime = 0.0;
\r
1096 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1097 float wanted_frametime = 1.0 / wanted_fps;
\r
1099 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1100 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1102 // If needed frametime change is very small, just return
\r
1103 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1105 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1109 float range = draw_control.wanted_range;
\r
1110 float new_range = range;
\r
1112 static s16 range_old = 0;
\r
1113 static f32 frametime_old = 0;
\r
1115 float d_range = range - range_old;
\r
1116 f32 d_frametime = frametime - frametime_old;
\r
1117 // A sane default of 30ms per 50 nodes of range
\r
1118 static f32 time_per_range = 30. / 50;
\r
1121 time_per_range = d_frametime / d_range;
\r
1124 // The minimum allowed calculated frametime-range derivative:
\r
1125 // Practically this sets the maximum speed of changing the range.
\r
1126 // The lower this value, the higher the maximum changing speed.
\r
1127 // A low value here results in wobbly range (0.001)
\r
1128 // A high value here results in slow changing range (0.0025)
\r
1129 // SUGG: This could be dynamically adjusted so that when
\r
1130 // the camera is turning, this is lower
\r
1131 //float min_time_per_range = 0.0015;
\r
1132 float min_time_per_range = 0.0010;
\r
1133 //float min_time_per_range = 0.05 / range;
\r
1134 if(time_per_range < min_time_per_range)
\r
1136 time_per_range = min_time_per_range;
\r
1137 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1141 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1144 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1145 // Dampen the change a bit to kill oscillations
\r
1146 //wanted_range_change *= 0.9;
\r
1147 //wanted_range_change *= 0.75;
\r
1148 wanted_range_change *= 0.5;
\r
1149 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1151 // If needed range change is very small, just return
\r
1152 if(fabs(wanted_range_change) < 0.001)
\r
1154 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1158 new_range += wanted_range_change;
\r
1159 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1161 //float new_range_unclamped = new_range;
\r
1162 if(new_range < range_min)
\r
1163 new_range = range_min;
\r
1164 if(new_range > range_max)
\r
1165 new_range = range_max;
\r
1167 /*if(new_range != new_range_unclamped)
\r
1168 dstream<<", clamped to "<<new_range<<std::endl;
\r
1170 dstream<<std::endl;*/
\r
1172 draw_control.wanted_range = new_range;
\r
1174 range_old = new_range;
\r
1175 frametime_old = frametime;
\r
1178 class GUIQuickInventory : public IEventReceiver
\r
1181 GUIQuickInventory(
\r
1182 gui::IGUIEnvironment* env,
\r
1183 gui::IGUIElement* parent,
\r
1186 Inventory *inventory):
\r
1187 m_itemcount(itemcount),
\r
1188 m_inventory(inventory)
\r
1190 core::rect<s32> imgsize(0,0,48,48);
\r
1191 core::rect<s32> textsize(0,0,48,16);
\r
1192 v2s32 spacing(0, 64);
\r
1193 for(s32 i=0; i<m_itemcount; i++)
\r
1195 m_images.push_back(env->addImage(
\r
1196 imgsize + pos + spacing*i
\r
1198 m_images[i]->setScaleImage(true);
\r
1199 m_texts.push_back(env->addStaticText(
\r
1201 textsize + pos + spacing*i,
\r
1204 m_texts[i]->setBackgroundColor(
\r
1205 video::SColor(128,0,0,0));
\r
1206 m_texts[i]->setTextAlignment(
\r
1207 gui::EGUIA_CENTER,
\r
1208 gui::EGUIA_UPPERLEFT);
\r
1212 ~GUIQuickInventory()
\r
1214 for(u32 i=0; i<m_texts.size(); i++)
\r
1216 m_texts[i]->remove();
\r
1218 for(u32 i=0; i<m_images.size(); i++)
\r
1220 m_images[i]->remove();
\r
1224 virtual bool OnEvent(const SEvent& event)
\r
1229 void setSelection(s32 i)
\r
1238 start = m_selection - m_itemcount / 2;
\r
1240 InventoryList *mainlist = m_inventory->getList("main");
\r
1242 for(s32 i=0; i<m_itemcount; i++)
\r
1244 s32 j = i + start;
\r
1246 if(j > (s32)mainlist->getSize() - 1)
\r
1247 j -= mainlist->getSize();
\r
1249 j += mainlist->getSize();
\r
1251 InventoryItem *item = mainlist->getItem(j);
\r
1255 m_images[i]->setImage(NULL);
\r
1258 if(m_selection == j)
\r
1259 swprintf(t, 10, L"<-");
\r
1261 swprintf(t, 10, L"");
\r
1262 m_texts[i]->setText(t);
\r
1264 // The next ifs will segfault with a NULL pointer
\r
1269 m_images[i]->setImage(item->getImage());
\r
1272 if(m_selection == j)
\r
1273 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1275 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1276 m_texts[i]->setText(t);
\r
1282 core::array<gui::IGUIStaticText*> m_texts;
\r
1283 core::array<gui::IGUIImage*> m_images;
\r
1284 Inventory *m_inventory;
\r
1295 ChatLine(const std::wstring &a_text):
\r
1301 std::wstring text;
\r
1304 // These are defined global so that they're not optimized too much.
\r
1305 // Can't change them to volatile.
\r
1310 std::string tempstring;
\r
1311 std::string tempstring2;
\r
1316 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1317 TimeTaker timer("Testing std::string speed");
\r
1318 const u32 jj = 10000;
\r
1319 for(u32 j=0; j<jj; j++)
\r
1323 const u32 ii = 10;
\r
1324 for(u32 i=0; i<ii; i++){
\r
1325 tempstring2 += "asd";
\r
1327 for(u32 i=0; i<ii+1; i++){
\r
1328 tempstring += "asd";
\r
1329 if(tempstring == tempstring2)
\r
1335 dstream<<"All of the following tests should take around 100ms each."
\r
1339 TimeTaker timer("Testing floating-point conversion speed");
\r
1341 for(u32 i=0; i<4000000; i++){
\r
1348 TimeTaker timer("Testing floating-point vector speed");
\r
1350 tempv3f1 = v3f(1,2,3);
\r
1351 tempv3f2 = v3f(4,5,6);
\r
1352 for(u32 i=0; i<10000000; i++){
\r
1353 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1354 tempv3f2 += v3f(7,8,9);
\r
1359 TimeTaker timer("Testing core::map speed");
\r
1361 core::map<v2s16, f32> map1;
\r
1364 for(s16 y=0; y<ii; y++){
\r
1365 for(s16 x=0; x<ii; x++){
\r
1366 map1.insert(v2s16(x,y), tempf);
\r
1370 for(s16 y=ii-1; y>=0; y--){
\r
1371 for(s16 x=0; x<ii; x++){
\r
1372 tempf = map1[v2s16(x,y)];
\r
1378 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1379 TimeTaker timer("Testing mutex speed");
\r
1392 // Do at least 10ms
\r
1393 while(timer.getTime() < 10);
\r
1395 u32 dtime = timer.stop();
\r
1396 u32 per_ms = n / dtime;
\r
1397 std::cout<<"Done. "<<dtime<<"ms, "
\r
1398 <<per_ms<<"/ms"<<std::endl;
\r
1402 int main(int argc, char *argv[])
\r
1405 Parse command line
\r
1408 // List all allowed options
\r
1409 core::map<std::string, ValueSpec> allowed_options;
\r
1410 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1411 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1412 "Run server directly"));
\r
1413 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1414 "Load configuration from specified file"));
\r
1415 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1416 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1417 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1418 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1419 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1420 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1422 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1424 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1426 Settings cmd_args;
\r
1428 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1430 if(ret == false || cmd_args.getFlag("help"))
\r
1432 dstream<<"Allowed options:"<<std::endl;
\r
1433 for(core::map<std::string, ValueSpec>::Iterator
\r
1434 i = allowed_options.getIterator();
\r
1435 i.atEnd() == false; i++)
\r
1437 dstream<<" --"<<i.getNode()->getKey();
\r
1438 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1443 dstream<<" <value>";
\r
1445 dstream<<std::endl;
\r
1447 if(i.getNode()->getValue().help != NULL)
\r
1449 dstream<<" "<<i.getNode()->getValue().help
\r
1454 return cmd_args.getFlag("help") ? 0 : 1;
\r
1458 Low-level initialization
\r
1461 bool disable_stderr = false;
\r
1463 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1464 disable_stderr = true;
\r
1467 // Initialize debug streams
\r
1468 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1469 // Initialize debug stacks
\r
1470 debug_stacks_init();
\r
1472 DSTACK(__FUNCTION_NAME);
\r
1474 porting::initializePaths();
\r
1475 // Create user data directory
\r
1476 fs::CreateDir(porting::path_userdata);
\r
1478 // C-style stuff initialization
\r
1479 initializeMaterialProperties();
\r
1482 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1484 // Print startup message
\r
1485 dstream<<DTIME<<"minetest-c55"
\r
1486 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1487 <<", "<<BUILD_INFO
\r
1491 Basic initialization
\r
1494 // Initialize default settings
\r
1495 set_default_settings();
\r
1497 // Set locale. This is for forcing '.' as the decimal point.
\r
1498 std::locale::global(std::locale("C"));
\r
1499 // This enables printing all characters in bitmap font
\r
1500 setlocale(LC_CTYPE, "en_US");
\r
1502 // Initialize sockets
\r
1504 atexit(sockets_cleanup);
\r
1514 // Path of configuration file in use
\r
1515 std::string configpath = "";
\r
1517 if(cmd_args.exists("config"))
\r
1519 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1522 dstream<<"Could not read configuration from \""
\r
1523 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1526 configpath = cmd_args.get("config");
\r
1530 core::array<std::string> filenames;
\r
1531 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1532 #ifdef RUN_IN_PLACE
\r
1533 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1536 for(u32 i=0; i<filenames.size(); i++)
\r
1538 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1541 configpath = filenames[i];
\r
1547 // Initialize random seed
\r
1552 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1554 These are needed for unit tests at least.
\r
1557 // Initial call with g_texturesource not set.
\r
1564 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1565 || cmd_args.getFlag("enable-unittests") == true)
\r
1570 /*for(s16 y=-100; y<100; y++)
\r
1571 for(s16 x=-100; x<100; x++)
\r
1573 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1583 if(cmd_args.exists("port"))
\r
1584 port = cmd_args.getU16("port");
\r
1585 else if(cmd_args.exists("port"))
\r
1586 port = g_settings.getU16("port");
\r
1589 std::string map_dir = porting::path_userdata+"/map";
\r
1590 if(cmd_args.exists("map-dir"))
\r
1591 map_dir = cmd_args.get("map-dir");
\r
1592 else if(g_settings.exists("map-dir"))
\r
1593 map_dir = g_settings.get("map-dir");
\r
1595 // Run dedicated server if asked to
\r
1596 if(cmd_args.getFlag("server"))
\r
1598 DSTACK("Dedicated server branch");
\r
1601 Server server(map_dir.c_str());
\r
1602 server.start(port);
\r
1605 dedicated_server_loop(server);
\r
1614 // Address to connect to
\r
1615 std::string address = "";
\r
1617 if(cmd_args.exists("address"))
\r
1619 address = cmd_args.get("address");
\r
1623 address = g_settings.get("address");
\r
1626 std::string playername = g_settings.get("name");
\r
1629 Resolution selection
\r
1632 bool fullscreen = false;
\r
1633 u16 screenW = g_settings.getU16("screenW");
\r
1634 u16 screenH = g_settings.getU16("screenH");
\r
1638 MyEventReceiver receiver;
\r
1640 video::E_DRIVER_TYPE driverType;
\r
1643 //driverType = video::EDT_DIRECT3D9;
\r
1644 driverType = video::EDT_OPENGL;
\r
1646 driverType = video::EDT_OPENGL;
\r
1647 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1650 // create device and exit if creation failed
\r
1652 IrrlichtDevice *device;
\r
1653 device = createDevice(driverType,
\r
1654 core::dimension2d<u32>(screenW, screenH),
\r
1655 16, fullscreen, false, false, &receiver);
\r
1658 return 1; // could not create selected driver.
\r
1660 g_device = device;
\r
1661 g_irrlicht = new IrrlichtWrapper(device);
\r
1662 TextureSource *texturesource = new TextureSource(device);
\r
1663 g_texturesource = texturesource;
\r
1666 Speed tests (done after irrlicht is loaded to get timer)
\r
1668 if(cmd_args.getFlag("speedtests"))
\r
1670 dstream<<"Running speed tests"<<std::endl;
\r
1675 device->setResizable(true);
\r
1677 bool random_input = g_settings.getBool("random_input")
\r
1678 || cmd_args.getFlag("random-input");
\r
1680 g_input = new RandomInputHandler();
\r
1682 g_input = new RealInputHandler(device, &receiver);
\r
1685 Continue initialization
\r
1688 video::IVideoDriver* driver = device->getVideoDriver();
\r
1691 This changes the minimum allowed number of vertices in a VBO.
\r
1694 //driver->setMinHardwareBufferVertexCount(50);
\r
1696 scene::ISceneManager* smgr = device->getSceneManager();
\r
1698 guienv = device->getGUIEnvironment();
\r
1699 gui::IGUISkin* skin = guienv->getSkin();
\r
1700 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1702 skin->setFont(font);
\r
1704 dstream<<"WARNING: Font file was not found."
\r
1705 " Using default font."<<std::endl;
\r
1706 // If font was not found, this will get us one
\r
1707 font = skin->getFont();
\r
1710 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1711 dstream<<"text_height="<<text_height<<std::endl;
\r
1713 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1714 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1715 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1716 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1717 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1718 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1721 Preload some textures and stuff
\r
1724 init_content_inventory_texture_paths();
\r
1725 init_mapnode(); // Second call with g_texturesource set
\r
1733 We need some kind of a root node to be able to add
\r
1734 custom gui elements directly on the screen.
\r
1735 Otherwise they won't be automatically drawn.
\r
1737 guiroot = guienv->addStaticText(L"",
\r
1738 core::rect<s32>(0, 0, 10000, 10000));
\r
1740 // First line of debug text
\r
1741 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1743 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1745 // Second line of debug text
\r
1746 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1748 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1751 // At the middle of the screen
\r
1752 // Object infos are shown in this
\r
1753 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1755 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1759 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1761 core::rect<s32>(0,0,0,0),
\r
1763 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1764 core::list<ChatLine> chat_lines;
\r
1767 If an error occurs, this is set to something and the
\r
1768 menu-game loop is restarted. It is then displayed before
\r
1771 std::wstring error_message = L"";
\r
1776 while(g_device->run())
\r
1779 // This is used for catching disconnects
\r
1784 Out-of-game menu loop.
\r
1786 Loop quits when menu returns proper parameters.
\r
1790 // Cursor can be non-visible when coming from the game
\r
1791 device->getCursorControl()->setVisible(true);
\r
1792 // Some stuff are left to scene manager when coming from the game
\r
1793 // (map at least?)
\r
1795 // Reset or hide the debug gui texts
\r
1796 guitext->setText(L"Minetest-c55");
\r
1797 guitext2->setVisible(false);
\r
1798 guitext_info->setVisible(false);
\r
1799 guitext_chat->setVisible(false);
\r
1801 // Initialize menu data
\r
1802 MainMenuData menudata;
\r
1803 menudata.address = narrow_to_wide(address);
\r
1804 menudata.name = narrow_to_wide(playername);
\r
1805 menudata.port = narrow_to_wide(itos(port));
\r
1806 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1808 GUIMainMenu *menu =
\r
1809 new GUIMainMenu(guienv, guiroot, -1,
\r
1810 &g_menumgr, &menudata, &g_gamecallback);
\r
1811 menu->allowFocusRemoval(true);
\r
1813 if(error_message != L"")
\r
1815 GUIMessageMenu *menu2 =
\r
1816 new GUIMessageMenu(guienv, guiroot, -1,
\r
1817 &g_menumgr, error_message.c_str());
\r
1819 error_message = L"";
\r
1822 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1824 dstream<<"Created main menu"<<std::endl;
\r
1826 while(g_device->run())
\r
1828 // Run global IrrlichtWrapper's main thread processing stuff
\r
1829 g_irrlicht->Run();
\r
1831 if(menu->getStatus() == true)
\r
1834 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1835 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1836 guienv->drawAll();
\r
1837 driver->endScene();
\r
1840 // Break out of menu-game loop to shut down cleanly
\r
1841 if(g_device->run() == false)
\r
1844 dstream<<"Dropping main menu"<<std::endl;
\r
1848 // Delete map if requested
\r
1849 if(menudata.delete_map)
\r
1851 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1853 error_message = L"Delete failed";
\r
1857 playername = wide_to_narrow(menudata.name);
\r
1858 address = wide_to_narrow(menudata.address);
\r
1859 port = stoi(wide_to_narrow(menudata.port));
\r
1860 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1862 // Check for valid parameters, restart menu if invalid.
\r
1863 if(playername == "")
\r
1865 error_message = L"Name required.";
\r
1870 g_settings.set("name", playername);
\r
1871 g_settings.set("address", address);
\r
1872 g_settings.set("port", itos(port));
\r
1873 // Update configuration file
\r
1874 if(configpath != "")
\r
1875 g_settings.updateConfigFile(configpath.c_str());
\r
1877 // Continue to game
\r
1881 // Break out of menu-game loop to shut down cleanly
\r
1882 if(g_device->run() == false)
\r
1886 Make a scope here so that the client and the server and other
\r
1887 stuff gets removed when disconnected or the irrlicht device
\r
1892 // This is set to true at the end of the scope
\r
1893 g_irrlicht->Shutdown(false);
\r
1896 Draw "Loading" screen
\r
1898 const wchar_t *text = L"Loading and connecting...";
\r
1899 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1900 core::vector2d<s32> textsize(300, text_height);
\r
1901 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1903 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1904 text, textrect, false, false);
\r
1905 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1907 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1908 guienv->drawAll();
\r
1909 driver->endScene();
\r
1911 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1915 SharedPtr will delete it when it goes out of scope.
\r
1917 SharedPtr<Server> server;
\r
1918 if(address == ""){
\r
1919 server = new Server(map_dir);
\r
1920 server->start(port);
\r
1927 Client client(device, playername.c_str(), draw_control);
\r
1929 g_client = &client;
\r
1931 Address connect_address(0,0,0,0, port);
\r
1934 //connect_address.Resolve("localhost");
\r
1935 connect_address.setAddress(127,0,0,1);
\r
1937 connect_address.Resolve(address.c_str());
\r
1939 catch(ResolveError &e)
\r
1941 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1943 error_message = L"Couldn't resolve address";
\r
1944 gui_loadingtext->remove();
\r
1948 dstream<<DTIME<<"Connecting to server at ";
\r
1949 connect_address.print(&dstream);
\r
1950 dstream<<std::endl;
\r
1951 client.connect(connect_address);
\r
1954 while(client.connectedAndInitialized() == false)
\r
1957 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1958 guienv->drawAll();
\r
1959 driver->endScene();
\r
1961 // Update client and server
\r
1965 if(server != NULL)
\r
1966 server->step(0.1);
\r
1972 catch(con::PeerNotFoundException &e)
\r
1974 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1976 error_message = L"Connection timed out.";
\r
1977 gui_loadingtext->remove();
\r
1984 /*scene::ISceneNode* skybox;
\r
1985 skybox = smgr->addSkyBoxSceneNode(
\r
1986 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1987 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1988 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1989 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1990 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1991 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1994 Create the camera node
\r
1997 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1998 0, // Camera parent
\r
1999 v3f(BS*100, BS*2, BS*100), // Look from
\r
2000 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2004 if(camera == NULL)
\r
2007 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2009 camera->setFOV(FOV_ANGLE);
\r
2011 // Just so big a value that everything rendered is visible
\r
2012 camera->setFarValue(100000*BS);
\r
2014 f32 camera_yaw = 0; // "right/left"
\r
2015 f32 camera_pitch = 0; // "up/down"
\r
2021 gui_loadingtext->remove();
\r
2024 Add some gui stuff
\r
2027 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2028 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2030 // Test the text input system
\r
2031 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2033 /*GUIMessageMenu *menu =
\r
2034 new GUIMessageMenu(guienv, guiroot, -1,
\r
2039 // Launch pause menu
\r
2040 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2041 &g_menumgr))->drop();
\r
2044 guitext2->setVisible(true);
\r
2045 guitext_info->setVisible(true);
\r
2046 guitext_chat->setVisible(true);
\r
2049 Some statistics are collected in these
\r
2052 u32 beginscenetime = 0;
\r
2053 u32 scenetime = 0;
\r
2054 u32 endscenetime = 0;
\r
2057 //throw con::PeerNotFoundException("lol");
\r
2059 core::list<float> frametime_log;
\r
2065 bool first_loop_after_window_activation = true;
\r
2067 // Time is in milliseconds
\r
2068 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2069 // NOTE: So we have to use getTime() and call run()s between them
\r
2070 u32 lasttime = device->getTimer()->getTime();
\r
2072 while(device->run())
\r
2074 if(g_disconnect_requested)
\r
2076 g_disconnect_requested = false;
\r
2081 Run global IrrlichtWrapper's main thread processing stuff
\r
2083 g_irrlicht->Run();
\r
2086 Process TextureSource's queue
\r
2088 texturesource->processQueue();
\r
2091 Random calculations
\r
2093 v2u32 screensize = driver->getScreenSize();
\r
2094 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2096 // Hilight boxes collected during the loop and displayed
\r
2097 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2100 std::wstring infotext;
\r
2102 //TimeTaker //timer1("//timer1");
\r
2104 // Time of frame without fps limit
\r
2108 // not using getRealTime is necessary for wine
\r
2109 u32 time = device->getTimer()->getTime();
\r
2110 if(time > lasttime)
\r
2111 busytime_u32 = time - lasttime;
\r
2114 busytime = busytime_u32 / 1000.0;
\r
2117 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2119 // Absolutelu necessary for wine!
\r
2126 updateViewingRange(busytime, &client);
\r
2133 float fps_max = g_settings.getFloat("fps_max");
\r
2134 u32 frametime_min = 1000./fps_max;
\r
2136 if(busytime_u32 < frametime_min)
\r
2138 u32 sleeptime = frametime_min - busytime_u32;
\r
2139 device->sleep(sleeptime);
\r
2143 // Absolutelu necessary for wine!
\r
2147 Time difference calculation
\r
2149 f32 dtime; // in seconds
\r
2151 u32 time = device->getTimer()->getTime();
\r
2152 if(time > lasttime)
\r
2153 dtime = (time - lasttime) / 1000.0;
\r
2159 Log frametime for visualization
\r
2161 frametime_log.push_back(dtime);
\r
2162 if(frametime_log.size() > 100)
\r
2164 core::list<float>::Iterator i = frametime_log.begin();
\r
2165 frametime_log.erase(i);
\r
2169 Visualize frametime in terminal
\r
2171 /*for(u32 i=0; i<dtime*400; i++)
\r
2173 std::cout<<std::endl;*/
\r
2176 Time average and jitter calculation
\r
2179 static f32 dtime_avg1 = 0.0;
\r
2180 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2181 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2183 static f32 dtime_jitter1_max_sample = 0.0;
\r
2184 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2186 static f32 jitter1_max = 0.0;
\r
2187 static f32 counter = 0.0;
\r
2188 if(dtime_jitter1 > jitter1_max)
\r
2189 jitter1_max = dtime_jitter1;
\r
2194 dtime_jitter1_max_sample = jitter1_max;
\r
2195 dtime_jitter1_max_fraction
\r
2196 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2197 jitter1_max = 0.0;
\r
2202 Busytime average and jitter calculation
\r
2205 static f32 busytime_avg1 = 0.0;
\r
2206 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2207 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2209 static f32 busytime_jitter1_max_sample = 0.0;
\r
2210 static f32 busytime_jitter1_min_sample = 0.0;
\r
2212 static f32 jitter1_max = 0.0;
\r
2213 static f32 jitter1_min = 0.0;
\r
2214 static f32 counter = 0.0;
\r
2215 if(busytime_jitter1 > jitter1_max)
\r
2216 jitter1_max = busytime_jitter1;
\r
2217 if(busytime_jitter1 < jitter1_min)
\r
2218 jitter1_min = busytime_jitter1;
\r
2220 if(counter > 0.0){
\r
2222 busytime_jitter1_max_sample = jitter1_max;
\r
2223 busytime_jitter1_min_sample = jitter1_min;
\r
2224 jitter1_max = 0.0;
\r
2225 jitter1_min = 0.0;
\r
2230 Debug info for client
\r
2233 static float counter = 0.0;
\r
2238 client.printDebugInfo(std::cout);
\r
2243 Input handler step()
\r
2245 g_input->step(dtime);
\r
2248 Player speed control
\r
2257 bool a_superspeed,
\r
2260 PlayerControl control(
\r
2261 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2262 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2263 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2264 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2265 g_input->isKeyDown(irr::KEY_SPACE),
\r
2266 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2270 client.setPlayerControl(control);
\r
2274 Process environment
\r
2278 //TimeTaker timer("client.step(dtime)");
\r
2279 client.step(dtime);
\r
2280 //client.step(dtime_avg1);
\r
2283 if(server != NULL)
\r
2285 //TimeTaker timer("server->step(dtime)");
\r
2286 server->step(dtime);
\r
2289 v3f player_position = client.getPlayerPosition();
\r
2291 //TimeTaker //timer2("//timer2");
\r
2294 Mouse and camera control
\r
2297 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2300 device->getCursorControl()->setVisible(false);
\r
2302 if(first_loop_after_window_activation){
\r
2303 //std::cout<<"window active, first loop"<<std::endl;
\r
2304 first_loop_after_window_activation = false;
\r
2307 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2308 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2309 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2310 camera_yaw -= dx*0.2;
\r
2311 camera_pitch += dy*0.2;
\r
2312 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2313 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2315 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2318 device->getCursorControl()->setVisible(true);
\r
2320 //std::cout<<"window inactive"<<std::endl;
\r
2321 first_loop_after_window_activation = true;
\r
2324 camera_yaw = wrapDegrees(camera_yaw);
\r
2325 camera_pitch = wrapDegrees(camera_pitch);
\r
2327 v3f camera_direction = v3f(0,0,1);
\r
2328 camera_direction.rotateYZBy(camera_pitch);
\r
2329 camera_direction.rotateXZBy(camera_yaw);
\r
2331 // This is at the height of the eyes of the current figure
\r
2332 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2333 // This is more like in minecraft
\r
2334 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2336 camera->setPosition(camera_position);
\r
2337 // *100.0 helps in large map coordinates
\r
2338 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2340 if(FIELD_OF_VIEW_TEST){
\r
2341 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2342 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2345 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2346 //TimeTaker timer("client.updateCamera");
\r
2347 client.updateCamera(camera_position, camera_direction);
\r
2351 //TimeTaker //timer3("//timer3");
\r
2354 Calculate what block is the crosshair pointing to
\r
2357 //u32 t1 = device->getTimer()->getRealTime();
\r
2359 //f32 d = 4; // max. distance
\r
2360 f32 d = 4; // max. distance
\r
2361 core::line3d<f32> shootline(camera_position,
\r
2362 camera_position + camera_direction * BS * (d+1));
\r
2364 MapBlockObject *selected_object = client.getSelectedObject
\r
2365 (d*BS, camera_position, shootline);
\r
2368 If it's pointing to a MapBlockObject
\r
2371 if(selected_object != NULL)
\r
2373 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2375 core::aabbox3d<f32> box_on_map
\r
2376 = selected_object->getSelectionBoxOnMap();
\r
2378 hilightboxes.push_back(box_on_map);
\r
2380 infotext = narrow_to_wide(selected_object->infoText());
\r
2382 if(g_input->getLeftClicked())
\r
2384 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2385 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2386 selected_object->getId(), g_selected_item);
\r
2388 else if(g_input->getRightClicked())
\r
2390 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2392 Check if we want to modify the object ourselves
\r
2394 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2396 dstream<<"Sign object right-clicked"<<std::endl;
\r
2398 if(random_input == false)
\r
2400 // Get a new text for it
\r
2402 TextDest *dest = new TextDestSign(
\r
2403 selected_object->getBlock()->getPos(),
\r
2404 selected_object->getId(),
\r
2407 SignObject *sign_object = (SignObject*)selected_object;
\r
2409 std::wstring wtext =
\r
2410 narrow_to_wide(sign_object->getText());
\r
2412 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2418 Otherwise pass the event to the server as-is
\r
2422 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2423 selected_object->getId(), g_selected_item);
\r
2427 else // selected_object == NULL
\r
2431 Find out which node we are pointing at
\r
2434 bool nodefound = false;
\r
2436 v3s16 neighbourpos;
\r
2437 core::aabbox3d<f32> nodehilightbox;
\r
2438 f32 mindistance = BS * 1001;
\r
2440 v3s16 pos_i = floatToInt(player_position);
\r
2442 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2446 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2447 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2448 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2449 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2450 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2451 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2453 for(s16 y = ystart; y <= yend; y++)
\r
2454 for(s16 z = zstart; z <= zend; z++)
\r
2455 for(s16 x = xstart; x <= xend; x++)
\r
2460 n = client.getNode(v3s16(x,y,z));
\r
2461 if(content_pointable(n.d) == false)
\r
2464 catch(InvalidPositionException &e)
\r
2470 v3f npf = intToFloat(np);
\r
2475 v3s16(0,0,1), // back
\r
2476 v3s16(0,1,0), // top
\r
2477 v3s16(1,0,0), // right
\r
2478 v3s16(0,0,-1), // front
\r
2479 v3s16(0,-1,0), // bottom
\r
2480 v3s16(-1,0,0), // left
\r
2486 if(n.d == CONTENT_TORCH)
\r
2488 v3s16 dir = unpackDir(n.dir);
\r
2489 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2490 dir_f *= BS/2 - BS/6 - BS/20;
\r
2491 v3f cpf = npf + dir_f;
\r
2492 f32 distance = (cpf - camera_position).getLength();
\r
2494 core::aabbox3d<f32> box;
\r
2497 if(dir == v3s16(0,-1,0))
\r
2499 box = core::aabbox3d<f32>(
\r
2500 npf - v3f(BS/6, BS/2, BS/6),
\r
2501 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2505 else if(dir == v3s16(0,1,0))
\r
2507 box = core::aabbox3d<f32>(
\r
2508 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2509 npf + v3f(BS/6, BS/2, BS/6)
\r
2515 box = core::aabbox3d<f32>(
\r
2516 cpf - v3f(BS/6, BS/3, BS/6),
\r
2517 cpf + v3f(BS/6, BS/3, BS/6)
\r
2521 if(distance < mindistance)
\r
2523 if(box.intersectsWithLine(shootline))
\r
2527 neighbourpos = np;
\r
2528 mindistance = distance;
\r
2529 nodehilightbox = box;
\r
2538 for(u16 i=0; i<6; i++)
\r
2540 v3f dir_f = v3f(dirs[i].X,
\r
2541 dirs[i].Y, dirs[i].Z);
\r
2542 v3f centerpoint = npf + dir_f * BS/2;
\r
2544 (centerpoint - camera_position).getLength();
\r
2546 if(distance < mindistance)
\r
2548 core::CMatrix4<f32> m;
\r
2549 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2551 // This is the back face
\r
2552 v3f corners[2] = {
\r
2553 v3f(BS/2, BS/2, BS/2),
\r
2554 v3f(-BS/2, -BS/2, BS/2+d)
\r
2557 for(u16 j=0; j<2; j++)
\r
2559 m.rotateVect(corners[j]);
\r
2560 corners[j] += npf;
\r
2563 core::aabbox3d<f32> facebox(corners[0]);
\r
2564 facebox.addInternalPoint(corners[1]);
\r
2566 if(facebox.intersectsWithLine(shootline))
\r
2570 neighbourpos = np + dirs[i];
\r
2571 mindistance = distance;
\r
2573 //nodehilightbox = facebox;
\r
2575 const float d = 0.502;
\r
2576 core::aabbox3d<f32> nodebox
\r
2577 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2578 v3f nodepos_f = intToFloat(nodepos);
\r
2579 nodebox.MinEdge += nodepos_f;
\r
2580 nodebox.MaxEdge += nodepos_f;
\r
2581 nodehilightbox = nodebox;
\r
2583 } // if distance < mindistance
\r
2585 } // regular block
\r
2588 static float nodig_delay_counter = 0.0;
\r
2592 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2594 static float dig_time = 0.0;
\r
2595 static u16 dig_index = 0;
\r
2597 // Visualize selection
\r
2599 hilightboxes.push_back(nodehilightbox);
\r
2603 if(g_input->getLeftReleased())
\r
2605 client.clearTempMod(nodepos);
\r
2609 if(nodig_delay_counter > 0.0)
\r
2611 nodig_delay_counter -= dtime;
\r
2615 if(nodepos != nodepos_old)
\r
2617 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2618 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2620 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2622 client.clearTempMod(nodepos_old);
\r
2627 if(g_input->getLeftClicked() ||
\r
2628 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2630 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2631 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2633 if(g_input->getLeftClicked())
\r
2635 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2637 if(g_input->getLeftState())
\r
2639 MapNode n = client.getNode(nodepos);
\r
2641 // Get tool name. Default is "" = bare hands
\r
2642 std::string toolname = "";
\r
2643 InventoryList *mlist = local_inventory.getList("main");
\r
2646 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2647 if(item && (std::string)item->getName() == "ToolItem")
\r
2649 ToolItem *titem = (ToolItem*)item;
\r
2650 toolname = titem->getToolName();
\r
2654 // Get digging properties for material and tool
\r
2655 u8 material = n.d;
\r
2656 DiggingProperties prop =
\r
2657 getDiggingProperties(material, toolname);
\r
2659 float dig_time_complete = 0.0;
\r
2661 if(prop.diggable == false)
\r
2663 /*dstream<<"Material "<<(int)material
\r
2664 <<" not diggable with \""
\r
2665 <<toolname<<"\""<<std::endl;*/
\r
2666 // I guess nobody will wait for this long
\r
2667 dig_time_complete = 10000000.0;
\r
2671 dig_time_complete = prop.time;
\r
2674 if(dig_time_complete >= 0.001)
\r
2676 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2677 * dig_time/dig_time_complete);
\r
2679 // This is for torches
\r
2682 dig_index = CRACK_ANIMATION_LENGTH;
\r
2685 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2687 //TimeTaker timer("client.setTempMod");
\r
2688 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2689 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2693 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2694 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2695 client.clearTempMod(nodepos);
\r
2696 client.removeNode(nodepos);
\r
2700 nodig_delay_counter = dig_time_complete
\r
2701 / (float)CRACK_ANIMATION_LENGTH;
\r
2703 // We don't want a corresponding delay to
\r
2704 // very time consuming nodes
\r
2705 if(nodig_delay_counter > 0.5)
\r
2707 nodig_delay_counter = 0.5;
\r
2709 // We want a slight delay to very little
\r
2710 // time consuming nodes
\r
2711 float mindelay = 0.15;
\r
2712 if(nodig_delay_counter < mindelay)
\r
2714 nodig_delay_counter = mindelay;
\r
2718 dig_time += dtime;
\r
2722 if(g_input->getRightClicked())
\r
2724 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2725 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2728 nodepos_old = nodepos;
\r
2733 } // selected_object == NULL
\r
2735 g_input->resetLeftClicked();
\r
2736 g_input->resetRightClicked();
\r
2738 if(g_input->getLeftReleased())
\r
2740 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2742 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2744 if(g_input->getRightReleased())
\r
2746 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2750 g_input->resetLeftReleased();
\r
2751 g_input->resetRightReleased();
\r
2754 Calculate stuff for drawing
\r
2757 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2759 u32 daynight_ratio = client.getDayNightRatio();
\r
2760 /*video::SColor bgcolor = video::SColor(
\r
2762 skycolor.getRed() * daynight_ratio / 1000,
\r
2763 skycolor.getGreen() * daynight_ratio / 1000,
\r
2764 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2766 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2767 video::SColor bgcolor = video::SColor(
\r
2769 skycolor.getRed() * l / 255,
\r
2770 skycolor.getGreen() * l / 255,
\r
2771 skycolor.getBlue() * l / 255);
\r
2777 if(g_settings.getBool("enable_fog") == true)
\r
2779 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2780 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2781 if(draw_control.range_all)
\r
2782 range = 100000*BS;
\r
2786 video::EFT_FOG_LINEAR,
\r
2790 false, // pixel fog
\r
2791 false // range fog
\r
2797 Update gui stuff (0ms)
\r
2800 //TimeTaker guiupdatetimer("Gui updating");
\r
2803 wchar_t temptext[150];
\r
2805 static float drawtime_avg = 0;
\r
2806 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2807 static float beginscenetime_avg = 0;
\r
2808 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2809 static float scenetime_avg = 0;
\r
2810 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2811 static float endscenetime_avg = 0;
\r
2812 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2814 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2816 L", R: range_all=%i"
\r
2818 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2820 draw_control.range_all,
\r
2822 beginscenetime_avg,
\r
2827 guitext->setText(temptext);
\r
2831 wchar_t temptext[150];
\r
2832 swprintf(temptext, 150,
\r
2833 L"(% .1f, % .1f, % .1f)"
\r
2834 L" (% .3f < btime_jitter < % .3f"
\r
2835 L", dtime_jitter = % .1f %%"
\r
2836 L", v_range = %.1f)",
\r
2837 player_position.X/BS,
\r
2838 player_position.Y/BS,
\r
2839 player_position.Z/BS,
\r
2840 busytime_jitter1_min_sample,
\r
2841 busytime_jitter1_max_sample,
\r
2842 dtime_jitter1_max_fraction * 100.0,
\r
2843 draw_control.wanted_range
\r
2846 guitext2->setText(temptext);
\r
2850 guitext_info->setText(infotext.c_str());
\r
2854 Get chat messages from client
\r
2857 // Get new messages
\r
2858 std::wstring message;
\r
2859 while(client.getChatMessage(message))
\r
2861 chat_lines.push_back(ChatLine(message));
\r
2862 /*if(chat_lines.size() > 6)
\r
2864 core::list<ChatLine>::Iterator
\r
2865 i = chat_lines.begin();
\r
2866 chat_lines.erase(i);
\r
2869 // Append them to form the whole static text and throw
\r
2870 // it to the gui element
\r
2871 std::wstring whole;
\r
2872 // This will correspond to the line number counted from
\r
2873 // top to bottom, from size-1 to 0
\r
2874 s16 line_number = chat_lines.size();
\r
2875 // Count of messages to be removed from the top
\r
2876 u16 to_be_removed_count = 0;
\r
2877 for(core::list<ChatLine>::Iterator
\r
2878 i = chat_lines.begin();
\r
2879 i != chat_lines.end(); i++)
\r
2881 // After this, line number is valid for this loop
\r
2884 (*i).age += dtime;
\r
2886 This results in a maximum age of 60*6 to the
\r
2887 lowermost line and a maximum of 6 lines
\r
2889 float allowed_age = (6-line_number) * 60.0;
\r
2891 if((*i).age > allowed_age)
\r
2893 to_be_removed_count++;
\r
2896 whole += (*i).text + L'\n';
\r
2898 for(u16 i=0; i<to_be_removed_count; i++)
\r
2900 core::list<ChatLine>::Iterator
\r
2901 it = chat_lines.begin();
\r
2902 chat_lines.erase(it);
\r
2904 guitext_chat->setText(whole.c_str());
\r
2905 // Update gui element size and position
\r
2906 core::rect<s32> rect(
\r
2908 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2909 screensize.X - 10,
\r
2912 guitext_chat->setRelativePosition(rect);
\r
2914 if(chat_lines.size() == 0)
\r
2915 guitext_chat->setVisible(false);
\r
2917 guitext_chat->setVisible(true);
\r
2924 static u16 old_selected_item = 65535;
\r
2925 if(client.getLocalInventoryUpdated()
\r
2926 || g_selected_item != old_selected_item)
\r
2928 old_selected_item = g_selected_item;
\r
2929 //std::cout<<"Updating local inventory"<<std::endl;
\r
2930 client.getLocalInventory(local_inventory);
\r
2931 quick_inventory->setSelection(g_selected_item);
\r
2932 quick_inventory->update();
\r
2936 Send actions returned by the inventory menu
\r
2938 while(inventory_action_queue.size() != 0)
\r
2940 InventoryAction *a = inventory_action_queue.pop_front();
\r
2942 client.sendInventoryAction(a);
\r
2951 TimeTaker drawtimer("Drawing");
\r
2955 TimeTaker timer("beginScene");
\r
2956 driver->beginScene(true, true, bgcolor);
\r
2957 //driver->beginScene(false, true, bgcolor);
\r
2958 beginscenetime = timer.stop(true);
\r
2963 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2966 TimeTaker timer("smgr");
\r
2968 scenetime = timer.stop(true);
\r
2972 //TimeTaker timer9("auxiliary drawings");
\r
2976 //TimeTaker //timer10("//timer10");
\r
2978 video::SMaterial m;
\r
2979 //m.Thickness = 10;
\r
2981 m.Lighting = false;
\r
2982 driver->setMaterial(m);
\r
2984 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2986 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2987 i != hilightboxes.end(); i++)
\r
2989 /*std::cout<<"hilightbox min="
\r
2990 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2992 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2994 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3000 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3001 displaycenter + core::vector2d<s32>(10,0),
\r
3002 video::SColor(255,255,255,255));
\r
3003 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3004 displaycenter + core::vector2d<s32>(0,10),
\r
3005 video::SColor(255,255,255,255));
\r
3010 if(g_settings.getBool("frametime_graph") == true)
\r
3013 for(core::list<float>::Iterator
\r
3014 i = frametime_log.begin();
\r
3015 i != frametime_log.end();
\r
3018 driver->draw2DLine(v2s32(x,50),
\r
3019 v2s32(x,50+(*i)*1000),
\r
3020 video::SColor(255,255,255,255));
\r
3028 //TimeTaker //timer11("//timer11");
\r
3034 guienv->drawAll();
\r
3038 TimeTaker timer("endScene");
\r
3039 driver->endScene();
\r
3040 endscenetime = timer.stop(true);
\r
3043 drawtime = drawtimer.stop(true);
\r
3049 static s16 lastFPS = 0;
\r
3050 //u16 fps = driver->getFPS();
\r
3051 u16 fps = (1.0/dtime_avg1);
\r
3053 if (lastFPS != fps)
\r
3055 core::stringw str = L"Minetest [";
\r
3056 str += driver->getName();
\r
3060 device->setWindowCaption(str.c_str());
\r
3066 device->yield();*/
\r
3069 delete quick_inventory;
\r
3072 Disable texture fetches and other stuff that is queued
\r
3073 to be processed by the main loop.
\r
3075 This has to be done before client goes out of scope.
\r
3077 g_irrlicht->Shutdown(true);
\r
3079 } // client and server are deleted at this point
\r
3082 catch(con::PeerNotFoundException &e)
\r
3084 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3085 error_message = L"Connection timed out.";
\r
3088 } // Menu-game loop
\r
3093 In the end, delete the Irrlicht device.
\r
3098 Update configuration file
\r
3100 /*if(configpath != "")
\r
3102 g_settings.updateConfigFile(configpath.c_str());
\r
3105 END_DEBUG_EXCEPTION_HANDLER
\r
3107 debugstreams_deinit();
\r