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: Better handling of objects and mobs
\r
204 - There has to be some way to do it with less messy code
\r
205 - Make separate classes for client and server
\r
206 - Client should not discriminate between blocks, server should
\r
207 - Make other players utilize the same framework
\r
208 - This is also needed for objects that don't get sent to client
\r
209 but are used for triggers etc
\r
211 TODO: There has to be some better way to handle static objects than to
\r
212 send them all the time. This affects signs and item objects.
\r
213 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
214 need an additional metadata field for the texts
\r
215 - This is also needed for item container chests
\r
217 Block object server side:
\r
218 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
219 - For all blocks in the buffer, objects are stepped(). This
\r
220 means they are active.
\r
221 - TODO: A global active buffer is needed for the server
\r
222 - TODO: A timestamp to blocks
\r
223 - TODO: All blocks going in and out of the buffer are recorded.
\r
224 - TODO: For outgoing blocks, timestamp is written.
\r
225 - TODO: For incoming blocks, time difference is calculated and
\r
226 objects are stepped according to it.
\r
228 - When an active object goes far from a player, either delete
\r
229 it or store it statically.
\r
230 - When a statically stored active object comes near a player,
\r
231 recreate the active object
\r
236 TODO: Mineral and ground material properties
\r
237 - This way mineral ground toughness can be calculated with just
\r
238 some formula, as well as tool strengths
\r
240 TODO: Flowing water to actually contain flow direction information
\r
242 TODO: Remove duplicate lighting implementation from Map (leave
\r
243 VoxelManipulator, which is faster)
\r
245 FEATURE: Create a system that allows a huge amount of different "map
\r
246 generator modules/filters"
\r
248 FEATURE: Erosion simulation at map generation time
\r
249 - Simulate water flows, which would carve out dirt fast and
\r
250 then turn stone into gravel and sand and relocate it.
\r
251 - How about relocating minerals, too? Coal and gold in
\r
252 downstream sand and gravel would be kind of cool
\r
253 - This would need a better way of handling minerals, mainly
\r
254 to have mineral content as a separate field. the first
\r
255 parameter field is free for this.
\r
256 - Simulate rock falling from cliffs when water has removed
\r
257 enough solid rock from the bottom
\r
259 Doing now (most important at the top):
\r
260 --------------------------------------
\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
272 * Check the fixmes in the list above
\r
274 === Making it more portable
\r
275 * Some MSVC: std::sto* are defined without a namespace and collide
\r
276 with the ones in utility.h
\r
279 * Add mud underground
\r
280 * Make an "environment metafile" to store at least time of day
\r
281 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
282 - Or maybe move content_features to material.{h,cpp}?
\r
283 * Add some kind of erosion and other stuff that now is possible
\r
284 * Make client to fetch stuff asynchronously
\r
285 - Needs method SyncProcessData
\r
286 * Fix the problem with the server constantly saving one or a few
\r
287 blocks? List the first saved block, maybe it explains.
\r
288 - It is probably caused by oscillating water
\r
289 * Water doesn't start flowing after map generation like it should
\r
290 - Are there still problems?
\r
291 * Better water generation (spread it to underwater caverns but don't
\r
292 fill dungeons that don't touch big water masses)
\r
293 * When generating a chunk and the neighboring chunk doesn't have mud
\r
294 and stuff yet and the ground is fairly flat, the mud will flow to
\r
295 the other chunk making nasty straight walls when the other chunk
\r
296 is generated. Fix it.
\r
297 * Make a small history check to transformLiquids to detect and log
\r
298 continuous oscillations, in such detail that they can be fixed.
\r
299 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
301 ======================================================================
\r
306 Setting this to 1 enables a special camera mode that forces
\r
307 the renderers to think that the camera statically points from
\r
308 the starting place to a static direction.
\r
310 This allows one to move around with the player and see what
\r
311 is actually drawn behind solid things and behind the player.
\r
313 #define FIELD_OF_VIEW_TEST 0
\r
317 #pragma message ("Disabling unit tests")
\r
319 #warning "Disabling unit tests"
\r
321 // Disable unit tests
\r
322 #define ENABLE_TESTS 0
\r
324 // Enable unit tests
\r
325 #define ENABLE_TESTS 1
\r
329 #pragma comment(lib, "Irrlicht.lib")
\r
330 //#pragma comment(lib, "jthread.lib")
\r
331 #pragma comment(lib, "zlibwapi.lib")
\r
332 #pragma comment(lib, "Shell32.lib")
\r
333 // This would get rid of the console window
\r
334 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
337 #include <iostream>
\r
339 #include <jmutexautolock.h>
\r
340 #include <locale.h>
\r
342 #include "common_irrlicht.h"
\r
345 #include "player.h"
\r
347 //#include "environment.h"
\r
348 #include "server.h"
\r
349 #include "client.h"
\r
350 //#include "serialization.h"
\r
351 #include "constants.h"
\r
352 //#include "strfnd.h"
\r
353 #include "porting.h"
\r
354 #include "irrlichtwrapper.h"
\r
355 #include "gettime.h"
\r
356 #include "porting.h"
\r
357 #include "guiPauseMenu.h"
\r
358 #include "guiInventoryMenu.h"
\r
359 #include "guiTextInputMenu.h"
\r
360 #include "materials.h"
\r
361 #include "guiMessageMenu.h"
\r
362 #include "filesys.h"
\r
363 #include "config.h"
\r
364 #include "guiMainMenu.h"
\r
365 #include "mineral.h"
\r
369 // TODO: Remove this
\r
370 IrrlichtWrapper *g_irrlicht = NULL;
\r
372 // This makes textures
\r
373 ITextureSource *g_texturesource = NULL;
\r
375 MapDrawControl draw_control;
\r
379 These are loaded from the config file.
\r
382 Settings g_settings;
\r
384 extern void set_default_settings();
\r
390 IrrlichtDevice *g_device = NULL;
\r
391 Client *g_client = NULL;
\r
393 /*const s16 quickinv_size = 40;
\r
394 const s16 quickinv_padding = 8;
\r
395 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
396 const s16 quickinv_outer_padding = 4;
\r
397 const s16 quickinv_itemcount = 8;*/
\r
399 const s32 hotbar_itemcount = 8;
\r
400 const s32 hotbar_imagesize = 36;
\r
406 gui::IGUIEnvironment* guienv = NULL;
\r
407 gui::IGUIStaticText *guiroot = NULL;
\r
409 class MainMenuManager : public IMenuManager
\r
412 virtual void createdMenu(GUIModalMenu *menu)
\r
414 for(core::list<GUIModalMenu*>::Iterator
\r
415 i = m_stack.begin();
\r
416 i != m_stack.end(); i++)
\r
418 assert(*i != menu);
\r
421 if(m_stack.size() != 0)
\r
422 (*m_stack.getLast())->setVisible(false);
\r
423 m_stack.push_back(menu);
\r
426 virtual void deletingMenu(GUIModalMenu *menu)
\r
428 // Remove all entries if there are duplicates
\r
429 bool removed_entry;
\r
431 removed_entry = false;
\r
432 for(core::list<GUIModalMenu*>::Iterator
\r
433 i = m_stack.begin();
\r
434 i != m_stack.end(); i++)
\r
439 removed_entry = true;
\r
443 }while(removed_entry);
\r
445 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
446 assert(*i == menu);
\r
447 m_stack.erase(i);*/
\r
449 if(m_stack.size() != 0)
\r
450 (*m_stack.getLast())->setVisible(true);
\r
455 return m_stack.size();
\r
458 core::list<GUIModalMenu*> m_stack;
\r
461 MainMenuManager g_menumgr;
\r
463 bool noMenuActive()
\r
465 return (g_menumgr.menuCount() == 0);
\r
468 bool g_disconnect_requested = false;
\r
470 class MainGameCallback : public IGameCallback
\r
473 virtual void exitToOS()
\r
475 g_device->closeDevice();
\r
478 virtual void disconnect()
\r
480 g_disconnect_requested = true;
\r
484 MainGameCallback g_gamecallback;
\r
486 // Inventory actions from the menu are buffered here before sending
\r
487 Queue<InventoryAction*> inventory_action_queue;
\r
488 // This is a copy of the inventory that the client's environment has
\r
489 Inventory local_inventory;
\r
491 u16 g_selected_item = 0;
\r
498 std::ostream *dout_con_ptr = &dummyout;
\r
499 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
500 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
501 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
502 //std::ostream *dout_con_ptr = &dstream;
\r
503 //std::ostream *derr_con_ptr = &dstream;
\r
506 std::ostream *dout_server_ptr = &dstream;
\r
507 std::ostream *derr_server_ptr = &dstream;
\r
510 std::ostream *dout_client_ptr = &dstream;
\r
511 std::ostream *derr_client_ptr = &dstream;
\r
514 gettime.h implementation
\r
520 Use irrlicht because it is more precise than porting.h's
\r
523 if(g_irrlicht == NULL)
\r
525 return g_irrlicht->getTime();
\r
532 struct TextDestSign : public TextDest
\r
534 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
536 m_blockpos = blockpos;
\r
540 void gotText(std::wstring text)
\r
542 std::string ntext = wide_to_narrow(text);
\r
543 dstream<<"Changing text of a sign object: "
\r
544 <<ntext<<std::endl;
\r
545 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
553 struct TextDestChat : public TextDest
\r
555 TextDestChat(Client *client)
\r
559 void gotText(std::wstring text)
\r
561 // Discard empty line
\r
565 // Parse command (server command starts with "/#")
\r
566 if(text[0] == L'/' && text[1] != L'#')
\r
568 std::wstring reply = L"Local: ";
\r
570 reply += L"Local commands not yet supported. "
\r
571 L"Server prefix is \"/#\".";
\r
573 m_client->addChatMessage(reply);
\r
578 m_client->sendChatMessage(text);
\r
580 m_client->addChatMessage(text);
\r
586 class MyEventReceiver : public IEventReceiver
\r
589 // This is the one method that we have to implement
\r
590 virtual bool OnEvent(const SEvent& event)
\r
593 React to nothing here if a menu is active
\r
595 if(noMenuActive() == false)
\r
601 // Remember whether each key is down or up
\r
602 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
604 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
606 if(event.KeyInput.PressedDown)
\r
608 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
614 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
616 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
618 dstream<<DTIME<<"MyEventReceiver: "
\r
619 <<"Launching pause menu"<<std::endl;
\r
620 // It will delete itself by itself
\r
621 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
622 &g_menumgr))->drop();
\r
625 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
627 dstream<<DTIME<<"MyEventReceiver: "
\r
628 <<"Launching inventory"<<std::endl;
\r
629 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
630 &local_inventory, &inventory_action_queue,
\r
631 &g_menumgr))->drop();
\r
634 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
636 TextDest *dest = new TextDestChat(g_client);
\r
638 (new GUITextInputMenu(guienv, guiroot, -1,
\r
645 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
646 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
648 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
649 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
651 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
652 g_selected_item = s1-1;
\r
653 dstream<<DTIME<<"Selected item: "
\r
654 <<g_selected_item<<std::endl;
\r
657 // Viewing range selection
\r
658 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
660 if(draw_control.range_all)
\r
662 draw_control.range_all = false;
\r
663 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
667 draw_control.range_all = true;
\r
668 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
672 // Print debug stacks
\r
673 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
675 dstream<<"-----------------------------------------"
\r
677 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
678 dstream<<"-----------------------------------------"
\r
680 debug_stacks_print();
\r
685 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
687 if(noMenuActive() == false)
\r
689 left_active = false;
\r
690 middle_active = false;
\r
691 right_active = false;
\r
695 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
696 left_active = event.MouseInput.isLeftPressed();
\r
697 middle_active = event.MouseInput.isMiddlePressed();
\r
698 right_active = event.MouseInput.isRightPressed();
\r
700 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
702 leftclicked = true;
\r
704 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
706 rightclicked = true;
\r
708 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
710 leftreleased = true;
\r
712 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
714 rightreleased = true;
\r
716 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
718 /*dstream<<"event.MouseInput.Wheel="
\r
719 <<event.MouseInput.Wheel<<std::endl;*/
\r
721 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
722 hotbar_itemcount-1);
\r
723 if(event.MouseInput.Wheel < 0)
\r
725 if(g_selected_item < max_item)
\r
728 g_selected_item = 0;
\r
730 else if(event.MouseInput.Wheel > 0)
\r
732 if(g_selected_item > 0)
\r
735 g_selected_item = max_item;
\r
744 // This is used to check whether a key is being held down
\r
745 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
747 return keyIsDown[keyCode];
\r
752 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
753 keyIsDown[i] = false;
\r
755 leftclicked = false;
\r
756 rightclicked = false;
\r
757 leftreleased = false;
\r
758 rightreleased = false;
\r
760 left_active = false;
\r
761 middle_active = false;
\r
762 right_active = false;
\r
773 bool rightreleased;
\r
776 bool middle_active;
\r
780 // We use this array to store the current state of each key
\r
781 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
784 IrrlichtDevice *m_device;
\r
793 virtual ~InputHandler()
\r
797 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
799 virtual v2s32 getMousePos() = 0;
\r
800 virtual void setMousePos(s32 x, s32 y) = 0;
\r
802 virtual bool getLeftState() = 0;
\r
803 virtual bool getRightState() = 0;
\r
805 virtual bool getLeftClicked() = 0;
\r
806 virtual bool getRightClicked() = 0;
\r
807 virtual void resetLeftClicked() = 0;
\r
808 virtual void resetRightClicked() = 0;
\r
810 virtual bool getLeftReleased() = 0;
\r
811 virtual bool getRightReleased() = 0;
\r
812 virtual void resetLeftReleased() = 0;
\r
813 virtual void resetRightReleased() = 0;
\r
815 virtual void step(float dtime) {};
\r
817 virtual void clear() {};
\r
820 InputHandler *g_input = NULL;
\r
822 class RealInputHandler : public InputHandler
\r
825 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
827 m_receiver(receiver)
\r
830 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
832 return m_receiver->IsKeyDown(keyCode);
\r
834 virtual v2s32 getMousePos()
\r
836 return m_device->getCursorControl()->getPosition();
\r
838 virtual void setMousePos(s32 x, s32 y)
\r
840 m_device->getCursorControl()->setPosition(x, y);
\r
843 virtual bool getLeftState()
\r
845 return m_receiver->left_active;
\r
847 virtual bool getRightState()
\r
849 return m_receiver->right_active;
\r
852 virtual bool getLeftClicked()
\r
854 return m_receiver->leftclicked;
\r
856 virtual bool getRightClicked()
\r
858 return m_receiver->rightclicked;
\r
860 virtual void resetLeftClicked()
\r
862 m_receiver->leftclicked = false;
\r
864 virtual void resetRightClicked()
\r
866 m_receiver->rightclicked = false;
\r
869 virtual bool getLeftReleased()
\r
871 return m_receiver->leftreleased;
\r
873 virtual bool getRightReleased()
\r
875 return m_receiver->rightreleased;
\r
877 virtual void resetLeftReleased()
\r
879 m_receiver->leftreleased = false;
\r
881 virtual void resetRightReleased()
\r
883 m_receiver->rightreleased = false;
\r
888 resetRightClicked();
\r
889 resetLeftClicked();
\r
892 IrrlichtDevice *m_device;
\r
893 MyEventReceiver *m_receiver;
\r
896 class RandomInputHandler : public InputHandler
\r
899 RandomInputHandler()
\r
903 leftclicked = false;
\r
904 rightclicked = false;
\r
905 leftreleased = false;
\r
906 rightreleased = false;
\r
907 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
908 keydown[i] = false;
\r
910 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
912 return keydown[keyCode];
\r
914 virtual v2s32 getMousePos()
\r
918 virtual void setMousePos(s32 x, s32 y)
\r
920 mousepos = v2s32(x,y);
\r
923 virtual bool getLeftState()
\r
927 virtual bool getRightState()
\r
932 virtual bool getLeftClicked()
\r
934 return leftclicked;
\r
936 virtual bool getRightClicked()
\r
938 return rightclicked;
\r
940 virtual void resetLeftClicked()
\r
942 leftclicked = false;
\r
944 virtual void resetRightClicked()
\r
946 rightclicked = false;
\r
949 virtual bool getLeftReleased()
\r
951 return leftreleased;
\r
953 virtual bool getRightReleased()
\r
955 return rightreleased;
\r
957 virtual void resetLeftReleased()
\r
959 leftreleased = false;
\r
961 virtual void resetRightReleased()
\r
963 rightreleased = false;
\r
966 virtual void step(float dtime)
\r
969 static float counter1 = 0;
\r
973 counter1 = 0.1*Rand(1, 40);
\r
974 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
978 static float counter1 = 0;
\r
982 counter1 = 0.1*Rand(1, 40);
\r
983 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
987 static float counter1 = 0;
\r
991 counter1 = 0.1*Rand(1, 40);
\r
992 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
996 static float counter1 = 0;
\r
1000 counter1 = 0.1*Rand(1, 40);
\r
1001 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1005 static float counter1 = 0;
\r
1006 counter1 -= dtime;
\r
1007 if(counter1 < 0.0)
\r
1009 counter1 = 0.1*Rand(1, 20);
\r
1010 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1014 static float counter1 = 0;
\r
1015 counter1 -= dtime;
\r
1016 if(counter1 < 0.0)
\r
1018 counter1 = 0.1*Rand(1, 30);
\r
1019 leftdown = !leftdown;
\r
1021 leftclicked = true;
\r
1023 leftreleased = true;
\r
1027 static float counter1 = 0;
\r
1028 counter1 -= dtime;
\r
1029 if(counter1 < 0.0)
\r
1031 counter1 = 0.1*Rand(1, 15);
\r
1032 rightdown = !rightdown;
\r
1034 rightclicked = true;
\r
1036 rightreleased = true;
\r
1039 mousepos += mousespeed;
\r
1042 s32 Rand(s32 min, s32 max)
\r
1044 return (myrand()%(max-min+1))+min;
\r
1047 bool keydown[KEY_KEY_CODES_COUNT];
\r
1053 bool rightclicked;
\r
1054 bool leftreleased;
\r
1055 bool rightreleased;
\r
1058 void updateViewingRange(f32 frametime_in, Client *client)
\r
1060 if(draw_control.range_all == true)
\r
1063 static f32 added_frametime = 0;
\r
1064 static s16 added_frames = 0;
\r
1066 added_frametime += frametime_in;
\r
1067 added_frames += 1;
\r
1069 // Actually this counter kind of sucks because frametime is busytime
\r
1070 static f32 counter = 0;
\r
1071 counter -= frametime_in;
\r
1077 /*dstream<<__FUNCTION_NAME
\r
1078 <<": Collected "<<added_frames<<" frames, total of "
\r
1079 <<added_frametime<<"s."<<std::endl;*/
\r
1081 /*dstream<<"draw_control.blocks_drawn="
\r
1082 <<draw_control.blocks_drawn
\r
1083 <<", draw_control.blocks_would_have_drawn="
\r
1084 <<draw_control.blocks_would_have_drawn
\r
1087 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1088 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1090 draw_control.wanted_min_range = range_min;
\r
1091 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1093 float block_draw_ratio = 1.0;
\r
1094 if(draw_control.blocks_would_have_drawn != 0)
\r
1096 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1097 / (float)draw_control.blocks_would_have_drawn;
\r
1100 // Calculate the average frametime in the case that all wanted
\r
1101 // blocks had been drawn
\r
1102 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1104 added_frametime = 0.0;
\r
1107 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1108 float wanted_frametime = 1.0 / wanted_fps;
\r
1110 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1111 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1113 // If needed frametime change is very small, just return
\r
1114 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1116 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1120 float range = draw_control.wanted_range;
\r
1121 float new_range = range;
\r
1123 static s16 range_old = 0;
\r
1124 static f32 frametime_old = 0;
\r
1126 float d_range = range - range_old;
\r
1127 f32 d_frametime = frametime - frametime_old;
\r
1128 // A sane default of 30ms per 50 nodes of range
\r
1129 static f32 time_per_range = 30. / 50;
\r
1132 time_per_range = d_frametime / d_range;
\r
1135 // The minimum allowed calculated frametime-range derivative:
\r
1136 // Practically this sets the maximum speed of changing the range.
\r
1137 // The lower this value, the higher the maximum changing speed.
\r
1138 // A low value here results in wobbly range (0.001)
\r
1139 // A high value here results in slow changing range (0.0025)
\r
1140 // SUGG: This could be dynamically adjusted so that when
\r
1141 // the camera is turning, this is lower
\r
1142 //float min_time_per_range = 0.0015;
\r
1143 float min_time_per_range = 0.0010;
\r
1144 //float min_time_per_range = 0.05 / range;
\r
1145 if(time_per_range < min_time_per_range)
\r
1147 time_per_range = min_time_per_range;
\r
1148 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1152 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1155 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1156 // Dampen the change a bit to kill oscillations
\r
1157 //wanted_range_change *= 0.9;
\r
1158 //wanted_range_change *= 0.75;
\r
1159 wanted_range_change *= 0.5;
\r
1160 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1162 // If needed range change is very small, just return
\r
1163 if(fabs(wanted_range_change) < 0.001)
\r
1165 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1169 new_range += wanted_range_change;
\r
1170 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1172 //float new_range_unclamped = new_range;
\r
1173 if(new_range < range_min)
\r
1174 new_range = range_min;
\r
1175 if(new_range > range_max)
\r
1176 new_range = range_max;
\r
1178 /*if(new_range != new_range_unclamped)
\r
1179 dstream<<", clamped to "<<new_range<<std::endl;
\r
1181 dstream<<std::endl;*/
\r
1183 draw_control.wanted_range = new_range;
\r
1185 range_old = new_range;
\r
1186 frametime_old = frametime;
\r
1191 class GUIQuickInventory
\r
1194 GUIQuickInventory(
\r
1195 gui::IGUIEnvironment* env,
\r
1196 gui::IGUIElement* parent,
\r
1199 Inventory *inventory):
\r
1200 m_itemcount(itemcount),
\r
1201 m_inventory(inventory)
\r
1203 core::rect<s32> imgsize(0,0,quickinv_size,quickinv_size);
\r
1204 core::rect<s32> textsize(0,0,quickinv_size,quickinv_size);
\r
1205 bgtext = env->addStaticText(L"", core::rect<s32>(0,0,1,1), false, false);
\r
1206 bgtext->setBackgroundColor(
\r
1207 video::SColor(128,0,0,0));
\r
1208 for(s32 i=0; i<m_itemcount; i++)
\r
1210 m_images.push_back(env->addImage(
\r
1213 m_images[i]->setScaleImage(true);
\r
1214 m_texts.push_back(env->addStaticText(
\r
1219 /*m_texts[i]->setBackgroundColor(
\r
1220 video::SColor(128,0,0,0));*/
\r
1221 m_texts[i]->setTextAlignment(
\r
1222 gui::EGUIA_LOWERRIGHT,
\r
1223 gui::EGUIA_LOWERRIGHT);
\r
1226 updatePosition(pos);
\r
1229 ~GUIQuickInventory()
\r
1231 for(u32 i=0; i<m_texts.size(); i++)
\r
1233 m_texts[i]->remove();
\r
1235 for(u32 i=0; i<m_images.size(); i++)
\r
1237 m_images[i]->remove();
\r
1242 void updatePosition(v2s32 pos)
\r
1244 v2s32 spacing(quickinv_spacing, 0);
\r
1245 for(s32 i=0; i<m_itemcount; i++)
\r
1247 m_images[i]->setRelativePosition(pos + spacing*i);
\r
1248 m_texts[i]->setRelativePosition(pos + spacing*i);
\r
1250 core::rect<s32> bgrect(-quickinv_outer_padding,-quickinv_outer_padding,
\r
1251 (quickinv_itemcount-1)*quickinv_spacing+quickinv_size+quickinv_outer_padding,
\r
1252 quickinv_size+quickinv_outer_padding);
\r
1253 bgtext->setRelativePosition(bgrect+pos);
\r
1256 void setSelection(s32 i)
\r
1265 //start = m_selection - m_itemcount / 2;
\r
1267 InventoryList *mainlist = m_inventory->getList("main");
\r
1269 for(s32 i=0; i<m_itemcount; i++)
\r
1271 s32 j = i + start;
\r
1273 if(j > (s32)mainlist->getSize() - 1)
\r
1274 j -= mainlist->getSize();
\r
1276 j += mainlist->getSize();
\r
1278 InventoryItem *item = mainlist->getItem(j);
\r
1282 m_images[i]->setImage(NULL);
\r
1284 if(m_selection == j)
\r
1285 m_texts[i]->setText(L"->");
\r
1287 m_texts[i]->setText(L"");
\r
1289 // The next ifs will segfault with a NULL pointer
\r
1294 m_images[i]->setImage(item->getImage());
\r
1296 std::ostringstream os;
\r
1297 if(m_selection == j)
\r
1299 os<<item->getText();
\r
1300 m_texts[i]->setText(narrow_to_wide(os.str()).c_str());
\r
1303 if(m_selection == j)
\r
1304 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1306 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1307 m_texts[i]->setText(t);*/
\r
1313 gui::IGUIStaticText *bgtext;
\r
1314 core::array<gui::IGUIStaticText*> m_texts;
\r
1315 core::array<gui::IGUIImage*> m_images;
\r
1316 Inventory *m_inventory;
\r
1321 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1322 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1323 Inventory *inventory)
\r
1325 InventoryList *mainlist = inventory->getList("main");
\r
1326 if(mainlist == NULL)
\r
1328 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1332 s32 padding = imgsize/12;
\r
1333 //s32 height = imgsize + padding*2;
\r
1334 s32 width = itemcount*(imgsize+padding*2);
\r
1336 // Position of upper left corner of bar
\r
1337 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1339 // Draw background color
\r
1340 /*core::rect<s32> barrect(0,0,width,height);
\r
1342 video::SColor bgcolor(255,128,128,128);
\r
1343 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1345 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1347 for(s32 i=0; i<itemcount; i++)
\r
1349 InventoryItem *item = mainlist->getItem(i);
\r
1351 core::rect<s32> rect = imgrect + pos
\r
1352 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1354 if(g_selected_item == i)
\r
1356 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1357 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1358 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1363 video::SColor bgcolor2(128,0,0,0);
\r
1364 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1369 drawInventoryItem(driver, font, item, rect, NULL);
\r
1381 ChatLine(const std::wstring &a_text):
\r
1387 std::wstring text;
\r
1390 // These are defined global so that they're not optimized too much.
\r
1391 // Can't change them to volatile.
\r
1396 std::string tempstring;
\r
1397 std::string tempstring2;
\r
1402 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1403 TimeTaker timer("Testing std::string speed");
\r
1404 const u32 jj = 10000;
\r
1405 for(u32 j=0; j<jj; j++)
\r
1409 const u32 ii = 10;
\r
1410 for(u32 i=0; i<ii; i++){
\r
1411 tempstring2 += "asd";
\r
1413 for(u32 i=0; i<ii+1; i++){
\r
1414 tempstring += "asd";
\r
1415 if(tempstring == tempstring2)
\r
1421 dstream<<"All of the following tests should take around 100ms each."
\r
1425 TimeTaker timer("Testing floating-point conversion speed");
\r
1427 for(u32 i=0; i<4000000; i++){
\r
1434 TimeTaker timer("Testing floating-point vector speed");
\r
1436 tempv3f1 = v3f(1,2,3);
\r
1437 tempv3f2 = v3f(4,5,6);
\r
1438 for(u32 i=0; i<10000000; i++){
\r
1439 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1440 tempv3f2 += v3f(7,8,9);
\r
1445 TimeTaker timer("Testing core::map speed");
\r
1447 core::map<v2s16, f32> map1;
\r
1450 for(s16 y=0; y<ii; y++){
\r
1451 for(s16 x=0; x<ii; x++){
\r
1452 map1.insert(v2s16(x,y), tempf);
\r
1456 for(s16 y=ii-1; y>=0; y--){
\r
1457 for(s16 x=0; x<ii; x++){
\r
1458 tempf = map1[v2s16(x,y)];
\r
1464 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1465 TimeTaker timer("Testing mutex speed");
\r
1478 // Do at least 10ms
\r
1479 while(timer.getTime() < 10);
\r
1481 u32 dtime = timer.stop();
\r
1482 u32 per_ms = n / dtime;
\r
1483 std::cout<<"Done. "<<dtime<<"ms, "
\r
1484 <<per_ms<<"/ms"<<std::endl;
\r
1488 int main(int argc, char *argv[])
\r
1491 Parse command line
\r
1494 // List all allowed options
\r
1495 core::map<std::string, ValueSpec> allowed_options;
\r
1496 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1497 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1498 "Run server directly"));
\r
1499 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1500 "Load configuration from specified file"));
\r
1501 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1502 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1503 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1504 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1505 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1506 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1508 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1510 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1512 Settings cmd_args;
\r
1514 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1516 if(ret == false || cmd_args.getFlag("help"))
\r
1518 dstream<<"Allowed options:"<<std::endl;
\r
1519 for(core::map<std::string, ValueSpec>::Iterator
\r
1520 i = allowed_options.getIterator();
\r
1521 i.atEnd() == false; i++)
\r
1523 dstream<<" --"<<i.getNode()->getKey();
\r
1524 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1529 dstream<<" <value>";
\r
1531 dstream<<std::endl;
\r
1533 if(i.getNode()->getValue().help != NULL)
\r
1535 dstream<<" "<<i.getNode()->getValue().help
\r
1540 return cmd_args.getFlag("help") ? 0 : 1;
\r
1544 Low-level initialization
\r
1547 bool disable_stderr = false;
\r
1549 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1550 disable_stderr = true;
\r
1553 // Initialize debug streams
\r
1554 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1555 // Initialize debug stacks
\r
1556 debug_stacks_init();
\r
1558 DSTACK(__FUNCTION_NAME);
\r
1560 porting::signal_handler_init();
\r
1561 bool &kill = *porting::signal_handler_killstatus();
\r
1563 porting::initializePaths();
\r
1564 // Create user data directory
\r
1565 fs::CreateDir(porting::path_userdata);
\r
1567 // C-style stuff initialization
\r
1568 initializeMaterialProperties();
\r
1571 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1573 // Print startup message
\r
1574 dstream<<DTIME<<"minetest-c55"
\r
1575 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1576 <<", "<<BUILD_INFO
\r
1580 Basic initialization
\r
1583 // Initialize default settings
\r
1584 set_default_settings();
\r
1586 // Set locale. This is for forcing '.' as the decimal point.
\r
1587 std::locale::global(std::locale("C"));
\r
1588 // This enables printing all characters in bitmap font
\r
1589 setlocale(LC_CTYPE, "en_US");
\r
1591 // Initialize sockets
\r
1593 atexit(sockets_cleanup);
\r
1603 // Path of configuration file in use
\r
1604 std::string configpath = "";
\r
1606 if(cmd_args.exists("config"))
\r
1608 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1611 dstream<<"Could not read configuration from \""
\r
1612 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1615 configpath = cmd_args.get("config");
\r
1619 core::array<std::string> filenames;
\r
1620 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1621 #ifdef RUN_IN_PLACE
\r
1622 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1625 for(u32 i=0; i<filenames.size(); i++)
\r
1627 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1630 configpath = filenames[i];
\r
1635 // If no path found, use the first one (menu creates the file)
\r
1636 if(configpath == "")
\r
1637 configpath = filenames[0];
\r
1640 // Initialize random seed
\r
1645 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1647 These are needed for unit tests at least.
\r
1650 // Initial call with g_texturesource not set.
\r
1657 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1658 || cmd_args.getFlag("enable-unittests") == true)
\r
1663 /*for(s16 y=-100; y<100; y++)
\r
1664 for(s16 x=-100; x<100; x++)
\r
1666 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1676 if(cmd_args.exists("port"))
\r
1677 port = cmd_args.getU16("port");
\r
1678 else if(cmd_args.exists("port"))
\r
1679 port = g_settings.getU16("port");
\r
1682 std::string map_dir = porting::path_userdata+"/map";
\r
1683 if(cmd_args.exists("map-dir"))
\r
1684 map_dir = cmd_args.get("map-dir");
\r
1685 else if(g_settings.exists("map-dir"))
\r
1686 map_dir = g_settings.get("map-dir");
\r
1688 // Run dedicated server if asked to
\r
1689 if(cmd_args.getFlag("server"))
\r
1691 DSTACK("Dedicated server branch");
\r
1694 Server server(map_dir.c_str());
\r
1695 server.start(port);
\r
1698 dedicated_server_loop(server, kill);
\r
1707 // Address to connect to
\r
1708 std::string address = "";
\r
1710 if(cmd_args.exists("address"))
\r
1712 address = cmd_args.get("address");
\r
1716 address = g_settings.get("address");
\r
1719 std::string playername = g_settings.get("name");
\r
1721 // Resolution selection
\r
1723 bool fullscreen = false;
\r
1724 u16 screenW = g_settings.getU16("screenW");
\r
1725 u16 screenH = g_settings.getU16("screenH");
\r
1727 // Determine driver
\r
1729 video::E_DRIVER_TYPE driverType;
\r
1731 std::string driverstring = g_settings.get("video_driver");
\r
1733 if(driverstring == "null")
\r
1734 driverType = video::EDT_NULL;
\r
1735 else if(driverstring == "software")
\r
1736 driverType = video::EDT_SOFTWARE;
\r
1737 else if(driverstring == "burningsvideo")
\r
1738 driverType = video::EDT_BURNINGSVIDEO;
\r
1739 else if(driverstring == "direct3d8")
\r
1740 driverType = video::EDT_DIRECT3D8;
\r
1741 else if(driverstring == "direct3d9")
\r
1742 driverType = video::EDT_DIRECT3D9;
\r
1743 else if(driverstring == "opengl")
\r
1744 driverType = video::EDT_OPENGL;
\r
1747 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1748 "to opengl"<<std::endl;
\r
1749 driverType = video::EDT_OPENGL;
\r
1752 // create device and exit if creation failed
\r
1754 MyEventReceiver receiver;
\r
1756 IrrlichtDevice *device;
\r
1757 device = createDevice(driverType,
\r
1758 core::dimension2d<u32>(screenW, screenH),
\r
1759 16, fullscreen, false, false, &receiver);
\r
1762 return 1; // could not create selected driver.
\r
1764 g_device = device;
\r
1765 g_irrlicht = new IrrlichtWrapper(device);
\r
1766 TextureSource *texturesource = new TextureSource(device);
\r
1767 g_texturesource = texturesource;
\r
1770 Speed tests (done after irrlicht is loaded to get timer)
\r
1772 if(cmd_args.getFlag("speedtests"))
\r
1774 dstream<<"Running speed tests"<<std::endl;
\r
1779 device->setResizable(true);
\r
1781 bool random_input = g_settings.getBool("random_input")
\r
1782 || cmd_args.getFlag("random-input");
\r
1784 g_input = new RandomInputHandler();
\r
1786 g_input = new RealInputHandler(device, &receiver);
\r
1789 Continue initialization
\r
1792 video::IVideoDriver* driver = device->getVideoDriver();
\r
1795 This changes the minimum allowed number of vertices in a VBO.
\r
1798 //driver->setMinHardwareBufferVertexCount(50);
\r
1800 scene::ISceneManager* smgr = device->getSceneManager();
\r
1802 guienv = device->getGUIEnvironment();
\r
1803 gui::IGUISkin* skin = guienv->getSkin();
\r
1804 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1806 skin->setFont(font);
\r
1808 dstream<<"WARNING: Font file was not found."
\r
1809 " Using default font."<<std::endl;
\r
1810 // If font was not found, this will get us one
\r
1811 font = skin->getFont();
\r
1814 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1815 dstream<<"text_height="<<text_height<<std::endl;
\r
1817 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1818 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1819 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1820 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1821 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1822 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1825 Preload some textures and stuff
\r
1828 init_content_inventory_texture_paths();
\r
1829 init_mapnode(); // Second call with g_texturesource set
\r
1837 We need some kind of a root node to be able to add
\r
1838 custom gui elements directly on the screen.
\r
1839 Otherwise they won't be automatically drawn.
\r
1841 guiroot = guienv->addStaticText(L"",
\r
1842 core::rect<s32>(0, 0, 10000, 10000));
\r
1844 // First line of debug text
\r
1845 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1847 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1849 // Second line of debug text
\r
1850 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1852 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1855 // At the middle of the screen
\r
1856 // Object infos are shown in this
\r
1857 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1859 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1863 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1865 core::rect<s32>(0,0,0,0),
\r
1866 false, false); // Disable word wrap as of now
\r
1868 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1869 core::list<ChatLine> chat_lines;
\r
1872 If an error occurs, this is set to something and the
\r
1873 menu-game loop is restarted. It is then displayed before
\r
1876 std::wstring error_message = L"";
\r
1881 while(g_device->run() && kill == false)
\r
1884 // This is used for catching disconnects
\r
1889 Out-of-game menu loop.
\r
1891 Loop quits when menu returns proper parameters.
\r
1893 while(kill == false)
\r
1895 // Cursor can be non-visible when coming from the game
\r
1896 device->getCursorControl()->setVisible(true);
\r
1897 // Some stuff are left to scene manager when coming from the game
\r
1898 // (map at least?)
\r
1900 // Reset or hide the debug gui texts
\r
1901 guitext->setText(L"Minetest-c55");
\r
1902 guitext2->setVisible(false);
\r
1903 guitext_info->setVisible(false);
\r
1904 guitext_chat->setVisible(false);
\r
1906 // Initialize menu data
\r
1907 MainMenuData menudata;
\r
1908 menudata.address = narrow_to_wide(address);
\r
1909 menudata.name = narrow_to_wide(playername);
\r
1910 menudata.port = narrow_to_wide(itos(port));
\r
1911 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1913 GUIMainMenu *menu =
\r
1914 new GUIMainMenu(guienv, guiroot, -1,
\r
1915 &g_menumgr, &menudata, &g_gamecallback);
\r
1916 menu->allowFocusRemoval(true);
\r
1918 if(error_message != L"")
\r
1920 GUIMessageMenu *menu2 =
\r
1921 new GUIMessageMenu(guienv, guiroot, -1,
\r
1922 &g_menumgr, error_message.c_str());
\r
1924 error_message = L"";
\r
1927 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1929 dstream<<"Created main menu"<<std::endl;
\r
1931 while(g_device->run())
\r
1933 // Run global IrrlichtWrapper's main thread processing stuff
\r
1934 g_irrlicht->Run();
\r
1936 if(menu->getStatus() == true)
\r
1939 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1940 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1941 guienv->drawAll();
\r
1942 driver->endScene();
\r
1945 // Break out of menu-game loop to shut down cleanly
\r
1946 if(g_device->run() == false)
\r
1949 dstream<<"Dropping main menu"<<std::endl;
\r
1953 // Delete map if requested
\r
1954 if(menudata.delete_map)
\r
1956 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1958 error_message = L"Delete failed";
\r
1962 playername = wide_to_narrow(menudata.name);
\r
1963 address = wide_to_narrow(menudata.address);
\r
1964 port = stoi(wide_to_narrow(menudata.port));
\r
1965 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1967 // Check for valid parameters, restart menu if invalid.
\r
1968 if(playername == "")
\r
1970 error_message = L"Name required.";
\r
1975 g_settings.set("name", playername);
\r
1976 g_settings.set("address", address);
\r
1977 g_settings.set("port", itos(port));
\r
1978 // Update configuration file
\r
1979 if(configpath != "")
\r
1980 g_settings.updateConfigFile(configpath.c_str());
\r
1982 // Continue to game
\r
1986 // Break out of menu-game loop to shut down cleanly
\r
1987 if(g_device->run() == false)
\r
1991 Make a scope here so that the client and the server and other
\r
1992 stuff gets removed when disconnected or the irrlicht device
\r
1997 // This is set to true at the end of the scope
\r
1998 g_irrlicht->Shutdown(false);
\r
2001 Draw "Loading" screen
\r
2003 const wchar_t *text = L"Loading and connecting...";
\r
2004 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2005 core::vector2d<s32> textsize(300, text_height);
\r
2006 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2008 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2009 text, textrect, false, false);
\r
2010 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2012 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2013 guienv->drawAll();
\r
2014 driver->endScene();
\r
2016 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2020 SharedPtr will delete it when it goes out of scope.
\r
2022 SharedPtr<Server> server;
\r
2023 if(address == ""){
\r
2024 server = new Server(map_dir);
\r
2025 server->start(port);
\r
2032 Client client(device, playername.c_str(), draw_control);
\r
2034 g_client = &client;
\r
2036 Address connect_address(0,0,0,0, port);
\r
2039 //connect_address.Resolve("localhost");
\r
2040 connect_address.setAddress(127,0,0,1);
\r
2042 connect_address.Resolve(address.c_str());
\r
2044 catch(ResolveError &e)
\r
2046 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2048 error_message = L"Couldn't resolve address";
\r
2049 gui_loadingtext->remove();
\r
2053 dstream<<DTIME<<"Connecting to server at ";
\r
2054 connect_address.print(&dstream);
\r
2055 dstream<<std::endl;
\r
2056 client.connect(connect_address);
\r
2059 while(client.connectedAndInitialized() == false)
\r
2062 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2063 guienv->drawAll();
\r
2064 driver->endScene();
\r
2066 // Update client and server
\r
2070 if(server != NULL)
\r
2071 server->step(0.1);
\r
2077 catch(con::PeerNotFoundException &e)
\r
2079 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2081 error_message = L"Connection timed out.";
\r
2082 gui_loadingtext->remove();
\r
2089 /*scene::ISceneNode* skybox;
\r
2090 skybox = smgr->addSkyBoxSceneNode(
\r
2091 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2092 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2093 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2094 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2095 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2096 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2099 Create the camera node
\r
2102 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2103 0, // Camera parent
\r
2104 v3f(BS*100, BS*2, BS*100), // Look from
\r
2105 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2109 if(camera == NULL)
\r
2112 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2113 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2114 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2116 camera->setFOV(FOV_ANGLE);
\r
2118 // Just so big a value that everything rendered is visible
\r
2119 camera->setFarValue(100000*BS);
\r
2122 Lighting test code. Doesn't quite work this way.
\r
2123 The CPU-computed lighting is good.
\r
2127 smgr->addLightSceneNode(NULL,
\r
2128 v3f(0, BS*1000000, 0),
\r
2129 video::SColorf(0.3,0.3,0.3),
\r
2132 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2134 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2135 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2138 f32 camera_yaw = 0; // "right/left"
\r
2139 f32 camera_pitch = 0; // "up/down"
\r
2145 gui_loadingtext->remove();
\r
2148 Add some gui stuff
\r
2151 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2152 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2153 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2154 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2156 // Test the text input system
\r
2157 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2159 /*GUIMessageMenu *menu =
\r
2160 new GUIMessageMenu(guienv, guiroot, -1,
\r
2165 // Launch pause menu
\r
2166 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2167 &g_menumgr))->drop();
\r
2170 guitext2->setVisible(true);
\r
2171 guitext_info->setVisible(true);
\r
2172 guitext_chat->setVisible(true);
\r
2174 //s32 guitext_chat_pad_bottom = 70;
\r
2176 v2u32 screensize(0,0);
\r
2177 v2u32 last_screensize(0,0);
\r
2180 Some statistics are collected in these
\r
2183 u32 beginscenetime = 0;
\r
2184 u32 scenetime = 0;
\r
2185 u32 endscenetime = 0;
\r
2188 //throw con::PeerNotFoundException("lol");
\r
2190 core::list<float> frametime_log;
\r
2196 bool first_loop_after_window_activation = true;
\r
2198 // Time is in milliseconds
\r
2199 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2200 // NOTE: So we have to use getTime() and call run()s between them
\r
2201 u32 lasttime = device->getTimer()->getTime();
\r
2203 while(device->run() && kill == false)
\r
2205 if(g_disconnect_requested)
\r
2207 g_disconnect_requested = false;
\r
2212 Run global IrrlichtWrapper's main thread processing stuff
\r
2214 g_irrlicht->Run();
\r
2217 Process TextureSource's queue
\r
2219 texturesource->processQueue();
\r
2222 Random calculations
\r
2224 last_screensize = screensize;
\r
2225 screensize = driver->getScreenSize();
\r
2226 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2227 //bool screensize_changed = screensize != last_screensize;
\r
2229 // Hilight boxes collected during the loop and displayed
\r
2230 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2233 std::wstring infotext;
\r
2235 // When screen size changes, update positions and sizes of stuff
\r
2236 /*if(screensize_changed)
\r
2238 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2239 quick_inventory->updatePosition(pos);
\r
2242 //TimeTaker //timer1("//timer1");
\r
2244 // Time of frame without fps limit
\r
2248 // not using getRealTime is necessary for wine
\r
2249 u32 time = device->getTimer()->getTime();
\r
2250 if(time > lasttime)
\r
2251 busytime_u32 = time - lasttime;
\r
2254 busytime = busytime_u32 / 1000.0;
\r
2257 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2259 // Absolutelu necessary for wine!
\r
2266 updateViewingRange(busytime, &client);
\r
2273 float fps_max = g_settings.getFloat("fps_max");
\r
2274 u32 frametime_min = 1000./fps_max;
\r
2276 if(busytime_u32 < frametime_min)
\r
2278 u32 sleeptime = frametime_min - busytime_u32;
\r
2279 device->sleep(sleeptime);
\r
2283 // Absolutelu necessary for wine!
\r
2287 Time difference calculation
\r
2289 f32 dtime; // in seconds
\r
2291 u32 time = device->getTimer()->getTime();
\r
2292 if(time > lasttime)
\r
2293 dtime = (time - lasttime) / 1000.0;
\r
2299 Log frametime for visualization
\r
2301 frametime_log.push_back(dtime);
\r
2302 if(frametime_log.size() > 100)
\r
2304 core::list<float>::Iterator i = frametime_log.begin();
\r
2305 frametime_log.erase(i);
\r
2309 Visualize frametime in terminal
\r
2311 /*for(u32 i=0; i<dtime*400; i++)
\r
2313 std::cout<<std::endl;*/
\r
2316 Time average and jitter calculation
\r
2319 static f32 dtime_avg1 = 0.0;
\r
2320 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2321 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2323 static f32 dtime_jitter1_max_sample = 0.0;
\r
2324 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2326 static f32 jitter1_max = 0.0;
\r
2327 static f32 counter = 0.0;
\r
2328 if(dtime_jitter1 > jitter1_max)
\r
2329 jitter1_max = dtime_jitter1;
\r
2334 dtime_jitter1_max_sample = jitter1_max;
\r
2335 dtime_jitter1_max_fraction
\r
2336 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2337 jitter1_max = 0.0;
\r
2342 Busytime average and jitter calculation
\r
2345 static f32 busytime_avg1 = 0.0;
\r
2346 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2347 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2349 static f32 busytime_jitter1_max_sample = 0.0;
\r
2350 static f32 busytime_jitter1_min_sample = 0.0;
\r
2352 static f32 jitter1_max = 0.0;
\r
2353 static f32 jitter1_min = 0.0;
\r
2354 static f32 counter = 0.0;
\r
2355 if(busytime_jitter1 > jitter1_max)
\r
2356 jitter1_max = busytime_jitter1;
\r
2357 if(busytime_jitter1 < jitter1_min)
\r
2358 jitter1_min = busytime_jitter1;
\r
2360 if(counter > 0.0){
\r
2362 busytime_jitter1_max_sample = jitter1_max;
\r
2363 busytime_jitter1_min_sample = jitter1_min;
\r
2364 jitter1_max = 0.0;
\r
2365 jitter1_min = 0.0;
\r
2370 Debug info for client
\r
2373 static float counter = 0.0;
\r
2378 client.printDebugInfo(std::cout);
\r
2383 Input handler step()
\r
2385 g_input->step(dtime);
\r
2388 Player speed control
\r
2397 bool a_superspeed,
\r
2401 PlayerControl control(
\r
2402 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2403 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2404 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2405 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2406 g_input->isKeyDown(irr::KEY_SPACE),
\r
2407 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2408 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2409 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2413 client.setPlayerControl(control);
\r
2417 Process environment
\r
2421 //TimeTaker timer("client.step(dtime)");
\r
2422 client.step(dtime);
\r
2423 //client.step(dtime_avg1);
\r
2426 if(server != NULL)
\r
2428 //TimeTaker timer("server->step(dtime)");
\r
2429 server->step(dtime);
\r
2432 v3f player_position = client.getPlayerPosition();
\r
2434 //TimeTaker //timer2("//timer2");
\r
2437 Mouse and camera control
\r
2440 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2443 device->getCursorControl()->setVisible(false);
\r
2445 if(first_loop_after_window_activation){
\r
2446 //std::cout<<"window active, first loop"<<std::endl;
\r
2447 first_loop_after_window_activation = false;
\r
2450 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2451 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2452 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2453 camera_yaw -= dx*0.2;
\r
2454 camera_pitch += dy*0.2;
\r
2455 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2456 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2458 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2461 device->getCursorControl()->setVisible(true);
\r
2463 //std::cout<<"window inactive"<<std::endl;
\r
2464 first_loop_after_window_activation = true;
\r
2467 camera_yaw = wrapDegrees(camera_yaw);
\r
2468 camera_pitch = wrapDegrees(camera_pitch);
\r
2470 v3f camera_direction = v3f(0,0,1);
\r
2471 camera_direction.rotateYZBy(camera_pitch);
\r
2472 camera_direction.rotateXZBy(camera_yaw);
\r
2474 // This is at the height of the eyes of the current figure
\r
2475 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2476 // This is more like in minecraft
\r
2477 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2479 camera->setPosition(camera_position);
\r
2480 // *100.0 helps in large map coordinates
\r
2481 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2483 if(FIELD_OF_VIEW_TEST){
\r
2484 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2487 //TimeTaker timer("client.updateCamera");
\r
2488 client.updateCamera(camera_position, camera_direction);
\r
2492 //TimeTaker //timer3("//timer3");
\r
2495 Calculate what block is the crosshair pointing to
\r
2498 //u32 t1 = device->getTimer()->getRealTime();
\r
2500 //f32 d = 4; // max. distance
\r
2501 f32 d = 4; // max. distance
\r
2502 core::line3d<f32> shootline(camera_position,
\r
2503 camera_position + camera_direction * BS * (d+1));
\r
2505 MapBlockObject *selected_object = client.getSelectedObject
\r
2506 (d*BS, camera_position, shootline);
\r
2509 If it's pointing to a MapBlockObject
\r
2512 if(selected_object != NULL)
\r
2514 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2516 core::aabbox3d<f32> box_on_map
\r
2517 = selected_object->getSelectionBoxOnMap();
\r
2519 hilightboxes.push_back(box_on_map);
\r
2521 infotext = narrow_to_wide(selected_object->infoText());
\r
2523 if(g_input->getLeftClicked())
\r
2525 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2526 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2527 selected_object->getId(), g_selected_item);
\r
2529 else if(g_input->getRightClicked())
\r
2531 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2533 Check if we want to modify the object ourselves
\r
2535 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2537 dstream<<"Sign object right-clicked"<<std::endl;
\r
2539 if(random_input == false)
\r
2541 // Get a new text for it
\r
2543 TextDest *dest = new TextDestSign(
\r
2544 selected_object->getBlock()->getPos(),
\r
2545 selected_object->getId(),
\r
2548 SignObject *sign_object = (SignObject*)selected_object;
\r
2550 std::wstring wtext =
\r
2551 narrow_to_wide(sign_object->getText());
\r
2553 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2559 Otherwise pass the event to the server as-is
\r
2563 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2564 selected_object->getId(), g_selected_item);
\r
2568 else // selected_object == NULL
\r
2572 Find out which node we are pointing at
\r
2575 bool nodefound = false;
\r
2577 v3s16 neighbourpos;
\r
2578 core::aabbox3d<f32> nodehilightbox;
\r
2579 f32 mindistance = BS * 1001;
\r
2581 v3s16 pos_i = floatToInt(player_position, BS);
\r
2583 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2587 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2588 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2589 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2590 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2591 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2592 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2594 for(s16 y = ystart; y <= yend; y++)
\r
2595 for(s16 z = zstart; z <= zend; z++)
\r
2596 for(s16 x = xstart; x <= xend; x++)
\r
2601 n = client.getNode(v3s16(x,y,z));
\r
2602 if(content_pointable(n.d) == false)
\r
2605 catch(InvalidPositionException &e)
\r
2611 v3f npf = intToFloat(np, BS);
\r
2616 v3s16(0,0,1), // back
\r
2617 v3s16(0,1,0), // top
\r
2618 v3s16(1,0,0), // right
\r
2619 v3s16(0,0,-1), // front
\r
2620 v3s16(0,-1,0), // bottom
\r
2621 v3s16(-1,0,0), // left
\r
2627 if(n.d == CONTENT_TORCH)
\r
2629 v3s16 dir = unpackDir(n.dir);
\r
2630 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2631 dir_f *= BS/2 - BS/6 - BS/20;
\r
2632 v3f cpf = npf + dir_f;
\r
2633 f32 distance = (cpf - camera_position).getLength();
\r
2635 core::aabbox3d<f32> box;
\r
2638 if(dir == v3s16(0,-1,0))
\r
2640 box = core::aabbox3d<f32>(
\r
2641 npf - v3f(BS/6, BS/2, BS/6),
\r
2642 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2646 else if(dir == v3s16(0,1,0))
\r
2648 box = core::aabbox3d<f32>(
\r
2649 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2650 npf + v3f(BS/6, BS/2, BS/6)
\r
2656 box = core::aabbox3d<f32>(
\r
2657 cpf - v3f(BS/6, BS/3, BS/6),
\r
2658 cpf + v3f(BS/6, BS/3, BS/6)
\r
2662 if(distance < mindistance)
\r
2664 if(box.intersectsWithLine(shootline))
\r
2668 neighbourpos = np;
\r
2669 mindistance = distance;
\r
2670 nodehilightbox = box;
\r
2679 for(u16 i=0; i<6; i++)
\r
2681 v3f dir_f = v3f(dirs[i].X,
\r
2682 dirs[i].Y, dirs[i].Z);
\r
2683 v3f centerpoint = npf + dir_f * BS/2;
\r
2685 (centerpoint - camera_position).getLength();
\r
2687 if(distance < mindistance)
\r
2689 core::CMatrix4<f32> m;
\r
2690 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2692 // This is the back face
\r
2693 v3f corners[2] = {
\r
2694 v3f(BS/2, BS/2, BS/2),
\r
2695 v3f(-BS/2, -BS/2, BS/2+d)
\r
2698 for(u16 j=0; j<2; j++)
\r
2700 m.rotateVect(corners[j]);
\r
2701 corners[j] += npf;
\r
2704 core::aabbox3d<f32> facebox(corners[0]);
\r
2705 facebox.addInternalPoint(corners[1]);
\r
2707 if(facebox.intersectsWithLine(shootline))
\r
2711 neighbourpos = np + dirs[i];
\r
2712 mindistance = distance;
\r
2714 //nodehilightbox = facebox;
\r
2716 const float d = 0.502;
\r
2717 core::aabbox3d<f32> nodebox
\r
2718 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2719 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2720 nodebox.MinEdge += nodepos_f;
\r
2721 nodebox.MaxEdge += nodepos_f;
\r
2722 nodehilightbox = nodebox;
\r
2724 } // if distance < mindistance
\r
2726 } // regular block
\r
2729 static float nodig_delay_counter = 0.0;
\r
2733 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2735 static float dig_time = 0.0;
\r
2736 static u16 dig_index = 0;
\r
2738 // Visualize selection
\r
2740 hilightboxes.push_back(nodehilightbox);
\r
2744 if(g_input->getLeftReleased())
\r
2746 client.clearTempMod(nodepos);
\r
2750 if(nodig_delay_counter > 0.0)
\r
2752 nodig_delay_counter -= dtime;
\r
2756 if(nodepos != nodepos_old)
\r
2758 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2759 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2761 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2763 client.clearTempMod(nodepos_old);
\r
2768 if(g_input->getLeftClicked() ||
\r
2769 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2771 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2772 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2774 if(g_input->getLeftClicked())
\r
2776 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2778 if(g_input->getLeftState())
\r
2780 MapNode n = client.getNode(nodepos);
\r
2782 // Get tool name. Default is "" = bare hands
\r
2783 std::string toolname = "";
\r
2784 InventoryList *mlist = local_inventory.getList("main");
\r
2787 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2788 if(item && (std::string)item->getName() == "ToolItem")
\r
2790 ToolItem *titem = (ToolItem*)item;
\r
2791 toolname = titem->getToolName();
\r
2795 // Get digging properties for material and tool
\r
2796 u8 material = n.d;
\r
2797 DiggingProperties prop =
\r
2798 getDiggingProperties(material, toolname);
\r
2800 float dig_time_complete = 0.0;
\r
2802 if(prop.diggable == false)
\r
2804 /*dstream<<"Material "<<(int)material
\r
2805 <<" not diggable with \""
\r
2806 <<toolname<<"\""<<std::endl;*/
\r
2807 // I guess nobody will wait for this long
\r
2808 dig_time_complete = 10000000.0;
\r
2812 dig_time_complete = prop.time;
\r
2815 if(dig_time_complete >= 0.001)
\r
2817 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2818 * dig_time/dig_time_complete);
\r
2820 // This is for torches
\r
2823 dig_index = CRACK_ANIMATION_LENGTH;
\r
2826 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2828 //TimeTaker timer("client.setTempMod");
\r
2829 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2830 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2834 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2835 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2836 client.clearTempMod(nodepos);
\r
2837 client.removeNode(nodepos);
\r
2841 nodig_delay_counter = dig_time_complete
\r
2842 / (float)CRACK_ANIMATION_LENGTH;
\r
2844 // We don't want a corresponding delay to
\r
2845 // very time consuming nodes
\r
2846 if(nodig_delay_counter > 0.5)
\r
2848 nodig_delay_counter = 0.5;
\r
2850 // We want a slight delay to very little
\r
2851 // time consuming nodes
\r
2852 float mindelay = 0.15;
\r
2853 if(nodig_delay_counter < mindelay)
\r
2855 nodig_delay_counter = mindelay;
\r
2859 dig_time += dtime;
\r
2863 if(g_input->getRightClicked())
\r
2865 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2866 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2869 nodepos_old = nodepos;
\r
2874 } // selected_object == NULL
\r
2876 g_input->resetLeftClicked();
\r
2877 g_input->resetRightClicked();
\r
2879 if(g_input->getLeftReleased())
\r
2881 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2883 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2885 if(g_input->getRightReleased())
\r
2887 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2891 g_input->resetLeftReleased();
\r
2892 g_input->resetRightReleased();
\r
2895 Calculate stuff for drawing
\r
2898 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2900 u32 daynight_ratio = client.getDayNightRatio();
\r
2901 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2902 video::SColor bgcolor = video::SColor(
\r
2904 skycolor.getRed() * l / 255,
\r
2905 skycolor.getGreen() * l / 255,
\r
2906 skycolor.getBlue() * l / 255);
\r
2912 if(g_settings.getBool("enable_fog") == true)
\r
2914 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2915 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2916 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2917 if(draw_control.range_all)
\r
2918 range = 100000*BS;
\r
2922 video::EFT_FOG_LINEAR,
\r
2926 false, // pixel fog
\r
2927 false // range fog
\r
2934 video::EFT_FOG_LINEAR,
\r
2938 false, // pixel fog
\r
2939 false // range fog
\r
2945 Update gui stuff (0ms)
\r
2948 //TimeTaker guiupdatetimer("Gui updating");
\r
2951 static float drawtime_avg = 0;
\r
2952 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2953 static float beginscenetime_avg = 0;
\r
2954 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2955 static float scenetime_avg = 0;
\r
2956 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2957 static float endscenetime_avg = 0;
\r
2958 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2960 char temptext[300];
\r
2961 snprintf(temptext, 300, "Minetest-c55 ("
\r
2963 ", R: range_all=%i"
\r
2965 " drawtime=%.0f, beginscenetime=%.0f"
\r
2966 ", scenetime=%.0f, endscenetime=%.0f",
\r
2968 draw_control.range_all,
\r
2970 beginscenetime_avg,
\r
2975 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2979 char temptext[300];
\r
2980 snprintf(temptext, 300,
\r
2981 "(% .1f, % .1f, % .1f)"
\r
2982 " (% .3f < btime_jitter < % .3f"
\r
2983 ", dtime_jitter = % .1f %%"
\r
2984 ", v_range = %.1f)",
\r
2985 player_position.X/BS,
\r
2986 player_position.Y/BS,
\r
2987 player_position.Z/BS,
\r
2988 busytime_jitter1_min_sample,
\r
2989 busytime_jitter1_max_sample,
\r
2990 dtime_jitter1_max_fraction * 100.0,
\r
2991 draw_control.wanted_range
\r
2994 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2998 guitext_info->setText(infotext.c_str());
\r
3002 Get chat messages from client
\r
3005 // Get new messages
\r
3006 std::wstring message;
\r
3007 while(client.getChatMessage(message))
\r
3009 chat_lines.push_back(ChatLine(message));
\r
3010 /*if(chat_lines.size() > 6)
\r
3012 core::list<ChatLine>::Iterator
\r
3013 i = chat_lines.begin();
\r
3014 chat_lines.erase(i);
\r
3017 // Append them to form the whole static text and throw
\r
3018 // it to the gui element
\r
3019 std::wstring whole;
\r
3020 // This will correspond to the line number counted from
\r
3021 // top to bottom, from size-1 to 0
\r
3022 s16 line_number = chat_lines.size();
\r
3023 // Count of messages to be removed from the top
\r
3024 u16 to_be_removed_count = 0;
\r
3025 for(core::list<ChatLine>::Iterator
\r
3026 i = chat_lines.begin();
\r
3027 i != chat_lines.end(); i++)
\r
3029 // After this, line number is valid for this loop
\r
3032 (*i).age += dtime;
\r
3034 This results in a maximum age of 60*6 to the
\r
3035 lowermost line and a maximum of 6 lines
\r
3037 float allowed_age = (6-line_number) * 60.0;
\r
3039 if((*i).age > allowed_age)
\r
3041 to_be_removed_count++;
\r
3044 whole += (*i).text + L'\n';
\r
3046 for(u16 i=0; i<to_be_removed_count; i++)
\r
3048 core::list<ChatLine>::Iterator
\r
3049 it = chat_lines.begin();
\r
3050 chat_lines.erase(it);
\r
3052 guitext_chat->setText(whole.c_str());
\r
3054 // Update gui element size and position
\r
3056 /*core::rect<s32> rect(
\r
3058 screensize.Y - guitext_chat_pad_bottom
\r
3059 - text_height*chat_lines.size(),
\r
3060 screensize.X - 10,
\r
3061 screensize.Y - guitext_chat_pad_bottom
\r
3063 core::rect<s32> rect(
\r
3066 screensize.X - 10,
\r
3067 50 + text_height*chat_lines.size()
\r
3070 guitext_chat->setRelativePosition(rect);
\r
3072 if(chat_lines.size() == 0)
\r
3073 guitext_chat->setVisible(false);
\r
3075 guitext_chat->setVisible(true);
\r
3082 static u16 old_selected_item = 65535;
\r
3083 if(client.getLocalInventoryUpdated()
\r
3084 || g_selected_item != old_selected_item)
\r
3086 old_selected_item = g_selected_item;
\r
3087 //std::cout<<"Updating local inventory"<<std::endl;
\r
3088 client.getLocalInventory(local_inventory);
\r
3089 /*quick_inventory->setSelection(g_selected_item);
\r
3090 quick_inventory->update();*/
\r
3094 Send actions returned by the inventory menu
\r
3096 while(inventory_action_queue.size() != 0)
\r
3098 InventoryAction *a = inventory_action_queue.pop_front();
\r
3100 client.sendInventoryAction(a);
\r
3109 TimeTaker drawtimer("Drawing");
\r
3113 TimeTaker timer("beginScene");
\r
3114 driver->beginScene(true, true, bgcolor);
\r
3115 //driver->beginScene(false, true, bgcolor);
\r
3116 beginscenetime = timer.stop(true);
\r
3121 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3124 TimeTaker timer("smgr");
\r
3126 scenetime = timer.stop(true);
\r
3130 //TimeTaker timer9("auxiliary drawings");
\r
3134 //TimeTaker //timer10("//timer10");
\r
3136 video::SMaterial m;
\r
3137 //m.Thickness = 10;
\r
3139 m.Lighting = false;
\r
3140 driver->setMaterial(m);
\r
3142 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3144 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3145 i != hilightboxes.end(); i++)
\r
3147 /*std::cout<<"hilightbox min="
\r
3148 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3150 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3152 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3158 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3159 displaycenter + core::vector2d<s32>(10,0),
\r
3160 video::SColor(255,255,255,255));
\r
3161 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3162 displaycenter + core::vector2d<s32>(0,10),
\r
3163 video::SColor(255,255,255,255));
\r
3168 if(g_settings.getBool("frametime_graph") == true)
\r
3171 for(core::list<float>::Iterator
\r
3172 i = frametime_log.begin();
\r
3173 i != frametime_log.end();
\r
3176 driver->draw2DLine(v2s32(x,50),
\r
3177 v2s32(x,50+(*i)*1000),
\r
3178 video::SColor(255,255,255,255));
\r
3186 //TimeTaker //timer11("//timer11");
\r
3192 guienv->drawAll();
\r
3198 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3199 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3204 TimeTaker timer("endScene");
\r
3205 driver->endScene();
\r
3206 endscenetime = timer.stop(true);
\r
3209 drawtime = drawtimer.stop(true);
\r
3215 static s16 lastFPS = 0;
\r
3216 //u16 fps = driver->getFPS();
\r
3217 u16 fps = (1.0/dtime_avg1);
\r
3219 if (lastFPS != fps)
\r
3221 core::stringw str = L"Minetest [";
\r
3222 str += driver->getName();
\r
3226 device->setWindowCaption(str.c_str());
\r
3232 device->yield();*/
\r
3235 //delete quick_inventory;
\r
3238 Disable texture fetches and other stuff that is queued
\r
3239 to be processed by the main loop.
\r
3241 This has to be done before client goes out of scope.
\r
3243 g_irrlicht->Shutdown(true);
\r
3245 } // client and server are deleted at this point
\r
3248 catch(con::PeerNotFoundException &e)
\r
3250 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3251 error_message = L"Connection timed out.";
\r
3254 } // Menu-game loop
\r
3259 In the end, delete the Irrlicht device.
\r
3264 Update configuration file
\r
3266 /*if(configpath != "")
\r
3268 g_settings.updateConfigFile(configpath.c_str());
\r
3271 END_DEBUG_EXCEPTION_HANDLER
\r
3273 debugstreams_deinit();
\r