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.
\r
85 - Note: This has been changing lately
\r
87 SUGG: A version number to blocks, which increments when the block is
\r
88 modified (node add/remove, water update, lighting update)
\r
89 - This can then be used to make sure the most recent version of
\r
90 a block has been sent to client
\r
92 SUGG: Make the amount of blocks sending to client and the total
\r
93 amount of blocks dynamically limited. Transferring blocks is the
\r
94 main network eater of this system, so it is the one that has
\r
95 to be throttled so that RTTs stay low.
\r
97 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
98 different directions and then only those drawn that need to be
\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 * Make server find the spawning place from the real map data, not from
\r
273 - But the changing borders of chunk have to be avoided, because
\r
274 there is time to generate only one chunk.
\r
275 * Make the generator to run in background and not blocking block
\r
276 placement and transfer
\r
277 * only_from_disk might not work anymore - check and fix it.
\r
278 * Check the fixmes in the list above
\r
280 === Making it more portable
\r
281 * Some MSVC: std::sto* are defined without a namespace and collide
\r
282 with the ones in utility.h
\r
283 * On Kray's machine, the new find_library(XXF86VM_LIBRARY, Xxf86vm)
\r
284 line doesn't find the library.
\r
286 === Stuff to do after release
\r
287 * Make an "environment metafile" to store at least time of day
\r
288 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
289 - Or maybe move content_features to material.{h,cpp}?
\r
290 * Add some kind of erosion and other stuff that now is possible
\r
291 * Make client to fetch stuff asynchronously
\r
292 - Needs method SyncProcessData
\r
293 * Fix the problem with the server constantly saving one or a few
\r
294 blocks? List the first saved block, maybe it explains.
\r
295 - It is probably caused by oscillating water
\r
296 * Water doesn't start flowing after map generation like it should
\r
297 - Are there still problems?
\r
298 * Better water generation (spread it to underwater caverns but don't
\r
299 fill dungeons that don't touch big water masses)
\r
300 * When generating a chunk and the neighboring chunk doesn't have mud
\r
301 and stuff yet and the ground is fairly flat, the mud will flow to
\r
302 the other chunk making nasty straight walls when the other chunk
\r
303 is generated. Fix it.
\r
304 * Make a small history check to transformLiquids to detect and log
\r
305 continuous oscillations, in such detail that they can be fixed.
\r
306 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
308 ======================================================================
\r
313 Setting this to 1 enables a special camera mode that forces
\r
314 the renderers to think that the camera statically points from
\r
315 the starting place to a static direction.
\r
317 This allows one to move around with the player and see what
\r
318 is actually drawn behind solid things and behind the player.
\r
320 #define FIELD_OF_VIEW_TEST 0
\r
324 #pragma message ("Disabling unit tests")
\r
326 #warning "Disabling unit tests"
\r
328 // Disable unit tests
\r
329 #define ENABLE_TESTS 0
\r
331 // Enable unit tests
\r
332 #define ENABLE_TESTS 1
\r
336 #pragma comment(lib, "Irrlicht.lib")
\r
337 //#pragma comment(lib, "jthread.lib")
\r
338 #pragma comment(lib, "zlibwapi.lib")
\r
339 #pragma comment(lib, "Shell32.lib")
\r
340 // This would get rid of the console window
\r
341 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
344 #include <iostream>
\r
346 #include <jmutexautolock.h>
\r
347 #include <locale.h>
\r
348 #include "common_irrlicht.h"
\r
351 #include "player.h"
\r
354 #include "environment.h"
\r
355 #include "server.h"
\r
356 #include "client.h"
\r
357 #include "serialization.h"
\r
358 #include "constants.h"
\r
359 #include "strfnd.h"
\r
360 #include "porting.h"
\r
361 #include "irrlichtwrapper.h"
\r
362 #include "gettime.h"
\r
363 #include "porting.h"
\r
364 #include "guiPauseMenu.h"
\r
365 #include "guiInventoryMenu.h"
\r
366 #include "guiTextInputMenu.h"
\r
367 #include "materials.h"
\r
368 #include "guiMessageMenu.h"
\r
369 #include "filesys.h"
\r
370 #include "config.h"
\r
371 #include "guiMainMenu.h"
\r
372 #include "mineral.h"
\r
376 // TODO: Remove this
\r
377 IrrlichtWrapper *g_irrlicht = NULL;
\r
379 // This makes textures
\r
380 ITextureSource *g_texturesource = NULL;
\r
382 MapDrawControl draw_control;
\r
386 These are loaded from the config file.
\r
389 Settings g_settings;
\r
391 extern void set_default_settings();
\r
397 IrrlichtDevice *g_device = NULL;
\r
398 Client *g_client = NULL;
\r
404 gui::IGUIEnvironment* guienv = NULL;
\r
405 gui::IGUIStaticText *guiroot = NULL;
\r
407 class MainMenuManager : public IMenuManager
\r
410 virtual void createdMenu(GUIModalMenu *menu)
\r
412 for(core::list<GUIModalMenu*>::Iterator
\r
413 i = m_stack.begin();
\r
414 i != m_stack.end(); i++)
\r
416 assert(*i != menu);
\r
419 if(m_stack.size() != 0)
\r
420 (*m_stack.getLast())->setVisible(false);
\r
421 m_stack.push_back(menu);
\r
424 virtual void deletingMenu(GUIModalMenu *menu)
\r
426 // Remove all entries if there are duplicates
\r
427 bool removed_entry;
\r
429 removed_entry = false;
\r
430 for(core::list<GUIModalMenu*>::Iterator
\r
431 i = m_stack.begin();
\r
432 i != m_stack.end(); i++)
\r
437 removed_entry = true;
\r
441 }while(removed_entry);
\r
443 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
444 assert(*i == menu);
\r
445 m_stack.erase(i);*/
\r
447 if(m_stack.size() != 0)
\r
448 (*m_stack.getLast())->setVisible(true);
\r
453 return m_stack.size();
\r
456 core::list<GUIModalMenu*> m_stack;
\r
459 MainMenuManager g_menumgr;
\r
461 bool noMenuActive()
\r
463 return (g_menumgr.menuCount() == 0);
\r
466 bool g_disconnect_requested = false;
\r
468 class MainGameCallback : public IGameCallback
\r
471 virtual void exitToOS()
\r
473 g_device->closeDevice();
\r
476 virtual void disconnect()
\r
478 g_disconnect_requested = true;
\r
482 MainGameCallback g_gamecallback;
\r
484 // Inventory actions from the menu are buffered here before sending
\r
485 Queue<InventoryAction*> inventory_action_queue;
\r
486 // This is a copy of the inventory that the client's environment has
\r
487 Inventory local_inventory;
\r
489 u16 g_selected_item = 0;
\r
496 std::ostream *dout_con_ptr = &dummyout;
\r
497 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
498 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
499 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
500 //std::ostream *dout_con_ptr = &dstream;
\r
501 //std::ostream *derr_con_ptr = &dstream;
\r
504 std::ostream *dout_server_ptr = &dstream;
\r
505 std::ostream *derr_server_ptr = &dstream;
\r
508 std::ostream *dout_client_ptr = &dstream;
\r
509 std::ostream *derr_client_ptr = &dstream;
\r
512 gettime.h implementation
\r
518 Use irrlicht because it is more precise than porting.h's
\r
521 if(g_irrlicht == NULL)
\r
523 return g_irrlicht->getTime();
\r
530 struct TextDestSign : public TextDest
\r
532 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
534 m_blockpos = blockpos;
\r
538 void gotText(std::wstring text)
\r
540 std::string ntext = wide_to_narrow(text);
\r
541 dstream<<"Changing text of a sign object: "
\r
542 <<ntext<<std::endl;
\r
543 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
551 struct TextDestChat : public TextDest
\r
553 TextDestChat(Client *client)
\r
557 void gotText(std::wstring text)
\r
559 m_client->sendChatMessage(text);
\r
560 m_client->addChatMessage(text);
\r
566 class MyEventReceiver : public IEventReceiver
\r
569 // This is the one method that we have to implement
\r
570 virtual bool OnEvent(const SEvent& event)
\r
573 React to nothing here if a menu is active
\r
575 if(noMenuActive() == false)
\r
581 // Remember whether each key is down or up
\r
582 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
584 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
586 if(event.KeyInput.PressedDown)
\r
588 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
594 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
596 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
598 dstream<<DTIME<<"MyEventReceiver: "
\r
599 <<"Launching pause menu"<<std::endl;
\r
600 // It will delete itself by itself
\r
601 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
602 &g_menumgr))->drop();
\r
605 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
607 dstream<<DTIME<<"MyEventReceiver: "
\r
608 <<"Launching inventory"<<std::endl;
\r
609 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
610 &local_inventory, &inventory_action_queue,
\r
611 &g_menumgr))->drop();
\r
614 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
616 TextDest *dest = new TextDestChat(g_client);
\r
618 (new GUITextInputMenu(guienv, guiroot, -1,
\r
624 // Material selection
\r
625 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
627 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
630 g_selected_item = 0;
\r
631 dstream<<DTIME<<"Selected item: "
\r
632 <<g_selected_item<<std::endl;
\r
635 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
636 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
638 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
639 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
641 if(s1 < PLAYER_INVENTORY_SIZE)
\r
642 g_selected_item = s1-1;
\r
643 dstream<<DTIME<<"Selected item: "
\r
644 <<g_selected_item<<std::endl;
\r
647 // Viewing range selection
\r
648 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
650 if(draw_control.range_all)
\r
652 draw_control.range_all = false;
\r
653 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
657 draw_control.range_all = true;
\r
658 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
662 // Print debug stacks
\r
663 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
665 dstream<<"-----------------------------------------"
\r
667 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
668 dstream<<"-----------------------------------------"
\r
670 debug_stacks_print();
\r
675 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
677 if(noMenuActive() == false)
\r
679 left_active = false;
\r
680 middle_active = false;
\r
681 right_active = false;
\r
685 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
686 left_active = event.MouseInput.isLeftPressed();
\r
687 middle_active = event.MouseInput.isMiddlePressed();
\r
688 right_active = event.MouseInput.isRightPressed();
\r
690 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
692 leftclicked = true;
\r
694 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
696 rightclicked = true;
\r
698 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
700 leftreleased = true;
\r
702 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
704 rightreleased = true;
\r
706 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
708 /*dstream<<"event.MouseInput.Wheel="
\r
709 <<event.MouseInput.Wheel<<std::endl;*/
\r
710 if(event.MouseInput.Wheel < 0)
\r
712 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
715 g_selected_item = 0;
\r
717 else if(event.MouseInput.Wheel > 0)
\r
719 if(g_selected_item > 0)
\r
722 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
731 // This is used to check whether a key is being held down
\r
732 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
734 return keyIsDown[keyCode];
\r
739 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
740 keyIsDown[i] = false;
\r
742 leftclicked = false;
\r
743 rightclicked = false;
\r
744 leftreleased = false;
\r
745 rightreleased = false;
\r
747 left_active = false;
\r
748 middle_active = false;
\r
749 right_active = false;
\r
760 bool rightreleased;
\r
763 bool middle_active;
\r
767 // We use this array to store the current state of each key
\r
768 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
771 IrrlichtDevice *m_device;
\r
780 virtual ~InputHandler()
\r
784 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
786 virtual v2s32 getMousePos() = 0;
\r
787 virtual void setMousePos(s32 x, s32 y) = 0;
\r
789 virtual bool getLeftState() = 0;
\r
790 virtual bool getRightState() = 0;
\r
792 virtual bool getLeftClicked() = 0;
\r
793 virtual bool getRightClicked() = 0;
\r
794 virtual void resetLeftClicked() = 0;
\r
795 virtual void resetRightClicked() = 0;
\r
797 virtual bool getLeftReleased() = 0;
\r
798 virtual bool getRightReleased() = 0;
\r
799 virtual void resetLeftReleased() = 0;
\r
800 virtual void resetRightReleased() = 0;
\r
802 virtual void step(float dtime) {};
\r
804 virtual void clear() {};
\r
807 InputHandler *g_input = NULL;
\r
809 class RealInputHandler : public InputHandler
\r
812 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
814 m_receiver(receiver)
\r
817 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
819 return m_receiver->IsKeyDown(keyCode);
\r
821 virtual v2s32 getMousePos()
\r
823 return m_device->getCursorControl()->getPosition();
\r
825 virtual void setMousePos(s32 x, s32 y)
\r
827 m_device->getCursorControl()->setPosition(x, y);
\r
830 virtual bool getLeftState()
\r
832 return m_receiver->left_active;
\r
834 virtual bool getRightState()
\r
836 return m_receiver->right_active;
\r
839 virtual bool getLeftClicked()
\r
841 return m_receiver->leftclicked;
\r
843 virtual bool getRightClicked()
\r
845 return m_receiver->rightclicked;
\r
847 virtual void resetLeftClicked()
\r
849 m_receiver->leftclicked = false;
\r
851 virtual void resetRightClicked()
\r
853 m_receiver->rightclicked = false;
\r
856 virtual bool getLeftReleased()
\r
858 return m_receiver->leftreleased;
\r
860 virtual bool getRightReleased()
\r
862 return m_receiver->rightreleased;
\r
864 virtual void resetLeftReleased()
\r
866 m_receiver->leftreleased = false;
\r
868 virtual void resetRightReleased()
\r
870 m_receiver->rightreleased = false;
\r
875 resetRightClicked();
\r
876 resetLeftClicked();
\r
879 IrrlichtDevice *m_device;
\r
880 MyEventReceiver *m_receiver;
\r
883 class RandomInputHandler : public InputHandler
\r
886 RandomInputHandler()
\r
888 leftclicked = false;
\r
889 rightclicked = false;
\r
890 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
891 keydown[i] = false;
\r
893 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
895 return keydown[keyCode];
\r
897 virtual v2s32 getMousePos()
\r
901 virtual void setMousePos(s32 x, s32 y)
\r
903 mousepos = v2s32(x,y);
\r
906 virtual bool getLeftState()
\r
910 virtual bool getRightState()
\r
915 virtual bool getLeftClicked()
\r
917 return leftclicked;
\r
919 virtual bool getRightClicked()
\r
921 return rightclicked;
\r
923 virtual void resetLeftClicked()
\r
925 leftclicked = false;
\r
927 virtual void resetRightClicked()
\r
929 rightclicked = false;
\r
932 virtual bool getLeftReleased()
\r
936 virtual bool getRightReleased()
\r
940 virtual void resetLeftReleased()
\r
943 virtual void resetRightReleased()
\r
947 virtual void step(float dtime)
\r
950 static float counter1 = 0;
\r
954 counter1 = 0.1*Rand(1,10);
\r
955 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
956 g_selected_material++;
\r
958 g_selected_material = 0;*/
\r
959 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
962 g_selected_item = 0;
\r
966 static float counter1 = 0;
\r
970 counter1 = 0.1*Rand(1, 40);
\r
971 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
975 static float counter1 = 0;
\r
979 counter1 = 0.1*Rand(1, 40);
\r
980 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
984 static float counter1 = 0;
\r
988 counter1 = 0.1*Rand(1, 40);
\r
989 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
993 static float counter1 = 0;
\r
997 counter1 = 0.1*Rand(1, 40);
\r
998 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1002 static float counter1 = 0;
\r
1003 counter1 -= dtime;
\r
1004 if(counter1 < 0.0)
\r
1006 counter1 = 0.1*Rand(1, 20);
\r
1007 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1011 static float counter1 = 0;
\r
1012 counter1 -= dtime;
\r
1013 if(counter1 < 0.0)
\r
1015 counter1 = 0.1*Rand(1, 30);
\r
1016 leftclicked = true;
\r
1020 static float counter1 = 0;
\r
1021 counter1 -= dtime;
\r
1022 if(counter1 < 0.0)
\r
1024 counter1 = 0.1*Rand(1, 20);
\r
1025 rightclicked = true;
\r
1028 mousepos += mousespeed;
\r
1031 s32 Rand(s32 min, s32 max)
\r
1033 return (myrand()%(max-min+1))+min;
\r
1036 bool keydown[KEY_KEY_CODES_COUNT];
\r
1040 bool rightclicked;
\r
1043 void updateViewingRange(f32 frametime_in, Client *client)
\r
1045 if(draw_control.range_all == true)
\r
1048 static f32 added_frametime = 0;
\r
1049 static s16 added_frames = 0;
\r
1051 added_frametime += frametime_in;
\r
1052 added_frames += 1;
\r
1054 // Actually this counter kind of sucks because frametime is busytime
\r
1055 static f32 counter = 0;
\r
1056 counter -= frametime_in;
\r
1062 /*dstream<<__FUNCTION_NAME
\r
1063 <<": Collected "<<added_frames<<" frames, total of "
\r
1064 <<added_frametime<<"s."<<std::endl;*/
\r
1066 /*dstream<<"draw_control.blocks_drawn="
\r
1067 <<draw_control.blocks_drawn
\r
1068 <<", draw_control.blocks_would_have_drawn="
\r
1069 <<draw_control.blocks_would_have_drawn
\r
1072 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1073 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1075 draw_control.wanted_min_range = range_min;
\r
1076 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1078 float block_draw_ratio = 1.0;
\r
1079 if(draw_control.blocks_would_have_drawn != 0)
\r
1081 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1082 / (float)draw_control.blocks_would_have_drawn;
\r
1085 // Calculate the average frametime in the case that all wanted
\r
1086 // blocks had been drawn
\r
1087 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1089 added_frametime = 0.0;
\r
1092 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1093 float wanted_frametime = 1.0 / wanted_fps;
\r
1095 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1096 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1098 // If needed frametime change is very small, just return
\r
1099 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1101 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1105 float range = draw_control.wanted_range;
\r
1106 float new_range = range;
\r
1108 static s16 range_old = 0;
\r
1109 static f32 frametime_old = 0;
\r
1111 float d_range = range - range_old;
\r
1112 f32 d_frametime = frametime - frametime_old;
\r
1113 // A sane default of 30ms per 50 nodes of range
\r
1114 static f32 time_per_range = 30. / 50;
\r
1117 time_per_range = d_frametime / d_range;
\r
1120 // The minimum allowed calculated frametime-range derivative:
\r
1121 // Practically this sets the maximum speed of changing the range.
\r
1122 // The lower this value, the higher the maximum changing speed.
\r
1123 // A low value here results in wobbly range (0.001)
\r
1124 // A high value here results in slow changing range (0.0025)
\r
1125 // SUGG: This could be dynamically adjusted so that when
\r
1126 // the camera is turning, this is lower
\r
1127 //float min_time_per_range = 0.0015;
\r
1128 float min_time_per_range = 0.0010;
\r
1129 //float min_time_per_range = 0.05 / range;
\r
1130 if(time_per_range < min_time_per_range)
\r
1132 time_per_range = min_time_per_range;
\r
1133 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1137 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1140 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1141 // Dampen the change a bit to kill oscillations
\r
1142 //wanted_range_change *= 0.9;
\r
1143 //wanted_range_change *= 0.75;
\r
1144 wanted_range_change *= 0.5;
\r
1145 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1147 // If needed range change is very small, just return
\r
1148 if(fabs(wanted_range_change) < 0.001)
\r
1150 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1154 new_range += wanted_range_change;
\r
1155 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1157 //float new_range_unclamped = new_range;
\r
1158 if(new_range < range_min)
\r
1159 new_range = range_min;
\r
1160 if(new_range > range_max)
\r
1161 new_range = range_max;
\r
1163 /*if(new_range != new_range_unclamped)
\r
1164 dstream<<", clamped to "<<new_range<<std::endl;
\r
1166 dstream<<std::endl;*/
\r
1168 draw_control.wanted_range = new_range;
\r
1170 range_old = new_range;
\r
1171 frametime_old = frametime;
\r
1174 class GUIQuickInventory : public IEventReceiver
\r
1177 GUIQuickInventory(
\r
1178 gui::IGUIEnvironment* env,
\r
1179 gui::IGUIElement* parent,
\r
1182 Inventory *inventory):
\r
1183 m_itemcount(itemcount),
\r
1184 m_inventory(inventory)
\r
1186 core::rect<s32> imgsize(0,0,48,48);
\r
1187 core::rect<s32> textsize(0,0,48,16);
\r
1188 v2s32 spacing(0, 64);
\r
1189 for(s32 i=0; i<m_itemcount; i++)
\r
1191 m_images.push_back(env->addImage(
\r
1192 imgsize + pos + spacing*i
\r
1194 m_images[i]->setScaleImage(true);
\r
1195 m_texts.push_back(env->addStaticText(
\r
1197 textsize + pos + spacing*i,
\r
1200 m_texts[i]->setBackgroundColor(
\r
1201 video::SColor(128,0,0,0));
\r
1202 m_texts[i]->setTextAlignment(
\r
1203 gui::EGUIA_CENTER,
\r
1204 gui::EGUIA_UPPERLEFT);
\r
1208 ~GUIQuickInventory()
\r
1210 for(u32 i=0; i<m_texts.size(); i++)
\r
1212 m_texts[i]->remove();
\r
1214 for(u32 i=0; i<m_images.size(); i++)
\r
1216 m_images[i]->remove();
\r
1220 virtual bool OnEvent(const SEvent& event)
\r
1225 void setSelection(s32 i)
\r
1234 start = m_selection - m_itemcount / 2;
\r
1236 InventoryList *mainlist = m_inventory->getList("main");
\r
1238 for(s32 i=0; i<m_itemcount; i++)
\r
1240 s32 j = i + start;
\r
1242 if(j > (s32)mainlist->getSize() - 1)
\r
1243 j -= mainlist->getSize();
\r
1245 j += mainlist->getSize();
\r
1247 InventoryItem *item = mainlist->getItem(j);
\r
1251 m_images[i]->setImage(NULL);
\r
1253 if(m_selection == j)
\r
1254 m_texts[i]->setText(L"<-");
\r
1256 m_texts[i]->setText(L"");
\r
1258 // The next ifs will segfault with a NULL pointer
\r
1263 m_images[i]->setImage(item->getImage());
\r
1265 std::ostringstream os;
\r
1266 os<<item->getText();
\r
1267 if(m_selection == j)
\r
1269 m_texts[i]->setText(narrow_to_wide(os.str()).c_str());
\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
1546 // If no path found, use the first one (menu creates the file)
\r
1547 if(configpath == "")
\r
1548 configpath = filenames[0];
\r
1551 // Initialize random seed
\r
1556 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1558 These are needed for unit tests at least.
\r
1561 // Initial call with g_texturesource not set.
\r
1568 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1569 || cmd_args.getFlag("enable-unittests") == true)
\r
1574 /*for(s16 y=-100; y<100; y++)
\r
1575 for(s16 x=-100; x<100; x++)
\r
1577 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1587 if(cmd_args.exists("port"))
\r
1588 port = cmd_args.getU16("port");
\r
1589 else if(cmd_args.exists("port"))
\r
1590 port = g_settings.getU16("port");
\r
1593 std::string map_dir = porting::path_userdata+"/map";
\r
1594 if(cmd_args.exists("map-dir"))
\r
1595 map_dir = cmd_args.get("map-dir");
\r
1596 else if(g_settings.exists("map-dir"))
\r
1597 map_dir = g_settings.get("map-dir");
\r
1599 // Run dedicated server if asked to
\r
1600 if(cmd_args.getFlag("server"))
\r
1602 DSTACK("Dedicated server branch");
\r
1605 Server server(map_dir.c_str());
\r
1606 server.start(port);
\r
1609 dedicated_server_loop(server);
\r
1618 // Address to connect to
\r
1619 std::string address = "";
\r
1621 if(cmd_args.exists("address"))
\r
1623 address = cmd_args.get("address");
\r
1627 address = g_settings.get("address");
\r
1630 std::string playername = g_settings.get("name");
\r
1633 Resolution selection
\r
1636 bool fullscreen = false;
\r
1637 u16 screenW = g_settings.getU16("screenW");
\r
1638 u16 screenH = g_settings.getU16("screenH");
\r
1642 MyEventReceiver receiver;
\r
1644 video::E_DRIVER_TYPE driverType;
\r
1647 //driverType = video::EDT_DIRECT3D9;
\r
1648 driverType = video::EDT_OPENGL;
\r
1650 driverType = video::EDT_OPENGL;
\r
1651 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1654 // create device and exit if creation failed
\r
1656 IrrlichtDevice *device;
\r
1657 device = createDevice(driverType,
\r
1658 core::dimension2d<u32>(screenW, screenH),
\r
1659 16, fullscreen, false, false, &receiver);
\r
1662 return 1; // could not create selected driver.
\r
1664 g_device = device;
\r
1665 g_irrlicht = new IrrlichtWrapper(device);
\r
1666 TextureSource *texturesource = new TextureSource(device);
\r
1667 g_texturesource = texturesource;
\r
1670 Speed tests (done after irrlicht is loaded to get timer)
\r
1672 if(cmd_args.getFlag("speedtests"))
\r
1674 dstream<<"Running speed tests"<<std::endl;
\r
1679 device->setResizable(true);
\r
1681 bool random_input = g_settings.getBool("random_input")
\r
1682 || cmd_args.getFlag("random-input");
\r
1684 g_input = new RandomInputHandler();
\r
1686 g_input = new RealInputHandler(device, &receiver);
\r
1689 Continue initialization
\r
1692 video::IVideoDriver* driver = device->getVideoDriver();
\r
1695 This changes the minimum allowed number of vertices in a VBO.
\r
1698 //driver->setMinHardwareBufferVertexCount(50);
\r
1700 scene::ISceneManager* smgr = device->getSceneManager();
\r
1702 guienv = device->getGUIEnvironment();
\r
1703 gui::IGUISkin* skin = guienv->getSkin();
\r
1704 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1706 skin->setFont(font);
\r
1708 dstream<<"WARNING: Font file was not found."
\r
1709 " Using default font."<<std::endl;
\r
1710 // If font was not found, this will get us one
\r
1711 font = skin->getFont();
\r
1714 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1715 dstream<<"text_height="<<text_height<<std::endl;
\r
1717 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1718 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1719 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1720 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1721 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1722 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1725 Preload some textures and stuff
\r
1728 init_content_inventory_texture_paths();
\r
1729 init_mapnode(); // Second call with g_texturesource set
\r
1737 We need some kind of a root node to be able to add
\r
1738 custom gui elements directly on the screen.
\r
1739 Otherwise they won't be automatically drawn.
\r
1741 guiroot = guienv->addStaticText(L"",
\r
1742 core::rect<s32>(0, 0, 10000, 10000));
\r
1744 // First line of debug text
\r
1745 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1747 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1749 // Second line of debug text
\r
1750 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1752 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1755 // At the middle of the screen
\r
1756 // Object infos are shown in this
\r
1757 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1759 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1763 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1765 core::rect<s32>(0,0,0,0),
\r
1766 false, false); // Disable word wrap as of now
\r
1768 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1769 core::list<ChatLine> chat_lines;
\r
1772 If an error occurs, this is set to something and the
\r
1773 menu-game loop is restarted. It is then displayed before
\r
1776 std::wstring error_message = L"";
\r
1781 while(g_device->run())
\r
1784 // This is used for catching disconnects
\r
1789 Out-of-game menu loop.
\r
1791 Loop quits when menu returns proper parameters.
\r
1795 // Cursor can be non-visible when coming from the game
\r
1796 device->getCursorControl()->setVisible(true);
\r
1797 // Some stuff are left to scene manager when coming from the game
\r
1798 // (map at least?)
\r
1800 // Reset or hide the debug gui texts
\r
1801 guitext->setText(L"Minetest-c55");
\r
1802 guitext2->setVisible(false);
\r
1803 guitext_info->setVisible(false);
\r
1804 guitext_chat->setVisible(false);
\r
1806 // Initialize menu data
\r
1807 MainMenuData menudata;
\r
1808 menudata.address = narrow_to_wide(address);
\r
1809 menudata.name = narrow_to_wide(playername);
\r
1810 menudata.port = narrow_to_wide(itos(port));
\r
1811 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1813 GUIMainMenu *menu =
\r
1814 new GUIMainMenu(guienv, guiroot, -1,
\r
1815 &g_menumgr, &menudata, &g_gamecallback);
\r
1816 menu->allowFocusRemoval(true);
\r
1818 if(error_message != L"")
\r
1820 GUIMessageMenu *menu2 =
\r
1821 new GUIMessageMenu(guienv, guiroot, -1,
\r
1822 &g_menumgr, error_message.c_str());
\r
1824 error_message = L"";
\r
1827 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1829 dstream<<"Created main menu"<<std::endl;
\r
1831 while(g_device->run())
\r
1833 // Run global IrrlichtWrapper's main thread processing stuff
\r
1834 g_irrlicht->Run();
\r
1836 if(menu->getStatus() == true)
\r
1839 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1840 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1841 guienv->drawAll();
\r
1842 driver->endScene();
\r
1845 // Break out of menu-game loop to shut down cleanly
\r
1846 if(g_device->run() == false)
\r
1849 dstream<<"Dropping main menu"<<std::endl;
\r
1853 // Delete map if requested
\r
1854 if(menudata.delete_map)
\r
1856 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1858 error_message = L"Delete failed";
\r
1862 playername = wide_to_narrow(menudata.name);
\r
1863 address = wide_to_narrow(menudata.address);
\r
1864 port = stoi(wide_to_narrow(menudata.port));
\r
1865 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1867 // Check for valid parameters, restart menu if invalid.
\r
1868 if(playername == "")
\r
1870 error_message = L"Name required.";
\r
1875 g_settings.set("name", playername);
\r
1876 g_settings.set("address", address);
\r
1877 g_settings.set("port", itos(port));
\r
1878 // Update configuration file
\r
1879 if(configpath != "")
\r
1880 g_settings.updateConfigFile(configpath.c_str());
\r
1882 // Continue to game
\r
1886 // Break out of menu-game loop to shut down cleanly
\r
1887 if(g_device->run() == false)
\r
1891 Make a scope here so that the client and the server and other
\r
1892 stuff gets removed when disconnected or the irrlicht device
\r
1897 // This is set to true at the end of the scope
\r
1898 g_irrlicht->Shutdown(false);
\r
1901 Draw "Loading" screen
\r
1903 const wchar_t *text = L"Loading and connecting...";
\r
1904 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1905 core::vector2d<s32> textsize(300, text_height);
\r
1906 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1908 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1909 text, textrect, false, false);
\r
1910 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1912 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1913 guienv->drawAll();
\r
1914 driver->endScene();
\r
1916 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1920 SharedPtr will delete it when it goes out of scope.
\r
1922 SharedPtr<Server> server;
\r
1923 if(address == ""){
\r
1924 server = new Server(map_dir);
\r
1925 server->start(port);
\r
1932 Client client(device, playername.c_str(), draw_control);
\r
1934 g_client = &client;
\r
1936 Address connect_address(0,0,0,0, port);
\r
1939 //connect_address.Resolve("localhost");
\r
1940 connect_address.setAddress(127,0,0,1);
\r
1942 connect_address.Resolve(address.c_str());
\r
1944 catch(ResolveError &e)
\r
1946 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1948 error_message = L"Couldn't resolve address";
\r
1949 gui_loadingtext->remove();
\r
1953 dstream<<DTIME<<"Connecting to server at ";
\r
1954 connect_address.print(&dstream);
\r
1955 dstream<<std::endl;
\r
1956 client.connect(connect_address);
\r
1959 while(client.connectedAndInitialized() == false)
\r
1962 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1963 guienv->drawAll();
\r
1964 driver->endScene();
\r
1966 // Update client and server
\r
1970 if(server != NULL)
\r
1971 server->step(0.1);
\r
1977 catch(con::PeerNotFoundException &e)
\r
1979 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1981 error_message = L"Connection timed out.";
\r
1982 gui_loadingtext->remove();
\r
1989 /*scene::ISceneNode* skybox;
\r
1990 skybox = smgr->addSkyBoxSceneNode(
\r
1991 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1992 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1993 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1994 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1995 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1996 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1999 Create the camera node
\r
2002 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2003 0, // Camera parent
\r
2004 v3f(BS*100, BS*2, BS*100), // Look from
\r
2005 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2009 if(camera == NULL)
\r
2012 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2013 video::SColor skycolor = video::SColor(255,166,202,244);
\r
2015 camera->setFOV(FOV_ANGLE);
\r
2017 // Just so big a value that everything rendered is visible
\r
2018 camera->setFarValue(100000*BS);
\r
2020 f32 camera_yaw = 0; // "right/left"
\r
2021 f32 camera_pitch = 0; // "up/down"
\r
2027 gui_loadingtext->remove();
\r
2030 Add some gui stuff
\r
2033 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2034 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2036 // Test the text input system
\r
2037 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2039 /*GUIMessageMenu *menu =
\r
2040 new GUIMessageMenu(guienv, guiroot, -1,
\r
2045 // Launch pause menu
\r
2046 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2047 &g_menumgr))->drop();
\r
2050 guitext2->setVisible(true);
\r
2051 guitext_info->setVisible(true);
\r
2052 guitext_chat->setVisible(true);
\r
2055 Some statistics are collected in these
\r
2058 u32 beginscenetime = 0;
\r
2059 u32 scenetime = 0;
\r
2060 u32 endscenetime = 0;
\r
2063 //throw con::PeerNotFoundException("lol");
\r
2065 core::list<float> frametime_log;
\r
2071 bool first_loop_after_window_activation = true;
\r
2073 // Time is in milliseconds
\r
2074 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2075 // NOTE: So we have to use getTime() and call run()s between them
\r
2076 u32 lasttime = device->getTimer()->getTime();
\r
2078 while(device->run())
\r
2080 if(g_disconnect_requested)
\r
2082 g_disconnect_requested = false;
\r
2087 Run global IrrlichtWrapper's main thread processing stuff
\r
2089 g_irrlicht->Run();
\r
2092 Process TextureSource's queue
\r
2094 texturesource->processQueue();
\r
2097 Random calculations
\r
2099 v2u32 screensize = driver->getScreenSize();
\r
2100 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2102 // Hilight boxes collected during the loop and displayed
\r
2103 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2106 std::wstring infotext;
\r
2108 //TimeTaker //timer1("//timer1");
\r
2110 // Time of frame without fps limit
\r
2114 // not using getRealTime is necessary for wine
\r
2115 u32 time = device->getTimer()->getTime();
\r
2116 if(time > lasttime)
\r
2117 busytime_u32 = time - lasttime;
\r
2120 busytime = busytime_u32 / 1000.0;
\r
2123 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2125 // Absolutelu necessary for wine!
\r
2132 updateViewingRange(busytime, &client);
\r
2139 float fps_max = g_settings.getFloat("fps_max");
\r
2140 u32 frametime_min = 1000./fps_max;
\r
2142 if(busytime_u32 < frametime_min)
\r
2144 u32 sleeptime = frametime_min - busytime_u32;
\r
2145 device->sleep(sleeptime);
\r
2149 // Absolutelu necessary for wine!
\r
2153 Time difference calculation
\r
2155 f32 dtime; // in seconds
\r
2157 u32 time = device->getTimer()->getTime();
\r
2158 if(time > lasttime)
\r
2159 dtime = (time - lasttime) / 1000.0;
\r
2165 Log frametime for visualization
\r
2167 frametime_log.push_back(dtime);
\r
2168 if(frametime_log.size() > 100)
\r
2170 core::list<float>::Iterator i = frametime_log.begin();
\r
2171 frametime_log.erase(i);
\r
2175 Visualize frametime in terminal
\r
2177 /*for(u32 i=0; i<dtime*400; i++)
\r
2179 std::cout<<std::endl;*/
\r
2182 Time average and jitter calculation
\r
2185 static f32 dtime_avg1 = 0.0;
\r
2186 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2187 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2189 static f32 dtime_jitter1_max_sample = 0.0;
\r
2190 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2192 static f32 jitter1_max = 0.0;
\r
2193 static f32 counter = 0.0;
\r
2194 if(dtime_jitter1 > jitter1_max)
\r
2195 jitter1_max = dtime_jitter1;
\r
2200 dtime_jitter1_max_sample = jitter1_max;
\r
2201 dtime_jitter1_max_fraction
\r
2202 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2203 jitter1_max = 0.0;
\r
2208 Busytime average and jitter calculation
\r
2211 static f32 busytime_avg1 = 0.0;
\r
2212 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2213 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2215 static f32 busytime_jitter1_max_sample = 0.0;
\r
2216 static f32 busytime_jitter1_min_sample = 0.0;
\r
2218 static f32 jitter1_max = 0.0;
\r
2219 static f32 jitter1_min = 0.0;
\r
2220 static f32 counter = 0.0;
\r
2221 if(busytime_jitter1 > jitter1_max)
\r
2222 jitter1_max = busytime_jitter1;
\r
2223 if(busytime_jitter1 < jitter1_min)
\r
2224 jitter1_min = busytime_jitter1;
\r
2226 if(counter > 0.0){
\r
2228 busytime_jitter1_max_sample = jitter1_max;
\r
2229 busytime_jitter1_min_sample = jitter1_min;
\r
2230 jitter1_max = 0.0;
\r
2231 jitter1_min = 0.0;
\r
2236 Debug info for client
\r
2239 static float counter = 0.0;
\r
2244 client.printDebugInfo(std::cout);
\r
2249 Input handler step()
\r
2251 g_input->step(dtime);
\r
2254 Player speed control
\r
2263 bool a_superspeed,
\r
2266 PlayerControl control(
\r
2267 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2268 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2269 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2270 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2271 g_input->isKeyDown(irr::KEY_SPACE),
\r
2272 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2276 client.setPlayerControl(control);
\r
2280 Process environment
\r
2284 //TimeTaker timer("client.step(dtime)");
\r
2285 client.step(dtime);
\r
2286 //client.step(dtime_avg1);
\r
2289 if(server != NULL)
\r
2291 //TimeTaker timer("server->step(dtime)");
\r
2292 server->step(dtime);
\r
2295 v3f player_position = client.getPlayerPosition();
\r
2297 //TimeTaker //timer2("//timer2");
\r
2300 Mouse and camera control
\r
2303 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2306 device->getCursorControl()->setVisible(false);
\r
2308 if(first_loop_after_window_activation){
\r
2309 //std::cout<<"window active, first loop"<<std::endl;
\r
2310 first_loop_after_window_activation = false;
\r
2313 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2314 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2315 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2316 camera_yaw -= dx*0.2;
\r
2317 camera_pitch += dy*0.2;
\r
2318 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2319 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2321 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2324 device->getCursorControl()->setVisible(true);
\r
2326 //std::cout<<"window inactive"<<std::endl;
\r
2327 first_loop_after_window_activation = true;
\r
2330 camera_yaw = wrapDegrees(camera_yaw);
\r
2331 camera_pitch = wrapDegrees(camera_pitch);
\r
2333 v3f camera_direction = v3f(0,0,1);
\r
2334 camera_direction.rotateYZBy(camera_pitch);
\r
2335 camera_direction.rotateXZBy(camera_yaw);
\r
2337 // This is at the height of the eyes of the current figure
\r
2338 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2339 // This is more like in minecraft
\r
2340 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2342 camera->setPosition(camera_position);
\r
2343 // *100.0 helps in large map coordinates
\r
2344 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2346 if(FIELD_OF_VIEW_TEST){
\r
2347 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2348 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2351 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2352 //TimeTaker timer("client.updateCamera");
\r
2353 client.updateCamera(camera_position, camera_direction);
\r
2357 //TimeTaker //timer3("//timer3");
\r
2360 Calculate what block is the crosshair pointing to
\r
2363 //u32 t1 = device->getTimer()->getRealTime();
\r
2365 //f32 d = 4; // max. distance
\r
2366 f32 d = 4; // max. distance
\r
2367 core::line3d<f32> shootline(camera_position,
\r
2368 camera_position + camera_direction * BS * (d+1));
\r
2370 MapBlockObject *selected_object = client.getSelectedObject
\r
2371 (d*BS, camera_position, shootline);
\r
2374 If it's pointing to a MapBlockObject
\r
2377 if(selected_object != NULL)
\r
2379 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2381 core::aabbox3d<f32> box_on_map
\r
2382 = selected_object->getSelectionBoxOnMap();
\r
2384 hilightboxes.push_back(box_on_map);
\r
2386 infotext = narrow_to_wide(selected_object->infoText());
\r
2388 if(g_input->getLeftClicked())
\r
2390 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2391 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2392 selected_object->getId(), g_selected_item);
\r
2394 else if(g_input->getRightClicked())
\r
2396 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2398 Check if we want to modify the object ourselves
\r
2400 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2402 dstream<<"Sign object right-clicked"<<std::endl;
\r
2404 if(random_input == false)
\r
2406 // Get a new text for it
\r
2408 TextDest *dest = new TextDestSign(
\r
2409 selected_object->getBlock()->getPos(),
\r
2410 selected_object->getId(),
\r
2413 SignObject *sign_object = (SignObject*)selected_object;
\r
2415 std::wstring wtext =
\r
2416 narrow_to_wide(sign_object->getText());
\r
2418 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2424 Otherwise pass the event to the server as-is
\r
2428 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2429 selected_object->getId(), g_selected_item);
\r
2433 else // selected_object == NULL
\r
2437 Find out which node we are pointing at
\r
2440 bool nodefound = false;
\r
2442 v3s16 neighbourpos;
\r
2443 core::aabbox3d<f32> nodehilightbox;
\r
2444 f32 mindistance = BS * 1001;
\r
2446 v3s16 pos_i = floatToInt(player_position);
\r
2448 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2452 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2453 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2454 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2455 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2456 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2457 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2459 for(s16 y = ystart; y <= yend; y++)
\r
2460 for(s16 z = zstart; z <= zend; z++)
\r
2461 for(s16 x = xstart; x <= xend; x++)
\r
2466 n = client.getNode(v3s16(x,y,z));
\r
2467 if(content_pointable(n.d) == false)
\r
2470 catch(InvalidPositionException &e)
\r
2476 v3f npf = intToFloat(np);
\r
2481 v3s16(0,0,1), // back
\r
2482 v3s16(0,1,0), // top
\r
2483 v3s16(1,0,0), // right
\r
2484 v3s16(0,0,-1), // front
\r
2485 v3s16(0,-1,0), // bottom
\r
2486 v3s16(-1,0,0), // left
\r
2492 if(n.d == CONTENT_TORCH)
\r
2494 v3s16 dir = unpackDir(n.dir);
\r
2495 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2496 dir_f *= BS/2 - BS/6 - BS/20;
\r
2497 v3f cpf = npf + dir_f;
\r
2498 f32 distance = (cpf - camera_position).getLength();
\r
2500 core::aabbox3d<f32> box;
\r
2503 if(dir == v3s16(0,-1,0))
\r
2505 box = core::aabbox3d<f32>(
\r
2506 npf - v3f(BS/6, BS/2, BS/6),
\r
2507 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2511 else if(dir == v3s16(0,1,0))
\r
2513 box = core::aabbox3d<f32>(
\r
2514 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2515 npf + v3f(BS/6, BS/2, BS/6)
\r
2521 box = core::aabbox3d<f32>(
\r
2522 cpf - v3f(BS/6, BS/3, BS/6),
\r
2523 cpf + v3f(BS/6, BS/3, BS/6)
\r
2527 if(distance < mindistance)
\r
2529 if(box.intersectsWithLine(shootline))
\r
2533 neighbourpos = np;
\r
2534 mindistance = distance;
\r
2535 nodehilightbox = box;
\r
2544 for(u16 i=0; i<6; i++)
\r
2546 v3f dir_f = v3f(dirs[i].X,
\r
2547 dirs[i].Y, dirs[i].Z);
\r
2548 v3f centerpoint = npf + dir_f * BS/2;
\r
2550 (centerpoint - camera_position).getLength();
\r
2552 if(distance < mindistance)
\r
2554 core::CMatrix4<f32> m;
\r
2555 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2557 // This is the back face
\r
2558 v3f corners[2] = {
\r
2559 v3f(BS/2, BS/2, BS/2),
\r
2560 v3f(-BS/2, -BS/2, BS/2+d)
\r
2563 for(u16 j=0; j<2; j++)
\r
2565 m.rotateVect(corners[j]);
\r
2566 corners[j] += npf;
\r
2569 core::aabbox3d<f32> facebox(corners[0]);
\r
2570 facebox.addInternalPoint(corners[1]);
\r
2572 if(facebox.intersectsWithLine(shootline))
\r
2576 neighbourpos = np + dirs[i];
\r
2577 mindistance = distance;
\r
2579 //nodehilightbox = facebox;
\r
2581 const float d = 0.502;
\r
2582 core::aabbox3d<f32> nodebox
\r
2583 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2584 v3f nodepos_f = intToFloat(nodepos);
\r
2585 nodebox.MinEdge += nodepos_f;
\r
2586 nodebox.MaxEdge += nodepos_f;
\r
2587 nodehilightbox = nodebox;
\r
2589 } // if distance < mindistance
\r
2591 } // regular block
\r
2594 static float nodig_delay_counter = 0.0;
\r
2598 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2600 static float dig_time = 0.0;
\r
2601 static u16 dig_index = 0;
\r
2603 // Visualize selection
\r
2605 hilightboxes.push_back(nodehilightbox);
\r
2609 if(g_input->getLeftReleased())
\r
2611 client.clearTempMod(nodepos);
\r
2615 if(nodig_delay_counter > 0.0)
\r
2617 nodig_delay_counter -= dtime;
\r
2621 if(nodepos != nodepos_old)
\r
2623 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2624 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2626 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2628 client.clearTempMod(nodepos_old);
\r
2633 if(g_input->getLeftClicked() ||
\r
2634 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2636 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2637 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2639 if(g_input->getLeftClicked())
\r
2641 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2643 if(g_input->getLeftState())
\r
2645 MapNode n = client.getNode(nodepos);
\r
2647 // Get tool name. Default is "" = bare hands
\r
2648 std::string toolname = "";
\r
2649 InventoryList *mlist = local_inventory.getList("main");
\r
2652 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2653 if(item && (std::string)item->getName() == "ToolItem")
\r
2655 ToolItem *titem = (ToolItem*)item;
\r
2656 toolname = titem->getToolName();
\r
2660 // Get digging properties for material and tool
\r
2661 u8 material = n.d;
\r
2662 DiggingProperties prop =
\r
2663 getDiggingProperties(material, toolname);
\r
2665 float dig_time_complete = 0.0;
\r
2667 if(prop.diggable == false)
\r
2669 /*dstream<<"Material "<<(int)material
\r
2670 <<" not diggable with \""
\r
2671 <<toolname<<"\""<<std::endl;*/
\r
2672 // I guess nobody will wait for this long
\r
2673 dig_time_complete = 10000000.0;
\r
2677 dig_time_complete = prop.time;
\r
2680 if(dig_time_complete >= 0.001)
\r
2682 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2683 * dig_time/dig_time_complete);
\r
2685 // This is for torches
\r
2688 dig_index = CRACK_ANIMATION_LENGTH;
\r
2691 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2693 //TimeTaker timer("client.setTempMod");
\r
2694 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2695 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2699 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2700 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2701 client.clearTempMod(nodepos);
\r
2702 client.removeNode(nodepos);
\r
2706 nodig_delay_counter = dig_time_complete
\r
2707 / (float)CRACK_ANIMATION_LENGTH;
\r
2709 // We don't want a corresponding delay to
\r
2710 // very time consuming nodes
\r
2711 if(nodig_delay_counter > 0.5)
\r
2713 nodig_delay_counter = 0.5;
\r
2715 // We want a slight delay to very little
\r
2716 // time consuming nodes
\r
2717 float mindelay = 0.15;
\r
2718 if(nodig_delay_counter < mindelay)
\r
2720 nodig_delay_counter = mindelay;
\r
2724 dig_time += dtime;
\r
2728 if(g_input->getRightClicked())
\r
2730 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2731 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2734 nodepos_old = nodepos;
\r
2739 } // selected_object == NULL
\r
2741 g_input->resetLeftClicked();
\r
2742 g_input->resetRightClicked();
\r
2744 if(g_input->getLeftReleased())
\r
2746 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2748 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2750 if(g_input->getRightReleased())
\r
2752 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2756 g_input->resetLeftReleased();
\r
2757 g_input->resetRightReleased();
\r
2760 Calculate stuff for drawing
\r
2763 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2765 u32 daynight_ratio = client.getDayNightRatio();
\r
2766 /*video::SColor bgcolor = video::SColor(
\r
2768 skycolor.getRed() * daynight_ratio / 1000,
\r
2769 skycolor.getGreen() * daynight_ratio / 1000,
\r
2770 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2772 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2773 video::SColor bgcolor = video::SColor(
\r
2775 skycolor.getRed() * l / 255,
\r
2776 skycolor.getGreen() * l / 255,
\r
2777 skycolor.getBlue() * l / 255);
\r
2783 if(g_settings.getBool("enable_fog") == true)
\r
2785 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2786 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2787 if(draw_control.range_all)
\r
2788 range = 100000*BS;
\r
2792 video::EFT_FOG_LINEAR,
\r
2796 false, // pixel fog
\r
2797 false // range fog
\r
2803 Update gui stuff (0ms)
\r
2806 //TimeTaker guiupdatetimer("Gui updating");
\r
2809 static float drawtime_avg = 0;
\r
2810 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2811 static float beginscenetime_avg = 0;
\r
2812 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2813 static float scenetime_avg = 0;
\r
2814 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2815 static float endscenetime_avg = 0;
\r
2816 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2818 char temptext[300];
\r
2819 snprintf(temptext, 300, "Minetest-c55 ("
\r
2821 ", R: range_all=%i"
\r
2823 " drawtime=%.0f, beginscenetime=%.0f"
\r
2824 ", scenetime=%.0f, endscenetime=%.0f",
\r
2826 draw_control.range_all,
\r
2828 beginscenetime_avg,
\r
2833 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2837 char temptext[300];
\r
2838 snprintf(temptext, 300,
\r
2839 "(% .1f, % .1f, % .1f)"
\r
2840 " (% .3f < btime_jitter < % .3f"
\r
2841 ", dtime_jitter = % .1f %%"
\r
2842 ", v_range = %.1f)",
\r
2843 player_position.X/BS,
\r
2844 player_position.Y/BS,
\r
2845 player_position.Z/BS,
\r
2846 busytime_jitter1_min_sample,
\r
2847 busytime_jitter1_max_sample,
\r
2848 dtime_jitter1_max_fraction * 100.0,
\r
2849 draw_control.wanted_range
\r
2852 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2856 guitext_info->setText(infotext.c_str());
\r
2860 Get chat messages from client
\r
2863 // Get new messages
\r
2864 std::wstring message;
\r
2865 while(client.getChatMessage(message))
\r
2867 chat_lines.push_back(ChatLine(message));
\r
2868 /*if(chat_lines.size() > 6)
\r
2870 core::list<ChatLine>::Iterator
\r
2871 i = chat_lines.begin();
\r
2872 chat_lines.erase(i);
\r
2875 // Append them to form the whole static text and throw
\r
2876 // it to the gui element
\r
2877 std::wstring whole;
\r
2878 // This will correspond to the line number counted from
\r
2879 // top to bottom, from size-1 to 0
\r
2880 s16 line_number = chat_lines.size();
\r
2881 // Count of messages to be removed from the top
\r
2882 u16 to_be_removed_count = 0;
\r
2883 for(core::list<ChatLine>::Iterator
\r
2884 i = chat_lines.begin();
\r
2885 i != chat_lines.end(); i++)
\r
2887 // After this, line number is valid for this loop
\r
2890 (*i).age += dtime;
\r
2892 This results in a maximum age of 60*6 to the
\r
2893 lowermost line and a maximum of 6 lines
\r
2895 float allowed_age = (6-line_number) * 60.0;
\r
2897 if((*i).age > allowed_age)
\r
2899 to_be_removed_count++;
\r
2902 whole += (*i).text + L'\n';
\r
2904 for(u16 i=0; i<to_be_removed_count; i++)
\r
2906 core::list<ChatLine>::Iterator
\r
2907 it = chat_lines.begin();
\r
2908 chat_lines.erase(it);
\r
2910 guitext_chat->setText(whole.c_str());
\r
2911 // Update gui element size and position
\r
2912 core::rect<s32> rect(
\r
2914 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2915 screensize.X - 10,
\r
2918 guitext_chat->setRelativePosition(rect);
\r
2920 if(chat_lines.size() == 0)
\r
2921 guitext_chat->setVisible(false);
\r
2923 guitext_chat->setVisible(true);
\r
2930 static u16 old_selected_item = 65535;
\r
2931 if(client.getLocalInventoryUpdated()
\r
2932 || g_selected_item != old_selected_item)
\r
2934 old_selected_item = g_selected_item;
\r
2935 //std::cout<<"Updating local inventory"<<std::endl;
\r
2936 client.getLocalInventory(local_inventory);
\r
2937 quick_inventory->setSelection(g_selected_item);
\r
2938 quick_inventory->update();
\r
2942 Send actions returned by the inventory menu
\r
2944 while(inventory_action_queue.size() != 0)
\r
2946 InventoryAction *a = inventory_action_queue.pop_front();
\r
2948 client.sendInventoryAction(a);
\r
2957 TimeTaker drawtimer("Drawing");
\r
2961 TimeTaker timer("beginScene");
\r
2962 driver->beginScene(true, true, bgcolor);
\r
2963 //driver->beginScene(false, true, bgcolor);
\r
2964 beginscenetime = timer.stop(true);
\r
2969 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2972 TimeTaker timer("smgr");
\r
2974 scenetime = timer.stop(true);
\r
2978 //TimeTaker timer9("auxiliary drawings");
\r
2982 //TimeTaker //timer10("//timer10");
\r
2984 video::SMaterial m;
\r
2985 //m.Thickness = 10;
\r
2987 m.Lighting = false;
\r
2988 driver->setMaterial(m);
\r
2990 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2992 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2993 i != hilightboxes.end(); i++)
\r
2995 /*std::cout<<"hilightbox min="
\r
2996 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2998 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3000 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3006 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3007 displaycenter + core::vector2d<s32>(10,0),
\r
3008 video::SColor(255,255,255,255));
\r
3009 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3010 displaycenter + core::vector2d<s32>(0,10),
\r
3011 video::SColor(255,255,255,255));
\r
3016 if(g_settings.getBool("frametime_graph") == true)
\r
3019 for(core::list<float>::Iterator
\r
3020 i = frametime_log.begin();
\r
3021 i != frametime_log.end();
\r
3024 driver->draw2DLine(v2s32(x,50),
\r
3025 v2s32(x,50+(*i)*1000),
\r
3026 video::SColor(255,255,255,255));
\r
3034 //TimeTaker //timer11("//timer11");
\r
3040 guienv->drawAll();
\r
3044 TimeTaker timer("endScene");
\r
3045 driver->endScene();
\r
3046 endscenetime = timer.stop(true);
\r
3049 drawtime = drawtimer.stop(true);
\r
3055 static s16 lastFPS = 0;
\r
3056 //u16 fps = driver->getFPS();
\r
3057 u16 fps = (1.0/dtime_avg1);
\r
3059 if (lastFPS != fps)
\r
3061 core::stringw str = L"Minetest [";
\r
3062 str += driver->getName();
\r
3066 device->setWindowCaption(str.c_str());
\r
3072 device->yield();*/
\r
3075 delete quick_inventory;
\r
3078 Disable texture fetches and other stuff that is queued
\r
3079 to be processed by the main loop.
\r
3081 This has to be done before client goes out of scope.
\r
3083 g_irrlicht->Shutdown(true);
\r
3085 } // client and server are deleted at this point
\r
3088 catch(con::PeerNotFoundException &e)
\r
3090 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3091 error_message = L"Connection timed out.";
\r
3094 } // Menu-game loop
\r
3099 In the end, delete the Irrlicht device.
\r
3104 Update configuration file
\r
3106 /*if(configpath != "")
\r
3108 g_settings.updateConfigFile(configpath.c_str());
\r
3111 END_DEBUG_EXCEPTION_HANDLER
\r
3113 debugstreams_deinit();
\r