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
81 - NOTE: Player::move is more up-to-date.
\r
83 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
84 - This is not doable because it is currently hand-made and not
\r
85 based on some mathematical function.
\r
86 - Note: This has been changing lately
\r
88 SUGG: A version number to blocks, which increments when the block is
\r
89 modified (node add/remove, water update, lighting update)
\r
90 - This can then be used to make sure the most recent version of
\r
91 a block has been sent to client
\r
93 SUGG: Make the amount of blocks sending to client and the total
\r
94 amount of blocks dynamically limited. Transferring blocks is the
\r
95 main network eater of this system, so it is the one that has
\r
96 to be throttled so that RTTs stay low.
\r
98 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
99 different directions and then only those drawn that need to be
\r
101 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
107 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
109 - The player could go faster by a crafting a boat, or riding an animal
\r
111 - Random NPC traders. what else?
\r
116 Build system / running:
\r
117 -----------------------
\r
119 FIXME: Some network errors on Windows that cause local game to not work
\r
120 - See siggjen's emails.
\r
121 - Is this the famous "windows 7 problem"?
\r
122 - Apparently there might be other errors too
\r
124 Networking and serialization:
\r
125 -----------------------------
\r
127 TODO: Get rid of GotSplitPacketException
\r
132 TODO: Add gui option to remove map
\r
134 TODO: Configuration menu, at least for keys
\r
139 TODO: Optimize day/night mesh updating somehow
\r
140 - create copies of all textures for all lighting values and only
\r
141 change texture for material?
\r
142 - Umm... the collecting of the faces is the slow part
\r
143 -> what about just changing the color values of the existing
\r
144 meshbuffers? It should go quite fast.
\r
145 - This is not easy; There'd need to be a buffer somewhere
\r
146 that would contain the night and day lighting values.
\r
147 - Actually if FastFaces would be stored, they could
\r
150 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
152 - That is >500 vertices
\r
153 - This is not easy; all the MapBlocks close to the player would
\r
154 still need to be drawn separately and combining the blocks
\r
155 would have to happen in a background thread
\r
157 TODO: Make fetching sector's blocks more efficient when rendering
\r
158 sectors that have very large amounts of blocks (on client)
\r
159 - Is this necessary at all?
\r
161 TODO: Flowing water animation
\r
169 TODO: Untie client network operations from framerate
\r
170 - Needs some input queues or something
\r
172 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
174 SUGG: Don't update all meshes always on single node changes, but
\r
175 check which ones should be updated
\r
176 - implement Map::updateNodeMeshes()
\r
178 TODO: Remove IrrlichtWrapper
\r
183 TODO: When player dies, throw items on map
\r
185 TODO: Make an option to the server to disable building and digging near
\r
186 the starting position
\r
188 TODO: Copy the text of the last picked sign to inventory in creative
\r
191 TODO: Check what goes wrong with caching map to disk (Kray)
\r
194 TODO: When server sees that client is removing an inexistent block in
\r
195 an existent position, resend the MapBlock.
\r
197 FIXME: Server went into some infinite PeerNotFoundException loop
\r
202 TODO: There has to be some better way to handle static objects than to
\r
203 send them all the time. This affects signs and item objects.
\r
204 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
205 need an additional metadata field for the texts
\r
206 - This is also needed for item container chests
\r
208 Block object server side:
\r
209 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
210 - For all blocks in the buffer, objects are stepped(). This
\r
211 means they are active.
\r
212 - TODO: A global active buffer is needed for the server
\r
213 - TODO: A timestamp to blocks
\r
214 - TODO: All blocks going in and out of the buffer are recorded.
\r
215 - TODO: For outgoing blocks, timestamp is written.
\r
216 - TODO: For incoming blocks, time difference is calculated and
\r
217 objects are stepped according to it.
\r
219 - When an active object goes far from a player, either delete
\r
220 it or store it statically.
\r
221 - When a statically stored active object comes near a player,
\r
222 recreate the active object
\r
227 TODO: Mineral and ground material properties
\r
228 - This way mineral ground toughness can be calculated with just
\r
229 some formula, as well as tool strengths
\r
231 TODO: Flowing water to actually contain flow direction information
\r
233 TODO: Remove duplicate lighting implementation from Map (leave
\r
234 VoxelManipulator, which is faster)
\r
236 FEATURE: Create a system that allows a huge amount of different "map
\r
237 generator modules/filters"
\r
239 FEATURE: Erosion simulation at map generation time
\r
240 - Simulate water flows, which would carve out dirt fast and
\r
241 then turn stone into gravel and sand and relocate it.
\r
242 - How about relocating minerals, too? Coal and gold in
\r
243 downstream sand and gravel would be kind of cool
\r
244 - This would need a better way of handling minerals, mainly
\r
245 to have mineral content as a separate field. the first
\r
246 parameter field is free for this.
\r
247 - Simulate rock falling from cliffs when water has removed
\r
248 enough solid rock from the bottom
\r
250 Doing now (most important at the top):
\r
251 --------------------------------------
\r
256 * Continue making the scripting system:
\r
257 * Rip up everything unnecessary from the lua build system
\r
258 * Make updateNodeMesh for a less verbose mesh update on add/removenode
\r
259 * Switch to using a safe way for the self and env pointers
\r
260 * Make some global environment hooks, like node placed and general
\r
264 * Check the fixmes in the list above
\r
265 * Make server find the spawning place from the real map data, not from
\r
267 - But the changing borders of chunk have to be avoided, because
\r
268 there is time to generate only one chunk.
\r
269 * Make the generator to run in background and not blocking block
\r
270 placement and transfer
\r
271 * only_from_disk might not work anymore - check and fix it.
\r
273 === Making it more portable
\r
274 * Some MSVC: std::sto* are defined without a namespace and collide
\r
275 with the ones in utility.h
\r
278 * Map should make the appropriate MapEditEvents
\r
279 * Add a global Lua spawn handler and such
\r
280 * Get rid of MapBlockObjects
\r
281 * Other players could be sent to clients as LuaCAOs
\r
282 * Add mud underground
\r
283 * Make an "environment metafile" to store at least time of day
\r
284 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
285 - Or maybe move content_features to material.{h,cpp}?
\r
286 * Add some kind of erosion and other stuff that now is possible
\r
287 * Make client to fetch stuff asynchronously
\r
288 - Needs method SyncProcessData
\r
289 * Better water generation (spread it to underwater caverns but don't
\r
290 fill dungeons that don't touch big water masses)
\r
291 * When generating a chunk and the neighboring chunk doesn't have mud
\r
292 and stuff yet and the ground is fairly flat, the mud will flow to
\r
293 the other chunk making nasty straight walls when the other chunk
\r
294 is generated. Fix it.
\r
295 * Fix the problem with the server constantly saving one or a few
\r
296 blocks? List the first saved block, maybe it explains.
\r
297 - It is probably caused by oscillating water
\r
298 * Make a small history check to transformLiquids to detect and log
\r
299 continuous oscillations, in such detail that they can be fixed.
\r
300 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
302 ======================================================================
\r
307 Setting this to 1 enables a special camera mode that forces
\r
308 the renderers to think that the camera statically points from
\r
309 the starting place to a static direction.
\r
311 This allows one to move around with the player and see what
\r
312 is actually drawn behind solid things and behind the player.
\r
314 #define FIELD_OF_VIEW_TEST 0
\r
318 #pragma message ("Disabling unit tests")
\r
320 #warning "Disabling unit tests"
\r
322 // Disable unit tests
\r
323 #define ENABLE_TESTS 0
\r
325 // Enable unit tests
\r
326 #define ENABLE_TESTS 1
\r
330 #pragma comment(lib, "Irrlicht.lib")
\r
331 //#pragma comment(lib, "jthread.lib")
\r
332 #pragma comment(lib, "zlibwapi.lib")
\r
333 #pragma comment(lib, "Shell32.lib")
\r
334 // This would get rid of the console window
\r
335 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
338 #include <iostream>
\r
340 #include <jmutexautolock.h>
\r
341 #include <locale.h>
\r
343 #include "common_irrlicht.h"
\r
346 #include "player.h"
\r
348 //#include "environment.h"
\r
349 #include "server.h"
\r
350 #include "client.h"
\r
351 //#include "serialization.h"
\r
352 #include "constants.h"
\r
353 //#include "strfnd.h"
\r
354 #include "porting.h"
\r
355 #include "irrlichtwrapper.h"
\r
356 #include "gettime.h"
\r
357 #include "porting.h"
\r
358 #include "guiPauseMenu.h"
\r
359 #include "guiInventoryMenu.h"
\r
360 #include "guiTextInputMenu.h"
\r
361 #include "materials.h"
\r
362 #include "guiMessageMenu.h"
\r
363 #include "filesys.h"
\r
364 #include "config.h"
\r
365 #include "guiMainMenu.h"
\r
366 #include "mineral.h"
\r
370 // TODO: Remove this
\r
371 IrrlichtWrapper *g_irrlicht = NULL;
\r
373 // This makes textures
\r
374 ITextureSource *g_texturesource = NULL;
\r
376 MapDrawControl draw_control;
\r
380 These are loaded from the config file.
\r
383 Settings g_settings;
\r
385 extern void set_default_settings();
\r
391 IrrlichtDevice *g_device = NULL;
\r
392 Client *g_client = NULL;
\r
394 /*const s16 quickinv_size = 40;
\r
395 const s16 quickinv_padding = 8;
\r
396 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
397 const s16 quickinv_outer_padding = 4;
\r
398 const s16 quickinv_itemcount = 8;*/
\r
400 const s32 hotbar_itemcount = 8;
\r
401 const s32 hotbar_imagesize = 36;
\r
407 gui::IGUIEnvironment* guienv = NULL;
\r
408 gui::IGUIStaticText *guiroot = NULL;
\r
410 class MainMenuManager : public IMenuManager
\r
413 virtual void createdMenu(GUIModalMenu *menu)
\r
415 for(core::list<GUIModalMenu*>::Iterator
\r
416 i = m_stack.begin();
\r
417 i != m_stack.end(); i++)
\r
419 assert(*i != menu);
\r
422 if(m_stack.size() != 0)
\r
423 (*m_stack.getLast())->setVisible(false);
\r
424 m_stack.push_back(menu);
\r
427 virtual void deletingMenu(GUIModalMenu *menu)
\r
429 // Remove all entries if there are duplicates
\r
430 bool removed_entry;
\r
432 removed_entry = false;
\r
433 for(core::list<GUIModalMenu*>::Iterator
\r
434 i = m_stack.begin();
\r
435 i != m_stack.end(); i++)
\r
440 removed_entry = true;
\r
444 }while(removed_entry);
\r
446 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
447 assert(*i == menu);
\r
448 m_stack.erase(i);*/
\r
450 if(m_stack.size() != 0)
\r
451 (*m_stack.getLast())->setVisible(true);
\r
456 return m_stack.size();
\r
459 core::list<GUIModalMenu*> m_stack;
\r
462 MainMenuManager g_menumgr;
\r
464 bool noMenuActive()
\r
466 return (g_menumgr.menuCount() == 0);
\r
469 bool g_disconnect_requested = false;
\r
471 class MainGameCallback : public IGameCallback
\r
474 virtual void exitToOS()
\r
476 g_device->closeDevice();
\r
479 virtual void disconnect()
\r
481 g_disconnect_requested = true;
\r
485 MainGameCallback g_gamecallback;
\r
487 // Inventory actions from the menu are buffered here before sending
\r
488 Queue<InventoryAction*> inventory_action_queue;
\r
489 // This is a copy of the inventory that the client's environment has
\r
490 Inventory local_inventory;
\r
492 u16 g_selected_item = 0;
\r
499 std::ostream *dout_con_ptr = &dummyout;
\r
500 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
501 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
502 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
503 //std::ostream *dout_con_ptr = &dstream;
\r
504 //std::ostream *derr_con_ptr = &dstream;
\r
507 std::ostream *dout_server_ptr = &dstream;
\r
508 std::ostream *derr_server_ptr = &dstream;
\r
511 std::ostream *dout_client_ptr = &dstream;
\r
512 std::ostream *derr_client_ptr = &dstream;
\r
515 gettime.h implementation
\r
521 Use irrlicht because it is more precise than porting.h's
\r
524 if(g_irrlicht == NULL)
\r
526 return g_irrlicht->getTime();
\r
533 struct TextDestSign : public TextDest
\r
535 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
537 m_blockpos = blockpos;
\r
541 void gotText(std::wstring text)
\r
543 std::string ntext = wide_to_narrow(text);
\r
544 dstream<<"Changing text of a sign object: "
\r
545 <<ntext<<std::endl;
\r
546 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
554 struct TextDestChat : public TextDest
\r
556 TextDestChat(Client *client)
\r
560 void gotText(std::wstring text)
\r
562 // Discard empty line
\r
566 // Parse command (server command starts with "/#")
\r
567 if(text[0] == L'/' && text[1] != L'#')
\r
569 std::wstring reply = L"Local: ";
\r
571 reply += L"Local commands not yet supported. "
\r
572 L"Server prefix is \"/#\".";
\r
574 m_client->addChatMessage(reply);
\r
579 m_client->sendChatMessage(text);
\r
581 m_client->addChatMessage(text);
\r
587 class MyEventReceiver : public IEventReceiver
\r
590 // This is the one method that we have to implement
\r
591 virtual bool OnEvent(const SEvent& event)
\r
594 React to nothing here if a menu is active
\r
596 if(noMenuActive() == false)
\r
602 // Remember whether each key is down or up
\r
603 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
605 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
607 if(event.KeyInput.PressedDown)
\r
609 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
615 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
617 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
619 dstream<<DTIME<<"MyEventReceiver: "
\r
620 <<"Launching pause menu"<<std::endl;
\r
621 // It will delete itself by itself
\r
622 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
623 &g_menumgr))->drop();
\r
626 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
628 dstream<<DTIME<<"MyEventReceiver: "
\r
629 <<"Launching inventory"<<std::endl;
\r
630 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
631 &local_inventory, &inventory_action_queue,
\r
632 &g_menumgr))->drop();
\r
635 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
637 TextDest *dest = new TextDestChat(g_client);
\r
639 (new GUITextInputMenu(guienv, guiroot, -1,
\r
646 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
647 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
649 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
650 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
652 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
653 g_selected_item = s1-1;
\r
654 dstream<<DTIME<<"Selected item: "
\r
655 <<g_selected_item<<std::endl;
\r
658 // Viewing range selection
\r
659 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
661 if(draw_control.range_all)
\r
663 draw_control.range_all = false;
\r
664 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
668 draw_control.range_all = true;
\r
669 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
673 // Print debug stacks
\r
674 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
676 dstream<<"-----------------------------------------"
\r
678 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
679 dstream<<"-----------------------------------------"
\r
681 debug_stacks_print();
\r
686 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
688 if(noMenuActive() == false)
\r
690 left_active = false;
\r
691 middle_active = false;
\r
692 right_active = false;
\r
696 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
697 left_active = event.MouseInput.isLeftPressed();
\r
698 middle_active = event.MouseInput.isMiddlePressed();
\r
699 right_active = event.MouseInput.isRightPressed();
\r
701 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
703 leftclicked = true;
\r
705 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
707 rightclicked = true;
\r
709 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
711 leftreleased = true;
\r
713 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
715 rightreleased = true;
\r
717 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
719 /*dstream<<"event.MouseInput.Wheel="
\r
720 <<event.MouseInput.Wheel<<std::endl;*/
\r
722 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
723 hotbar_itemcount-1);
\r
724 if(event.MouseInput.Wheel < 0)
\r
726 if(g_selected_item < max_item)
\r
729 g_selected_item = 0;
\r
731 else if(event.MouseInput.Wheel > 0)
\r
733 if(g_selected_item > 0)
\r
736 g_selected_item = max_item;
\r
745 // This is used to check whether a key is being held down
\r
746 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
748 return keyIsDown[keyCode];
\r
753 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
754 keyIsDown[i] = false;
\r
756 leftclicked = false;
\r
757 rightclicked = false;
\r
758 leftreleased = false;
\r
759 rightreleased = false;
\r
761 left_active = false;
\r
762 middle_active = false;
\r
763 right_active = false;
\r
774 bool rightreleased;
\r
777 bool middle_active;
\r
781 // We use this array to store the current state of each key
\r
782 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
785 IrrlichtDevice *m_device;
\r
794 virtual ~InputHandler()
\r
798 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
800 virtual v2s32 getMousePos() = 0;
\r
801 virtual void setMousePos(s32 x, s32 y) = 0;
\r
803 virtual bool getLeftState() = 0;
\r
804 virtual bool getRightState() = 0;
\r
806 virtual bool getLeftClicked() = 0;
\r
807 virtual bool getRightClicked() = 0;
\r
808 virtual void resetLeftClicked() = 0;
\r
809 virtual void resetRightClicked() = 0;
\r
811 virtual bool getLeftReleased() = 0;
\r
812 virtual bool getRightReleased() = 0;
\r
813 virtual void resetLeftReleased() = 0;
\r
814 virtual void resetRightReleased() = 0;
\r
816 virtual void step(float dtime) {};
\r
818 virtual void clear() {};
\r
821 InputHandler *g_input = NULL;
\r
823 class RealInputHandler : public InputHandler
\r
826 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
828 m_receiver(receiver)
\r
831 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
833 return m_receiver->IsKeyDown(keyCode);
\r
835 virtual v2s32 getMousePos()
\r
837 return m_device->getCursorControl()->getPosition();
\r
839 virtual void setMousePos(s32 x, s32 y)
\r
841 m_device->getCursorControl()->setPosition(x, y);
\r
844 virtual bool getLeftState()
\r
846 return m_receiver->left_active;
\r
848 virtual bool getRightState()
\r
850 return m_receiver->right_active;
\r
853 virtual bool getLeftClicked()
\r
855 return m_receiver->leftclicked;
\r
857 virtual bool getRightClicked()
\r
859 return m_receiver->rightclicked;
\r
861 virtual void resetLeftClicked()
\r
863 m_receiver->leftclicked = false;
\r
865 virtual void resetRightClicked()
\r
867 m_receiver->rightclicked = false;
\r
870 virtual bool getLeftReleased()
\r
872 return m_receiver->leftreleased;
\r
874 virtual bool getRightReleased()
\r
876 return m_receiver->rightreleased;
\r
878 virtual void resetLeftReleased()
\r
880 m_receiver->leftreleased = false;
\r
882 virtual void resetRightReleased()
\r
884 m_receiver->rightreleased = false;
\r
889 resetRightClicked();
\r
890 resetLeftClicked();
\r
893 IrrlichtDevice *m_device;
\r
894 MyEventReceiver *m_receiver;
\r
897 class RandomInputHandler : public InputHandler
\r
900 RandomInputHandler()
\r
904 leftclicked = false;
\r
905 rightclicked = false;
\r
906 leftreleased = false;
\r
907 rightreleased = false;
\r
908 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
909 keydown[i] = false;
\r
911 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
913 return keydown[keyCode];
\r
915 virtual v2s32 getMousePos()
\r
919 virtual void setMousePos(s32 x, s32 y)
\r
921 mousepos = v2s32(x,y);
\r
924 virtual bool getLeftState()
\r
928 virtual bool getRightState()
\r
933 virtual bool getLeftClicked()
\r
935 return leftclicked;
\r
937 virtual bool getRightClicked()
\r
939 return rightclicked;
\r
941 virtual void resetLeftClicked()
\r
943 leftclicked = false;
\r
945 virtual void resetRightClicked()
\r
947 rightclicked = false;
\r
950 virtual bool getLeftReleased()
\r
952 return leftreleased;
\r
954 virtual bool getRightReleased()
\r
956 return rightreleased;
\r
958 virtual void resetLeftReleased()
\r
960 leftreleased = false;
\r
962 virtual void resetRightReleased()
\r
964 rightreleased = false;
\r
967 virtual void step(float dtime)
\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 leftdown = !leftdown;
\r
1022 leftclicked = true;
\r
1024 leftreleased = true;
\r
1028 static float counter1 = 0;
\r
1029 counter1 -= dtime;
\r
1030 if(counter1 < 0.0)
\r
1032 counter1 = 0.1*Rand(1, 15);
\r
1033 rightdown = !rightdown;
\r
1035 rightclicked = true;
\r
1037 rightreleased = true;
\r
1040 mousepos += mousespeed;
\r
1043 s32 Rand(s32 min, s32 max)
\r
1045 return (myrand()%(max-min+1))+min;
\r
1048 bool keydown[KEY_KEY_CODES_COUNT];
\r
1054 bool rightclicked;
\r
1055 bool leftreleased;
\r
1056 bool rightreleased;
\r
1059 void updateViewingRange(f32 frametime_in, Client *client)
\r
1061 if(draw_control.range_all == true)
\r
1064 static f32 added_frametime = 0;
\r
1065 static s16 added_frames = 0;
\r
1067 added_frametime += frametime_in;
\r
1068 added_frames += 1;
\r
1070 // Actually this counter kind of sucks because frametime is busytime
\r
1071 static f32 counter = 0;
\r
1072 counter -= frametime_in;
\r
1078 /*dstream<<__FUNCTION_NAME
\r
1079 <<": Collected "<<added_frames<<" frames, total of "
\r
1080 <<added_frametime<<"s."<<std::endl;*/
\r
1082 /*dstream<<"draw_control.blocks_drawn="
\r
1083 <<draw_control.blocks_drawn
\r
1084 <<", draw_control.blocks_would_have_drawn="
\r
1085 <<draw_control.blocks_would_have_drawn
\r
1088 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1089 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1091 draw_control.wanted_min_range = range_min;
\r
1092 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1094 float block_draw_ratio = 1.0;
\r
1095 if(draw_control.blocks_would_have_drawn != 0)
\r
1097 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1098 / (float)draw_control.blocks_would_have_drawn;
\r
1101 // Calculate the average frametime in the case that all wanted
\r
1102 // blocks had been drawn
\r
1103 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1105 added_frametime = 0.0;
\r
1108 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1109 float wanted_frametime = 1.0 / wanted_fps;
\r
1111 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1112 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1114 // If needed frametime change is very small, just return
\r
1115 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1117 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1121 float range = draw_control.wanted_range;
\r
1122 float new_range = range;
\r
1124 static s16 range_old = 0;
\r
1125 static f32 frametime_old = 0;
\r
1127 float d_range = range - range_old;
\r
1128 f32 d_frametime = frametime - frametime_old;
\r
1129 // A sane default of 30ms per 50 nodes of range
\r
1130 static f32 time_per_range = 30. / 50;
\r
1133 time_per_range = d_frametime / d_range;
\r
1136 // The minimum allowed calculated frametime-range derivative:
\r
1137 // Practically this sets the maximum speed of changing the range.
\r
1138 // The lower this value, the higher the maximum changing speed.
\r
1139 // A low value here results in wobbly range (0.001)
\r
1140 // A high value here results in slow changing range (0.0025)
\r
1141 // SUGG: This could be dynamically adjusted so that when
\r
1142 // the camera is turning, this is lower
\r
1143 //float min_time_per_range = 0.0015;
\r
1144 float min_time_per_range = 0.0010;
\r
1145 //float min_time_per_range = 0.05 / range;
\r
1146 if(time_per_range < min_time_per_range)
\r
1148 time_per_range = min_time_per_range;
\r
1149 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1153 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1156 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1157 // Dampen the change a bit to kill oscillations
\r
1158 //wanted_range_change *= 0.9;
\r
1159 //wanted_range_change *= 0.75;
\r
1160 wanted_range_change *= 0.5;
\r
1161 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1163 // If needed range change is very small, just return
\r
1164 if(fabs(wanted_range_change) < 0.001)
\r
1166 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1170 new_range += wanted_range_change;
\r
1171 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1173 //float new_range_unclamped = new_range;
\r
1174 if(new_range < range_min)
\r
1175 new_range = range_min;
\r
1176 if(new_range > range_max)
\r
1177 new_range = range_max;
\r
1179 /*if(new_range != new_range_unclamped)
\r
1180 dstream<<", clamped to "<<new_range<<std::endl;
\r
1182 dstream<<std::endl;*/
\r
1184 draw_control.wanted_range = new_range;
\r
1186 range_old = new_range;
\r
1187 frametime_old = frametime;
\r
1190 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1191 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1192 Inventory *inventory)
\r
1194 InventoryList *mainlist = inventory->getList("main");
\r
1195 if(mainlist == NULL)
\r
1197 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1201 s32 padding = imgsize/12;
\r
1202 //s32 height = imgsize + padding*2;
\r
1203 s32 width = itemcount*(imgsize+padding*2);
\r
1205 // Position of upper left corner of bar
\r
1206 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1208 // Draw background color
\r
1209 /*core::rect<s32> barrect(0,0,width,height);
\r
1211 video::SColor bgcolor(255,128,128,128);
\r
1212 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1214 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1216 for(s32 i=0; i<itemcount; i++)
\r
1218 InventoryItem *item = mainlist->getItem(i);
\r
1220 core::rect<s32> rect = imgrect + pos
\r
1221 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1223 if(g_selected_item == i)
\r
1225 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1226 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1227 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1232 video::SColor bgcolor2(128,0,0,0);
\r
1233 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1238 drawInventoryItem(driver, font, item, rect, NULL);
\r
1250 ChatLine(const std::wstring &a_text):
\r
1256 std::wstring text;
\r
1259 // These are defined global so that they're not optimized too much.
\r
1260 // Can't change them to volatile.
\r
1265 std::string tempstring;
\r
1266 std::string tempstring2;
\r
1271 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1272 TimeTaker timer("Testing std::string speed");
\r
1273 const u32 jj = 10000;
\r
1274 for(u32 j=0; j<jj; j++)
\r
1278 const u32 ii = 10;
\r
1279 for(u32 i=0; i<ii; i++){
\r
1280 tempstring2 += "asd";
\r
1282 for(u32 i=0; i<ii+1; i++){
\r
1283 tempstring += "asd";
\r
1284 if(tempstring == tempstring2)
\r
1290 dstream<<"All of the following tests should take around 100ms each."
\r
1294 TimeTaker timer("Testing floating-point conversion speed");
\r
1296 for(u32 i=0; i<4000000; i++){
\r
1303 TimeTaker timer("Testing floating-point vector speed");
\r
1305 tempv3f1 = v3f(1,2,3);
\r
1306 tempv3f2 = v3f(4,5,6);
\r
1307 for(u32 i=0; i<10000000; i++){
\r
1308 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1309 tempv3f2 += v3f(7,8,9);
\r
1314 TimeTaker timer("Testing core::map speed");
\r
1316 core::map<v2s16, f32> map1;
\r
1319 for(s16 y=0; y<ii; y++){
\r
1320 for(s16 x=0; x<ii; x++){
\r
1321 map1.insert(v2s16(x,y), tempf);
\r
1325 for(s16 y=ii-1; y>=0; y--){
\r
1326 for(s16 x=0; x<ii; x++){
\r
1327 tempf = map1[v2s16(x,y)];
\r
1333 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1334 TimeTaker timer("Testing mutex speed");
\r
1347 // Do at least 10ms
\r
1348 while(timer.getTime() < 10);
\r
1350 u32 dtime = timer.stop();
\r
1351 u32 per_ms = n / dtime;
\r
1352 std::cout<<"Done. "<<dtime<<"ms, "
\r
1353 <<per_ms<<"/ms"<<std::endl;
\r
1357 int main(int argc, char *argv[])
\r
1360 Parse command line
\r
1363 // List all allowed options
\r
1364 core::map<std::string, ValueSpec> allowed_options;
\r
1365 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1366 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1367 "Run server directly"));
\r
1368 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1369 "Load configuration from specified file"));
\r
1370 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1371 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1372 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1373 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1374 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1375 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1377 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1379 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1381 Settings cmd_args;
\r
1383 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1385 if(ret == false || cmd_args.getFlag("help"))
\r
1387 dstream<<"Allowed options:"<<std::endl;
\r
1388 for(core::map<std::string, ValueSpec>::Iterator
\r
1389 i = allowed_options.getIterator();
\r
1390 i.atEnd() == false; i++)
\r
1392 dstream<<" --"<<i.getNode()->getKey();
\r
1393 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1398 dstream<<" <value>";
\r
1400 dstream<<std::endl;
\r
1402 if(i.getNode()->getValue().help != NULL)
\r
1404 dstream<<" "<<i.getNode()->getValue().help
\r
1409 return cmd_args.getFlag("help") ? 0 : 1;
\r
1413 Low-level initialization
\r
1416 bool disable_stderr = false;
\r
1418 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1419 disable_stderr = true;
\r
1422 // Initialize debug streams
\r
1423 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1424 // Initialize debug stacks
\r
1425 debug_stacks_init();
\r
1427 DSTACK(__FUNCTION_NAME);
\r
1429 porting::signal_handler_init();
\r
1430 bool &kill = *porting::signal_handler_killstatus();
\r
1432 porting::initializePaths();
\r
1433 // Create user data directory
\r
1434 fs::CreateDir(porting::path_userdata);
\r
1436 // C-style stuff initialization
\r
1437 initializeMaterialProperties();
\r
1440 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1442 // Print startup message
\r
1443 dstream<<DTIME<<"minetest-c55"
\r
1444 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1445 <<", "<<BUILD_INFO
\r
1449 Basic initialization
\r
1452 // Initialize default settings
\r
1453 set_default_settings();
\r
1455 // Set locale. This is for forcing '.' as the decimal point.
\r
1456 std::locale::global(std::locale("C"));
\r
1457 // This enables printing all characters in bitmap font
\r
1458 setlocale(LC_CTYPE, "en_US");
\r
1460 // Initialize sockets
\r
1462 atexit(sockets_cleanup);
\r
1472 // Path of configuration file in use
\r
1473 std::string configpath = "";
\r
1475 if(cmd_args.exists("config"))
\r
1477 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1480 dstream<<"Could not read configuration from \""
\r
1481 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1484 configpath = cmd_args.get("config");
\r
1488 core::array<std::string> filenames;
\r
1489 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1490 #ifdef RUN_IN_PLACE
\r
1491 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1494 for(u32 i=0; i<filenames.size(); i++)
\r
1496 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1499 configpath = filenames[i];
\r
1504 // If no path found, use the first one (menu creates the file)
\r
1505 if(configpath == "")
\r
1506 configpath = filenames[0];
\r
1509 // Initialize random seed
\r
1514 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1516 These are needed for unit tests at least.
\r
1519 // Initial call with g_texturesource not set.
\r
1526 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1527 || cmd_args.getFlag("enable-unittests") == true)
\r
1532 /*for(s16 y=-100; y<100; y++)
\r
1533 for(s16 x=-100; x<100; x++)
\r
1535 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1545 if(cmd_args.exists("port"))
\r
1546 port = cmd_args.getU16("port");
\r
1547 else if(cmd_args.exists("port"))
\r
1548 port = g_settings.getU16("port");
\r
1551 std::string map_dir = porting::path_userdata+"/map";
\r
1552 if(cmd_args.exists("map-dir"))
\r
1553 map_dir = cmd_args.get("map-dir");
\r
1554 else if(g_settings.exists("map-dir"))
\r
1555 map_dir = g_settings.get("map-dir");
\r
1557 // Run dedicated server if asked to
\r
1558 if(cmd_args.getFlag("server"))
\r
1560 DSTACK("Dedicated server branch");
\r
1563 Server server(map_dir.c_str());
\r
1564 server.start(port);
\r
1567 dedicated_server_loop(server, kill);
\r
1576 // Address to connect to
\r
1577 std::string address = "";
\r
1579 if(cmd_args.exists("address"))
\r
1581 address = cmd_args.get("address");
\r
1585 address = g_settings.get("address");
\r
1588 std::string playername = g_settings.get("name");
\r
1590 // Resolution selection
\r
1592 bool fullscreen = false;
\r
1593 u16 screenW = g_settings.getU16("screenW");
\r
1594 u16 screenH = g_settings.getU16("screenH");
\r
1596 // Determine driver
\r
1598 video::E_DRIVER_TYPE driverType;
\r
1600 std::string driverstring = g_settings.get("video_driver");
\r
1602 if(driverstring == "null")
\r
1603 driverType = video::EDT_NULL;
\r
1604 else if(driverstring == "software")
\r
1605 driverType = video::EDT_SOFTWARE;
\r
1606 else if(driverstring == "burningsvideo")
\r
1607 driverType = video::EDT_BURNINGSVIDEO;
\r
1608 else if(driverstring == "direct3d8")
\r
1609 driverType = video::EDT_DIRECT3D8;
\r
1610 else if(driverstring == "direct3d9")
\r
1611 driverType = video::EDT_DIRECT3D9;
\r
1612 else if(driverstring == "opengl")
\r
1613 driverType = video::EDT_OPENGL;
\r
1616 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1617 "to opengl"<<std::endl;
\r
1618 driverType = video::EDT_OPENGL;
\r
1621 // create device and exit if creation failed
\r
1623 MyEventReceiver receiver;
\r
1625 IrrlichtDevice *device;
\r
1626 device = createDevice(driverType,
\r
1627 core::dimension2d<u32>(screenW, screenH),
\r
1628 16, fullscreen, false, false, &receiver);
\r
1631 return 1; // could not create selected driver.
\r
1633 g_device = device;
\r
1634 g_irrlicht = new IrrlichtWrapper(device);
\r
1635 TextureSource *texturesource = new TextureSource(device);
\r
1636 g_texturesource = texturesource;
\r
1639 Speed tests (done after irrlicht is loaded to get timer)
\r
1641 if(cmd_args.getFlag("speedtests"))
\r
1643 dstream<<"Running speed tests"<<std::endl;
\r
1648 device->setResizable(true);
\r
1650 bool random_input = g_settings.getBool("random_input")
\r
1651 || cmd_args.getFlag("random-input");
\r
1653 g_input = new RandomInputHandler();
\r
1655 g_input = new RealInputHandler(device, &receiver);
\r
1658 Continue initialization
\r
1661 video::IVideoDriver* driver = device->getVideoDriver();
\r
1664 This changes the minimum allowed number of vertices in a VBO.
\r
1667 //driver->setMinHardwareBufferVertexCount(50);
\r
1669 scene::ISceneManager* smgr = device->getSceneManager();
\r
1671 guienv = device->getGUIEnvironment();
\r
1672 gui::IGUISkin* skin = guienv->getSkin();
\r
1673 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1675 skin->setFont(font);
\r
1677 dstream<<"WARNING: Font file was not found."
\r
1678 " Using default font."<<std::endl;
\r
1679 // If font was not found, this will get us one
\r
1680 font = skin->getFont();
\r
1683 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1684 dstream<<"text_height="<<text_height<<std::endl;
\r
1686 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1687 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1688 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1689 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1690 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1691 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1694 Preload some textures and stuff
\r
1697 init_content_inventory_texture_paths();
\r
1698 init_mapnode(); // Second call with g_texturesource set
\r
1706 We need some kind of a root node to be able to add
\r
1707 custom gui elements directly on the screen.
\r
1708 Otherwise they won't be automatically drawn.
\r
1710 guiroot = guienv->addStaticText(L"",
\r
1711 core::rect<s32>(0, 0, 10000, 10000));
\r
1713 // First line of debug text
\r
1714 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1716 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1718 // Second line of debug text
\r
1719 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1721 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1724 // At the middle of the screen
\r
1725 // Object infos are shown in this
\r
1726 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1728 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1732 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1734 core::rect<s32>(0,0,0,0),
\r
1735 false, false); // Disable word wrap as of now
\r
1737 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1738 core::list<ChatLine> chat_lines;
\r
1741 If an error occurs, this is set to something and the
\r
1742 menu-game loop is restarted. It is then displayed before
\r
1745 std::wstring error_message = L"";
\r
1750 while(g_device->run() && kill == false)
\r
1753 // This is used for catching disconnects
\r
1758 Out-of-game menu loop.
\r
1760 Loop quits when menu returns proper parameters.
\r
1762 while(kill == false)
\r
1764 // Cursor can be non-visible when coming from the game
\r
1765 device->getCursorControl()->setVisible(true);
\r
1766 // Some stuff are left to scene manager when coming from the game
\r
1767 // (map at least?)
\r
1769 // Reset or hide the debug gui texts
\r
1770 guitext->setText(L"Minetest-c55");
\r
1771 guitext2->setVisible(false);
\r
1772 guitext_info->setVisible(false);
\r
1773 guitext_chat->setVisible(false);
\r
1775 // Initialize menu data
\r
1776 MainMenuData menudata;
\r
1777 menudata.address = narrow_to_wide(address);
\r
1778 menudata.name = narrow_to_wide(playername);
\r
1779 menudata.port = narrow_to_wide(itos(port));
\r
1780 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1782 GUIMainMenu *menu =
\r
1783 new GUIMainMenu(guienv, guiroot, -1,
\r
1784 &g_menumgr, &menudata, &g_gamecallback);
\r
1785 menu->allowFocusRemoval(true);
\r
1787 if(error_message != L"")
\r
1789 GUIMessageMenu *menu2 =
\r
1790 new GUIMessageMenu(guienv, guiroot, -1,
\r
1791 &g_menumgr, error_message.c_str());
\r
1793 error_message = L"";
\r
1796 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1798 dstream<<"Created main menu"<<std::endl;
\r
1800 while(g_device->run() && kill == false)
\r
1802 // Run global IrrlichtWrapper's main thread processing stuff
\r
1803 g_irrlicht->Run();
\r
1805 if(menu->getStatus() == true)
\r
1808 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1809 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1810 guienv->drawAll();
\r
1811 driver->endScene();
\r
1814 // Break out of menu-game loop to shut down cleanly
\r
1815 if(g_device->run() == false || kill == true)
\r
1818 dstream<<"Dropping main menu"<<std::endl;
\r
1822 // Delete map if requested
\r
1823 if(menudata.delete_map)
\r
1825 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1827 error_message = L"Delete failed";
\r
1831 playername = wide_to_narrow(menudata.name);
\r
1832 address = wide_to_narrow(menudata.address);
\r
1833 port = stoi(wide_to_narrow(menudata.port));
\r
1834 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1836 // Check for valid parameters, restart menu if invalid.
\r
1837 if(playername == "")
\r
1839 error_message = L"Name required.";
\r
1844 g_settings.set("name", playername);
\r
1845 g_settings.set("address", address);
\r
1846 g_settings.set("port", itos(port));
\r
1847 // Update configuration file
\r
1848 if(configpath != "")
\r
1849 g_settings.updateConfigFile(configpath.c_str());
\r
1851 // Continue to game
\r
1855 // Break out of menu-game loop to shut down cleanly
\r
1856 if(g_device->run() == false)
\r
1860 Make a scope here so that the client and the server and other
\r
1861 stuff gets removed when disconnected or the irrlicht device
\r
1866 // This is set to true at the end of the scope
\r
1867 g_irrlicht->Shutdown(false);
\r
1870 Draw "Loading" screen
\r
1872 const wchar_t *text = L"Loading and connecting...";
\r
1873 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1874 core::vector2d<s32> textsize(300, text_height);
\r
1875 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1877 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1878 text, textrect, false, false);
\r
1879 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1881 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1882 guienv->drawAll();
\r
1883 driver->endScene();
\r
1885 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1889 SharedPtr will delete it when it goes out of scope.
\r
1891 SharedPtr<Server> server;
\r
1892 if(address == ""){
\r
1893 server = new Server(map_dir);
\r
1894 server->start(port);
\r
1901 Client client(device, playername.c_str(), draw_control);
\r
1903 g_client = &client;
\r
1905 Address connect_address(0,0,0,0, port);
\r
1908 //connect_address.Resolve("localhost");
\r
1909 connect_address.setAddress(127,0,0,1);
\r
1911 connect_address.Resolve(address.c_str());
\r
1913 catch(ResolveError &e)
\r
1915 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1917 error_message = L"Couldn't resolve address";
\r
1918 gui_loadingtext->remove();
\r
1922 dstream<<DTIME<<"Connecting to server at ";
\r
1923 connect_address.print(&dstream);
\r
1924 dstream<<std::endl;
\r
1925 client.connect(connect_address);
\r
1928 while(client.connectedAndInitialized() == false)
\r
1931 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1932 guienv->drawAll();
\r
1933 driver->endScene();
\r
1935 // Update client and server
\r
1939 if(server != NULL)
\r
1940 server->step(0.1);
\r
1946 catch(con::PeerNotFoundException &e)
\r
1948 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1950 error_message = L"Connection timed out.";
\r
1951 gui_loadingtext->remove();
\r
1958 /*scene::ISceneNode* skybox;
\r
1959 skybox = smgr->addSkyBoxSceneNode(
\r
1960 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1961 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1962 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1963 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1964 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1965 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1968 Create the camera node
\r
1971 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1972 0, // Camera parent
\r
1973 v3f(BS*100, BS*2, BS*100), // Look from
\r
1974 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1978 if(camera == NULL)
\r
1981 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
1982 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
1983 video::SColor skycolor = video::SColor(255,120,185,244);
\r
1985 camera->setFOV(FOV_ANGLE);
\r
1987 // Just so big a value that everything rendered is visible
\r
1988 camera->setFarValue(100000*BS);
\r
1991 Lighting test code. Doesn't quite work this way.
\r
1992 The CPU-computed lighting is good.
\r
1996 smgr->addLightSceneNode(NULL,
\r
1997 v3f(0, BS*1000000, 0),
\r
1998 video::SColorf(0.3,0.3,0.3),
\r
2001 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2003 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2004 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2007 f32 camera_yaw = 0; // "right/left"
\r
2008 f32 camera_pitch = 0; // "up/down"
\r
2014 gui_loadingtext->remove();
\r
2017 Add some gui stuff
\r
2020 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2021 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2022 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2023 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2025 // Test the text input system
\r
2026 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2028 /*GUIMessageMenu *menu =
\r
2029 new GUIMessageMenu(guienv, guiroot, -1,
\r
2034 // Launch pause menu
\r
2035 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2036 &g_menumgr))->drop();
\r
2039 guitext2->setVisible(true);
\r
2040 guitext_info->setVisible(true);
\r
2041 guitext_chat->setVisible(true);
\r
2043 //s32 guitext_chat_pad_bottom = 70;
\r
2045 v2u32 screensize(0,0);
\r
2046 v2u32 last_screensize(0,0);
\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() && kill == false)
\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 last_screensize = screensize;
\r
2094 screensize = driver->getScreenSize();
\r
2095 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2096 //bool screensize_changed = screensize != last_screensize;
\r
2098 // Hilight boxes collected during the loop and displayed
\r
2099 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2102 std::wstring infotext;
\r
2104 // When screen size changes, update positions and sizes of stuff
\r
2105 /*if(screensize_changed)
\r
2107 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2108 quick_inventory->updatePosition(pos);
\r
2111 //TimeTaker //timer1("//timer1");
\r
2113 // Time of frame without fps limit
\r
2117 // not using getRealTime is necessary for wine
\r
2118 u32 time = device->getTimer()->getTime();
\r
2119 if(time > lasttime)
\r
2120 busytime_u32 = time - lasttime;
\r
2123 busytime = busytime_u32 / 1000.0;
\r
2126 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2128 // Absolutelu necessary for wine!
\r
2135 updateViewingRange(busytime, &client);
\r
2142 float fps_max = g_settings.getFloat("fps_max");
\r
2143 u32 frametime_min = 1000./fps_max;
\r
2145 if(busytime_u32 < frametime_min)
\r
2147 u32 sleeptime = frametime_min - busytime_u32;
\r
2148 device->sleep(sleeptime);
\r
2152 // Absolutelu necessary for wine!
\r
2156 Time difference calculation
\r
2158 f32 dtime; // in seconds
\r
2160 u32 time = device->getTimer()->getTime();
\r
2161 if(time > lasttime)
\r
2162 dtime = (time - lasttime) / 1000.0;
\r
2168 Log frametime for visualization
\r
2170 frametime_log.push_back(dtime);
\r
2171 if(frametime_log.size() > 100)
\r
2173 core::list<float>::Iterator i = frametime_log.begin();
\r
2174 frametime_log.erase(i);
\r
2178 Visualize frametime in terminal
\r
2180 /*for(u32 i=0; i<dtime*400; i++)
\r
2182 std::cout<<std::endl;*/
\r
2185 Time average and jitter calculation
\r
2188 static f32 dtime_avg1 = 0.0;
\r
2189 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2190 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2192 static f32 dtime_jitter1_max_sample = 0.0;
\r
2193 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2195 static f32 jitter1_max = 0.0;
\r
2196 static f32 counter = 0.0;
\r
2197 if(dtime_jitter1 > jitter1_max)
\r
2198 jitter1_max = dtime_jitter1;
\r
2203 dtime_jitter1_max_sample = jitter1_max;
\r
2204 dtime_jitter1_max_fraction
\r
2205 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2206 jitter1_max = 0.0;
\r
2211 Busytime average and jitter calculation
\r
2214 static f32 busytime_avg1 = 0.0;
\r
2215 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2216 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2218 static f32 busytime_jitter1_max_sample = 0.0;
\r
2219 static f32 busytime_jitter1_min_sample = 0.0;
\r
2221 static f32 jitter1_max = 0.0;
\r
2222 static f32 jitter1_min = 0.0;
\r
2223 static f32 counter = 0.0;
\r
2224 if(busytime_jitter1 > jitter1_max)
\r
2225 jitter1_max = busytime_jitter1;
\r
2226 if(busytime_jitter1 < jitter1_min)
\r
2227 jitter1_min = busytime_jitter1;
\r
2229 if(counter > 0.0){
\r
2231 busytime_jitter1_max_sample = jitter1_max;
\r
2232 busytime_jitter1_min_sample = jitter1_min;
\r
2233 jitter1_max = 0.0;
\r
2234 jitter1_min = 0.0;
\r
2239 Debug info for client
\r
2242 static float counter = 0.0;
\r
2247 client.printDebugInfo(std::cout);
\r
2252 Input handler step()
\r
2254 g_input->step(dtime);
\r
2257 Player speed control
\r
2266 bool a_superspeed,
\r
2270 PlayerControl control(
\r
2271 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2272 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2273 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2274 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2275 g_input->isKeyDown(irr::KEY_SPACE),
\r
2276 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2277 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2278 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2282 client.setPlayerControl(control);
\r
2286 Process environment
\r
2290 //TimeTaker timer("client.step(dtime)");
\r
2291 client.step(dtime);
\r
2292 //client.step(dtime_avg1);
\r
2295 if(server != NULL)
\r
2297 //TimeTaker timer("server->step(dtime)");
\r
2298 server->step(dtime);
\r
2301 v3f player_position = client.getPlayerPosition();
\r
2303 //TimeTaker //timer2("//timer2");
\r
2306 Mouse and camera control
\r
2309 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2312 device->getCursorControl()->setVisible(false);
\r
2314 if(first_loop_after_window_activation){
\r
2315 //std::cout<<"window active, first loop"<<std::endl;
\r
2316 first_loop_after_window_activation = false;
\r
2319 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2320 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2321 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2322 camera_yaw -= dx*0.2;
\r
2323 camera_pitch += dy*0.2;
\r
2324 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2325 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2327 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2330 device->getCursorControl()->setVisible(true);
\r
2332 //std::cout<<"window inactive"<<std::endl;
\r
2333 first_loop_after_window_activation = true;
\r
2336 camera_yaw = wrapDegrees(camera_yaw);
\r
2337 camera_pitch = wrapDegrees(camera_pitch);
\r
2339 v3f camera_direction = v3f(0,0,1);
\r
2340 camera_direction.rotateYZBy(camera_pitch);
\r
2341 camera_direction.rotateXZBy(camera_yaw);
\r
2343 // This is at the height of the eyes of the current figure
\r
2344 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2345 // This is more like in minecraft
\r
2346 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2348 camera->setPosition(camera_position);
\r
2349 // *100.0 helps in large map coordinates
\r
2350 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2352 if(FIELD_OF_VIEW_TEST){
\r
2353 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2356 //TimeTaker timer("client.updateCamera");
\r
2357 client.updateCamera(camera_position, camera_direction);
\r
2361 //TimeTaker //timer3("//timer3");
\r
2364 Calculate what block is the crosshair pointing to
\r
2367 //u32 t1 = device->getTimer()->getRealTime();
\r
2369 //f32 d = 4; // max. distance
\r
2370 f32 d = 4; // max. distance
\r
2371 core::line3d<f32> shootline(camera_position,
\r
2372 camera_position + camera_direction * BS * (d+1));
\r
2374 MapBlockObject *selected_object = client.getSelectedObject
\r
2375 (d*BS, camera_position, shootline);
\r
2378 If it's pointing to a MapBlockObject
\r
2381 if(selected_object != NULL)
\r
2383 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2385 core::aabbox3d<f32> box_on_map
\r
2386 = selected_object->getSelectionBoxOnMap();
\r
2388 hilightboxes.push_back(box_on_map);
\r
2390 infotext = narrow_to_wide(selected_object->infoText());
\r
2392 if(g_input->getLeftClicked())
\r
2394 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2395 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2396 selected_object->getId(), g_selected_item);
\r
2398 else if(g_input->getRightClicked())
\r
2400 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2402 Check if we want to modify the object ourselves
\r
2404 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2406 dstream<<"Sign object right-clicked"<<std::endl;
\r
2408 if(random_input == false)
\r
2410 // Get a new text for it
\r
2412 TextDest *dest = new TextDestSign(
\r
2413 selected_object->getBlock()->getPos(),
\r
2414 selected_object->getId(),
\r
2417 SignObject *sign_object = (SignObject*)selected_object;
\r
2419 std::wstring wtext =
\r
2420 narrow_to_wide(sign_object->getText());
\r
2422 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2428 Otherwise pass the event to the server as-is
\r
2432 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2433 selected_object->getId(), g_selected_item);
\r
2437 else // selected_object == NULL
\r
2441 Find out which node we are pointing at
\r
2444 bool nodefound = false;
\r
2446 v3s16 neighbourpos;
\r
2447 core::aabbox3d<f32> nodehilightbox;
\r
2448 f32 mindistance = BS * 1001;
\r
2450 v3s16 pos_i = floatToInt(player_position, BS);
\r
2452 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2456 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2457 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2458 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2459 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2460 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2461 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2463 for(s16 y = ystart; y <= yend; y++)
\r
2464 for(s16 z = zstart; z <= zend; z++)
\r
2465 for(s16 x = xstart; x <= xend; x++)
\r
2470 n = client.getNode(v3s16(x,y,z));
\r
2471 if(content_pointable(n.d) == false)
\r
2474 catch(InvalidPositionException &e)
\r
2480 v3f npf = intToFloat(np, BS);
\r
2485 v3s16(0,0,1), // back
\r
2486 v3s16(0,1,0), // top
\r
2487 v3s16(1,0,0), // right
\r
2488 v3s16(0,0,-1), // front
\r
2489 v3s16(0,-1,0), // bottom
\r
2490 v3s16(-1,0,0), // left
\r
2496 if(n.d == CONTENT_TORCH)
\r
2498 v3s16 dir = unpackDir(n.dir);
\r
2499 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2500 dir_f *= BS/2 - BS/6 - BS/20;
\r
2501 v3f cpf = npf + dir_f;
\r
2502 f32 distance = (cpf - camera_position).getLength();
\r
2504 core::aabbox3d<f32> box;
\r
2507 if(dir == v3s16(0,-1,0))
\r
2509 box = core::aabbox3d<f32>(
\r
2510 npf - v3f(BS/6, BS/2, BS/6),
\r
2511 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2515 else if(dir == v3s16(0,1,0))
\r
2517 box = core::aabbox3d<f32>(
\r
2518 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2519 npf + v3f(BS/6, BS/2, BS/6)
\r
2525 box = core::aabbox3d<f32>(
\r
2526 cpf - v3f(BS/6, BS/3, BS/6),
\r
2527 cpf + v3f(BS/6, BS/3, BS/6)
\r
2531 if(distance < mindistance)
\r
2533 if(box.intersectsWithLine(shootline))
\r
2537 neighbourpos = np;
\r
2538 mindistance = distance;
\r
2539 nodehilightbox = box;
\r
2548 for(u16 i=0; i<6; i++)
\r
2550 v3f dir_f = v3f(dirs[i].X,
\r
2551 dirs[i].Y, dirs[i].Z);
\r
2552 v3f centerpoint = npf + dir_f * BS/2;
\r
2554 (centerpoint - camera_position).getLength();
\r
2556 if(distance < mindistance)
\r
2558 core::CMatrix4<f32> m;
\r
2559 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2561 // This is the back face
\r
2562 v3f corners[2] = {
\r
2563 v3f(BS/2, BS/2, BS/2),
\r
2564 v3f(-BS/2, -BS/2, BS/2+d)
\r
2567 for(u16 j=0; j<2; j++)
\r
2569 m.rotateVect(corners[j]);
\r
2570 corners[j] += npf;
\r
2573 core::aabbox3d<f32> facebox(corners[0]);
\r
2574 facebox.addInternalPoint(corners[1]);
\r
2576 if(facebox.intersectsWithLine(shootline))
\r
2580 neighbourpos = np + dirs[i];
\r
2581 mindistance = distance;
\r
2583 //nodehilightbox = facebox;
\r
2585 const float d = 0.502;
\r
2586 core::aabbox3d<f32> nodebox
\r
2587 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2588 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2589 nodebox.MinEdge += nodepos_f;
\r
2590 nodebox.MaxEdge += nodepos_f;
\r
2591 nodehilightbox = nodebox;
\r
2593 } // if distance < mindistance
\r
2595 } // regular block
\r
2598 static float nodig_delay_counter = 0.0;
\r
2602 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2604 static float dig_time = 0.0;
\r
2605 static u16 dig_index = 0;
\r
2607 // Visualize selection
\r
2609 hilightboxes.push_back(nodehilightbox);
\r
2613 if(g_input->getLeftReleased())
\r
2615 client.clearTempMod(nodepos);
\r
2619 if(nodig_delay_counter > 0.0)
\r
2621 nodig_delay_counter -= dtime;
\r
2625 if(nodepos != nodepos_old)
\r
2627 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2628 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2630 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2632 client.clearTempMod(nodepos_old);
\r
2637 if(g_input->getLeftClicked() ||
\r
2638 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2640 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2641 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2643 if(g_input->getLeftClicked())
\r
2645 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2647 if(g_input->getLeftState())
\r
2649 MapNode n = client.getNode(nodepos);
\r
2651 // Get tool name. Default is "" = bare hands
\r
2652 std::string toolname = "";
\r
2653 InventoryList *mlist = local_inventory.getList("main");
\r
2656 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2657 if(item && (std::string)item->getName() == "ToolItem")
\r
2659 ToolItem *titem = (ToolItem*)item;
\r
2660 toolname = titem->getToolName();
\r
2664 // Get digging properties for material and tool
\r
2665 u8 material = n.d;
\r
2666 DiggingProperties prop =
\r
2667 getDiggingProperties(material, toolname);
\r
2669 float dig_time_complete = 0.0;
\r
2671 if(prop.diggable == false)
\r
2673 /*dstream<<"Material "<<(int)material
\r
2674 <<" not diggable with \""
\r
2675 <<toolname<<"\""<<std::endl;*/
\r
2676 // I guess nobody will wait for this long
\r
2677 dig_time_complete = 10000000.0;
\r
2681 dig_time_complete = prop.time;
\r
2684 if(dig_time_complete >= 0.001)
\r
2686 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2687 * dig_time/dig_time_complete);
\r
2689 // This is for torches
\r
2692 dig_index = CRACK_ANIMATION_LENGTH;
\r
2695 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2697 //TimeTaker timer("client.setTempMod");
\r
2698 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2699 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2703 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2704 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2705 client.clearTempMod(nodepos);
\r
2706 client.removeNode(nodepos);
\r
2710 nodig_delay_counter = dig_time_complete
\r
2711 / (float)CRACK_ANIMATION_LENGTH;
\r
2713 // We don't want a corresponding delay to
\r
2714 // very time consuming nodes
\r
2715 if(nodig_delay_counter > 0.5)
\r
2717 nodig_delay_counter = 0.5;
\r
2719 // We want a slight delay to very little
\r
2720 // time consuming nodes
\r
2721 float mindelay = 0.15;
\r
2722 if(nodig_delay_counter < mindelay)
\r
2724 nodig_delay_counter = mindelay;
\r
2728 dig_time += dtime;
\r
2732 if(g_input->getRightClicked())
\r
2734 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2735 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2738 nodepos_old = nodepos;
\r
2743 } // selected_object == NULL
\r
2745 g_input->resetLeftClicked();
\r
2746 g_input->resetRightClicked();
\r
2748 if(g_input->getLeftReleased())
\r
2750 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2752 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2754 if(g_input->getRightReleased())
\r
2756 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2760 g_input->resetLeftReleased();
\r
2761 g_input->resetRightReleased();
\r
2764 Calculate stuff for drawing
\r
2767 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2769 u32 daynight_ratio = client.getDayNightRatio();
\r
2770 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2771 video::SColor bgcolor = video::SColor(
\r
2773 skycolor.getRed() * l / 255,
\r
2774 skycolor.getGreen() * l / 255,
\r
2775 skycolor.getBlue() * l / 255);
\r
2781 if(g_settings.getBool("enable_fog") == true)
\r
2783 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2784 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2785 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2786 if(draw_control.range_all)
\r
2787 range = 100000*BS;
\r
2791 video::EFT_FOG_LINEAR,
\r
2795 false, // pixel fog
\r
2796 false // range fog
\r
2803 video::EFT_FOG_LINEAR,
\r
2807 false, // pixel fog
\r
2808 false // range fog
\r
2814 Update gui stuff (0ms)
\r
2817 //TimeTaker guiupdatetimer("Gui updating");
\r
2820 static float drawtime_avg = 0;
\r
2821 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2822 static float beginscenetime_avg = 0;
\r
2823 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2824 static float scenetime_avg = 0;
\r
2825 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2826 static float endscenetime_avg = 0;
\r
2827 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2829 char temptext[300];
\r
2830 snprintf(temptext, 300, "Minetest-c55 ("
\r
2832 ", R: range_all=%i"
\r
2834 " drawtime=%.0f, beginscenetime=%.0f"
\r
2835 ", scenetime=%.0f, endscenetime=%.0f",
\r
2837 draw_control.range_all,
\r
2839 beginscenetime_avg,
\r
2844 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2848 char temptext[300];
\r
2849 snprintf(temptext, 300,
\r
2850 "(% .1f, % .1f, % .1f)"
\r
2851 " (% .3f < btime_jitter < % .3f"
\r
2852 ", dtime_jitter = % .1f %%"
\r
2853 ", v_range = %.1f)",
\r
2854 player_position.X/BS,
\r
2855 player_position.Y/BS,
\r
2856 player_position.Z/BS,
\r
2857 busytime_jitter1_min_sample,
\r
2858 busytime_jitter1_max_sample,
\r
2859 dtime_jitter1_max_fraction * 100.0,
\r
2860 draw_control.wanted_range
\r
2863 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2867 guitext_info->setText(infotext.c_str());
\r
2871 Get chat messages from client
\r
2874 // Get new messages
\r
2875 std::wstring message;
\r
2876 while(client.getChatMessage(message))
\r
2878 chat_lines.push_back(ChatLine(message));
\r
2879 /*if(chat_lines.size() > 6)
\r
2881 core::list<ChatLine>::Iterator
\r
2882 i = chat_lines.begin();
\r
2883 chat_lines.erase(i);
\r
2886 // Append them to form the whole static text and throw
\r
2887 // it to the gui element
\r
2888 std::wstring whole;
\r
2889 // This will correspond to the line number counted from
\r
2890 // top to bottom, from size-1 to 0
\r
2891 s16 line_number = chat_lines.size();
\r
2892 // Count of messages to be removed from the top
\r
2893 u16 to_be_removed_count = 0;
\r
2894 for(core::list<ChatLine>::Iterator
\r
2895 i = chat_lines.begin();
\r
2896 i != chat_lines.end(); i++)
\r
2898 // After this, line number is valid for this loop
\r
2901 (*i).age += dtime;
\r
2903 This results in a maximum age of 60*6 to the
\r
2904 lowermost line and a maximum of 6 lines
\r
2906 float allowed_age = (6-line_number) * 60.0;
\r
2908 if((*i).age > allowed_age)
\r
2910 to_be_removed_count++;
\r
2913 whole += (*i).text + L'\n';
\r
2915 for(u16 i=0; i<to_be_removed_count; i++)
\r
2917 core::list<ChatLine>::Iterator
\r
2918 it = chat_lines.begin();
\r
2919 chat_lines.erase(it);
\r
2921 guitext_chat->setText(whole.c_str());
\r
2923 // Update gui element size and position
\r
2925 /*core::rect<s32> rect(
\r
2927 screensize.Y - guitext_chat_pad_bottom
\r
2928 - text_height*chat_lines.size(),
\r
2929 screensize.X - 10,
\r
2930 screensize.Y - guitext_chat_pad_bottom
\r
2932 core::rect<s32> rect(
\r
2935 screensize.X - 10,
\r
2936 50 + text_height*chat_lines.size()
\r
2939 guitext_chat->setRelativePosition(rect);
\r
2941 if(chat_lines.size() == 0)
\r
2942 guitext_chat->setVisible(false);
\r
2944 guitext_chat->setVisible(true);
\r
2951 static u16 old_selected_item = 65535;
\r
2952 if(client.getLocalInventoryUpdated()
\r
2953 || g_selected_item != old_selected_item)
\r
2955 old_selected_item = g_selected_item;
\r
2956 //std::cout<<"Updating local inventory"<<std::endl;
\r
2957 client.getLocalInventory(local_inventory);
\r
2958 /*quick_inventory->setSelection(g_selected_item);
\r
2959 quick_inventory->update();*/
\r
2963 Send actions returned by the inventory menu
\r
2965 while(inventory_action_queue.size() != 0)
\r
2967 InventoryAction *a = inventory_action_queue.pop_front();
\r
2969 client.sendInventoryAction(a);
\r
2978 TimeTaker drawtimer("Drawing");
\r
2982 TimeTaker timer("beginScene");
\r
2983 driver->beginScene(true, true, bgcolor);
\r
2984 //driver->beginScene(false, true, bgcolor);
\r
2985 beginscenetime = timer.stop(true);
\r
2990 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2993 TimeTaker timer("smgr");
\r
2995 scenetime = timer.stop(true);
\r
2999 //TimeTaker timer9("auxiliary drawings");
\r
3003 //TimeTaker //timer10("//timer10");
\r
3005 video::SMaterial m;
\r
3006 //m.Thickness = 10;
\r
3008 m.Lighting = false;
\r
3009 driver->setMaterial(m);
\r
3011 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3013 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3014 i != hilightboxes.end(); i++)
\r
3016 /*std::cout<<"hilightbox min="
\r
3017 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3019 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3021 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3027 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3028 displaycenter + core::vector2d<s32>(10,0),
\r
3029 video::SColor(255,255,255,255));
\r
3030 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3031 displaycenter + core::vector2d<s32>(0,10),
\r
3032 video::SColor(255,255,255,255));
\r
3037 if(g_settings.getBool("frametime_graph") == true)
\r
3040 for(core::list<float>::Iterator
\r
3041 i = frametime_log.begin();
\r
3042 i != frametime_log.end();
\r
3045 driver->draw2DLine(v2s32(x,50),
\r
3046 v2s32(x,50+(*i)*1000),
\r
3047 video::SColor(255,255,255,255));
\r
3055 //TimeTaker //timer11("//timer11");
\r
3061 guienv->drawAll();
\r
3067 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3068 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3073 TimeTaker timer("endScene");
\r
3074 driver->endScene();
\r
3075 endscenetime = timer.stop(true);
\r
3078 drawtime = drawtimer.stop(true);
\r
3084 static s16 lastFPS = 0;
\r
3085 //u16 fps = driver->getFPS();
\r
3086 u16 fps = (1.0/dtime_avg1);
\r
3088 if (lastFPS != fps)
\r
3090 core::stringw str = L"Minetest [";
\r
3091 str += driver->getName();
\r
3095 device->setWindowCaption(str.c_str());
\r
3101 device->yield();*/
\r
3104 //delete quick_inventory;
\r
3107 Disable texture fetches and other stuff that is queued
\r
3108 to be processed by the main loop.
\r
3110 This has to be done before client goes out of scope.
\r
3112 g_irrlicht->Shutdown(true);
\r
3114 } // client and server are deleted at this point
\r
3117 catch(con::PeerNotFoundException &e)
\r
3119 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3120 error_message = L"Connection timed out.";
\r
3123 } // Menu-game loop
\r
3128 In the end, delete the Irrlicht device.
\r
3133 Update configuration file
\r
3135 /*if(configpath != "")
\r
3137 g_settings.updateConfigFile(configpath.c_str());
\r
3140 END_DEBUG_EXCEPTION_HANDLER
\r
3142 debugstreams_deinit();
\r