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
163 NOTE(FIXED): A lock condition is possible:
\r
164 1) MapBlock::updateMesh() is called from client asynchronously:
\r
165 - AsyncProcessData() -> Map::updateMeshes()
\r
166 2) Asynchronous locks m_temp_mods_mutex
\r
167 3) MapBlock::updateMesh() is called from client synchronously:
\r
168 - Client::step() -> Environment::step()
\r
169 4) Synchronous starts waiting for m_temp_mods_mutex
\r
170 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
175 TODO: Make the video backend selectable
\r
180 TODO: Untie client network operations from framerate
\r
181 - Needs some input queues or something
\r
183 TODO: Make morning and evening transition more smooth and maybe shorter
\r
185 TODO: Don't update all meshes always on single node changes, but
\r
186 check which ones should be updated
\r
187 - implement Map::updateNodeMeshes()
\r
192 TODO: When player dies, throw items on map
\r
194 TODO: Make an option to the server to disable building and digging near
\r
195 the starting position
\r
197 TODO: Save players with inventories to disk
\r
198 TODO: Players to be saved as text in map/players/<name>
\r
200 TODO: Copy the text of the last picked sign to inventory in creative
\r
203 TODO: Check what goes wrong with caching map to disk (Kray)
\r
206 TODO: When server sees that client is removing an inexistent block to
\r
207 an existent position, resend the MapBlock.
\r
209 FIXME: Server went into some infinite PeerNotFoundException loop
\r
214 TODO: Better handling of objects and mobs
\r
216 - There has to be some way to do it with less messy code
\r
217 - Make separate classes for client and server
\r
218 - Client should not discriminate between blocks, server should
\r
219 - Make other players utilize the same framework
\r
220 - This is also needed for objects that don't get sent to client
\r
221 but are used for triggers etc
\r
223 TODO: There has to be some better way to handle static objects than to
\r
224 send them all the time. This affects signs and item objects.
\r
225 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
226 need an additional metadata field for the texts
\r
227 - This is also needed for item container chests
\r
229 Block object server side:
\r
230 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
231 - For all blocks in the buffer, objects are stepped(). This
\r
232 means they are active.
\r
233 - TODO: A global active buffer is needed for the server
\r
234 - TODO: A timestamp to blocks
\r
235 - TODO: All blocks going in and out of the buffer are recorded.
\r
236 - TODO: For outgoing blocks, timestamp is written.
\r
237 - TODO: For incoming blocks, time difference is calculated and
\r
238 objects are stepped according to it.
\r
243 TODO: Mineral and ground material properties
\r
244 - This way mineral ground toughness can be calculated with just
\r
245 some formula, as well as tool strengths
\r
247 TODO: Flowing water to actually contain flow direction information
\r
249 TODO: Remove duplicate lighting implementation from Map (leave
\r
250 VoxelManipulator, which is faster)
\r
252 FEATURE: Create a system that allows a huge amount of different "map
\r
253 generator modules/filters"
\r
255 FEATURE: Erosion simulation at map generation time
\r
256 - Simulate water flows, which would carve out dirt fast and
\r
257 then turn stone into gravel and sand and relocate it.
\r
258 - How about relocating minerals, too? Coal and gold in
\r
259 downstream sand and gravel would be kind of cool
\r
260 - This would need a better way of handling minerals, mainly
\r
261 to have mineral content as a separate field. the first
\r
262 parameter field is free for this.
\r
263 - Simulate rock falling from cliffs when water has removed
\r
264 enough solid rock from the bottom
\r
266 Doing now (most important at the top):
\r
267 --------------------------------------
\r
272 * Make server find the spawning place from the real map data, not from
\r
274 - But the changing borders of chunk have to be avoided, because
\r
275 there is time to generate only one chunk.
\r
276 * Make the generator to run in background and not blocking block
\r
277 placement and transfer
\r
278 * only_from_disk might not work anymore - check and fix it.
\r
279 * Check the fixmes in the list above
\r
280 * When sending blocks to the client, the server takes way too much
\r
281 CPU time (20-30% for single player), find out what it is doing.
\r
282 - Make a simple profiler
\r
284 === Making it more portable
\r
285 * Some MSVC: std::sto* are defined without a namespace and collide
\r
286 with the ones in utility.h
\r
289 * Make an "environment metafile" to store at least time of day
\r
290 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
291 - Or maybe move content_features to material.{h,cpp}?
\r
292 * Add some kind of erosion and other stuff that now is possible
\r
293 * Make client to fetch stuff asynchronously
\r
294 - Needs method SyncProcessData
\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 * Water doesn't start flowing after map generation like it should
\r
299 - Are there still problems?
\r
300 * Better water generation (spread it to underwater caverns but don't
\r
301 fill dungeons that don't touch big water masses)
\r
302 * When generating a chunk and the neighboring chunk doesn't have mud
\r
303 and stuff yet and the ground is fairly flat, the mud will flow to
\r
304 the other chunk making nasty straight walls when the other chunk
\r
305 is generated. Fix it.
\r
306 * Make a small history check to transformLiquids to detect and log
\r
307 continuous oscillations, in such detail that they can be fixed.
\r
308 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
310 ======================================================================
\r
315 Setting this to 1 enables a special camera mode that forces
\r
316 the renderers to think that the camera statically points from
\r
317 the starting place to a static direction.
\r
319 This allows one to move around with the player and see what
\r
320 is actually drawn behind solid things and behind the player.
\r
322 #define FIELD_OF_VIEW_TEST 0
\r
326 #pragma message ("Disabling unit tests")
\r
328 #warning "Disabling unit tests"
\r
330 // Disable unit tests
\r
331 #define ENABLE_TESTS 0
\r
333 // Enable unit tests
\r
334 #define ENABLE_TESTS 1
\r
338 #pragma comment(lib, "Irrlicht.lib")
\r
339 //#pragma comment(lib, "jthread.lib")
\r
340 #pragma comment(lib, "zlibwapi.lib")
\r
341 #pragma comment(lib, "Shell32.lib")
\r
342 // This would get rid of the console window
\r
343 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
346 #include <iostream>
\r
348 #include <jmutexautolock.h>
\r
349 #include <locale.h>
\r
350 #include "common_irrlicht.h"
\r
353 #include "player.h"
\r
356 #include "environment.h"
\r
357 #include "server.h"
\r
358 #include "client.h"
\r
359 #include "serialization.h"
\r
360 #include "constants.h"
\r
361 #include "strfnd.h"
\r
362 #include "porting.h"
\r
363 #include "irrlichtwrapper.h"
\r
364 #include "gettime.h"
\r
365 #include "porting.h"
\r
366 #include "guiPauseMenu.h"
\r
367 #include "guiInventoryMenu.h"
\r
368 #include "guiTextInputMenu.h"
\r
369 #include "materials.h"
\r
370 #include "guiMessageMenu.h"
\r
371 #include "filesys.h"
\r
372 #include "config.h"
\r
373 #include "guiMainMenu.h"
\r
374 #include "mineral.h"
\r
378 // TODO: Remove this
\r
379 IrrlichtWrapper *g_irrlicht = NULL;
\r
381 // This makes textures
\r
382 ITextureSource *g_texturesource = NULL;
\r
384 MapDrawControl draw_control;
\r
388 These are loaded from the config file.
\r
391 Settings g_settings;
\r
393 extern void set_default_settings();
\r
399 IrrlichtDevice *g_device = NULL;
\r
400 Client *g_client = NULL;
\r
402 /*const s16 quickinv_size = 40;
\r
403 const s16 quickinv_padding = 8;
\r
404 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
405 const s16 quickinv_outer_padding = 4;
\r
406 const s16 quickinv_itemcount = 8;*/
\r
408 const s32 hotbar_itemcount = 8;
\r
409 const s32 hotbar_imagesize = 36;
\r
415 gui::IGUIEnvironment* guienv = NULL;
\r
416 gui::IGUIStaticText *guiroot = NULL;
\r
418 class MainMenuManager : public IMenuManager
\r
421 virtual void createdMenu(GUIModalMenu *menu)
\r
423 for(core::list<GUIModalMenu*>::Iterator
\r
424 i = m_stack.begin();
\r
425 i != m_stack.end(); i++)
\r
427 assert(*i != menu);
\r
430 if(m_stack.size() != 0)
\r
431 (*m_stack.getLast())->setVisible(false);
\r
432 m_stack.push_back(menu);
\r
435 virtual void deletingMenu(GUIModalMenu *menu)
\r
437 // Remove all entries if there are duplicates
\r
438 bool removed_entry;
\r
440 removed_entry = false;
\r
441 for(core::list<GUIModalMenu*>::Iterator
\r
442 i = m_stack.begin();
\r
443 i != m_stack.end(); i++)
\r
448 removed_entry = true;
\r
452 }while(removed_entry);
\r
454 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
455 assert(*i == menu);
\r
456 m_stack.erase(i);*/
\r
458 if(m_stack.size() != 0)
\r
459 (*m_stack.getLast())->setVisible(true);
\r
464 return m_stack.size();
\r
467 core::list<GUIModalMenu*> m_stack;
\r
470 MainMenuManager g_menumgr;
\r
472 bool noMenuActive()
\r
474 return (g_menumgr.menuCount() == 0);
\r
477 bool g_disconnect_requested = false;
\r
479 class MainGameCallback : public IGameCallback
\r
482 virtual void exitToOS()
\r
484 g_device->closeDevice();
\r
487 virtual void disconnect()
\r
489 g_disconnect_requested = true;
\r
493 MainGameCallback g_gamecallback;
\r
495 // Inventory actions from the menu are buffered here before sending
\r
496 Queue<InventoryAction*> inventory_action_queue;
\r
497 // This is a copy of the inventory that the client's environment has
\r
498 Inventory local_inventory;
\r
500 u16 g_selected_item = 0;
\r
507 std::ostream *dout_con_ptr = &dummyout;
\r
508 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
509 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
510 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
511 //std::ostream *dout_con_ptr = &dstream;
\r
512 //std::ostream *derr_con_ptr = &dstream;
\r
515 std::ostream *dout_server_ptr = &dstream;
\r
516 std::ostream *derr_server_ptr = &dstream;
\r
519 std::ostream *dout_client_ptr = &dstream;
\r
520 std::ostream *derr_client_ptr = &dstream;
\r
523 gettime.h implementation
\r
529 Use irrlicht because it is more precise than porting.h's
\r
532 if(g_irrlicht == NULL)
\r
534 return g_irrlicht->getTime();
\r
541 struct TextDestSign : public TextDest
\r
543 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
545 m_blockpos = blockpos;
\r
549 void gotText(std::wstring text)
\r
551 std::string ntext = wide_to_narrow(text);
\r
552 dstream<<"Changing text of a sign object: "
\r
553 <<ntext<<std::endl;
\r
554 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
562 struct TextDestChat : public TextDest
\r
564 TextDestChat(Client *client)
\r
568 void gotText(std::wstring text)
\r
570 // Discard empty line
\r
574 // Parse command (server command starts with "/#")
\r
575 if(text[0] == L'/' && text[1] != L'#')
\r
577 std::wstring reply = L"Local: ";
\r
579 reply += L"Local commands not yet supported. "
\r
580 L"Server prefix is \"/#\".";
\r
582 m_client->addChatMessage(reply);
\r
587 m_client->sendChatMessage(text);
\r
589 m_client->addChatMessage(text);
\r
595 class MyEventReceiver : public IEventReceiver
\r
598 // This is the one method that we have to implement
\r
599 virtual bool OnEvent(const SEvent& event)
\r
602 React to nothing here if a menu is active
\r
604 if(noMenuActive() == false)
\r
610 // Remember whether each key is down or up
\r
611 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
613 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
615 if(event.KeyInput.PressedDown)
\r
617 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
623 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
625 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
627 dstream<<DTIME<<"MyEventReceiver: "
\r
628 <<"Launching pause menu"<<std::endl;
\r
629 // It will delete itself by itself
\r
630 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
631 &g_menumgr))->drop();
\r
634 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
636 dstream<<DTIME<<"MyEventReceiver: "
\r
637 <<"Launching inventory"<<std::endl;
\r
638 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
639 &local_inventory, &inventory_action_queue,
\r
640 &g_menumgr))->drop();
\r
643 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
645 TextDest *dest = new TextDestChat(g_client);
\r
647 (new GUITextInputMenu(guienv, guiroot, -1,
\r
654 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
655 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
657 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
658 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
660 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
661 g_selected_item = s1-1;
\r
662 dstream<<DTIME<<"Selected item: "
\r
663 <<g_selected_item<<std::endl;
\r
666 // Viewing range selection
\r
667 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
669 if(draw_control.range_all)
\r
671 draw_control.range_all = false;
\r
672 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
676 draw_control.range_all = true;
\r
677 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
681 // Print debug stacks
\r
682 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
684 dstream<<"-----------------------------------------"
\r
686 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
687 dstream<<"-----------------------------------------"
\r
689 debug_stacks_print();
\r
694 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
696 if(noMenuActive() == false)
\r
698 left_active = false;
\r
699 middle_active = false;
\r
700 right_active = false;
\r
704 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
705 left_active = event.MouseInput.isLeftPressed();
\r
706 middle_active = event.MouseInput.isMiddlePressed();
\r
707 right_active = event.MouseInput.isRightPressed();
\r
709 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
711 leftclicked = true;
\r
713 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
715 rightclicked = true;
\r
717 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
719 leftreleased = true;
\r
721 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
723 rightreleased = true;
\r
725 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
727 /*dstream<<"event.MouseInput.Wheel="
\r
728 <<event.MouseInput.Wheel<<std::endl;*/
\r
730 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
731 hotbar_itemcount-1);
\r
732 if(event.MouseInput.Wheel < 0)
\r
734 if(g_selected_item < max_item)
\r
737 g_selected_item = 0;
\r
739 else if(event.MouseInput.Wheel > 0)
\r
741 if(g_selected_item > 0)
\r
744 g_selected_item = max_item;
\r
753 // This is used to check whether a key is being held down
\r
754 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
756 return keyIsDown[keyCode];
\r
761 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
762 keyIsDown[i] = false;
\r
764 leftclicked = false;
\r
765 rightclicked = false;
\r
766 leftreleased = false;
\r
767 rightreleased = false;
\r
769 left_active = false;
\r
770 middle_active = false;
\r
771 right_active = false;
\r
782 bool rightreleased;
\r
785 bool middle_active;
\r
789 // We use this array to store the current state of each key
\r
790 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
793 IrrlichtDevice *m_device;
\r
802 virtual ~InputHandler()
\r
806 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
808 virtual v2s32 getMousePos() = 0;
\r
809 virtual void setMousePos(s32 x, s32 y) = 0;
\r
811 virtual bool getLeftState() = 0;
\r
812 virtual bool getRightState() = 0;
\r
814 virtual bool getLeftClicked() = 0;
\r
815 virtual bool getRightClicked() = 0;
\r
816 virtual void resetLeftClicked() = 0;
\r
817 virtual void resetRightClicked() = 0;
\r
819 virtual bool getLeftReleased() = 0;
\r
820 virtual bool getRightReleased() = 0;
\r
821 virtual void resetLeftReleased() = 0;
\r
822 virtual void resetRightReleased() = 0;
\r
824 virtual void step(float dtime) {};
\r
826 virtual void clear() {};
\r
829 InputHandler *g_input = NULL;
\r
831 class RealInputHandler : public InputHandler
\r
834 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
836 m_receiver(receiver)
\r
839 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
841 return m_receiver->IsKeyDown(keyCode);
\r
843 virtual v2s32 getMousePos()
\r
845 return m_device->getCursorControl()->getPosition();
\r
847 virtual void setMousePos(s32 x, s32 y)
\r
849 m_device->getCursorControl()->setPosition(x, y);
\r
852 virtual bool getLeftState()
\r
854 return m_receiver->left_active;
\r
856 virtual bool getRightState()
\r
858 return m_receiver->right_active;
\r
861 virtual bool getLeftClicked()
\r
863 return m_receiver->leftclicked;
\r
865 virtual bool getRightClicked()
\r
867 return m_receiver->rightclicked;
\r
869 virtual void resetLeftClicked()
\r
871 m_receiver->leftclicked = false;
\r
873 virtual void resetRightClicked()
\r
875 m_receiver->rightclicked = false;
\r
878 virtual bool getLeftReleased()
\r
880 return m_receiver->leftreleased;
\r
882 virtual bool getRightReleased()
\r
884 return m_receiver->rightreleased;
\r
886 virtual void resetLeftReleased()
\r
888 m_receiver->leftreleased = false;
\r
890 virtual void resetRightReleased()
\r
892 m_receiver->rightreleased = false;
\r
897 resetRightClicked();
\r
898 resetLeftClicked();
\r
901 IrrlichtDevice *m_device;
\r
902 MyEventReceiver *m_receiver;
\r
905 class RandomInputHandler : public InputHandler
\r
908 RandomInputHandler()
\r
910 leftclicked = false;
\r
911 rightclicked = false;
\r
912 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
913 keydown[i] = false;
\r
915 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
917 return keydown[keyCode];
\r
919 virtual v2s32 getMousePos()
\r
923 virtual void setMousePos(s32 x, s32 y)
\r
925 mousepos = v2s32(x,y);
\r
928 virtual bool getLeftState()
\r
932 virtual bool getRightState()
\r
937 virtual bool getLeftClicked()
\r
939 return leftclicked;
\r
941 virtual bool getRightClicked()
\r
943 return rightclicked;
\r
945 virtual void resetLeftClicked()
\r
947 leftclicked = false;
\r
949 virtual void resetRightClicked()
\r
951 rightclicked = false;
\r
954 virtual bool getLeftReleased()
\r
958 virtual bool getRightReleased()
\r
962 virtual void resetLeftReleased()
\r
965 virtual void resetRightReleased()
\r
969 virtual void step(float dtime)
\r
972 static float counter1 = 0;
\r
976 counter1 = 0.1*Rand(1,10);
\r
977 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
978 g_selected_material++;
\r
980 g_selected_material = 0;*/
\r
981 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
984 g_selected_item = 0;
\r
988 static float counter1 = 0;
\r
992 counter1 = 0.1*Rand(1, 40);
\r
993 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
997 static float counter1 = 0;
\r
1001 counter1 = 0.1*Rand(1, 40);
\r
1002 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1006 static float counter1 = 0;
\r
1007 counter1 -= dtime;
\r
1008 if(counter1 < 0.0)
\r
1010 counter1 = 0.1*Rand(1, 40);
\r
1011 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1015 static float counter1 = 0;
\r
1016 counter1 -= dtime;
\r
1017 if(counter1 < 0.0)
\r
1019 counter1 = 0.1*Rand(1, 40);
\r
1020 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1024 static float counter1 = 0;
\r
1025 counter1 -= dtime;
\r
1026 if(counter1 < 0.0)
\r
1028 counter1 = 0.1*Rand(1, 20);
\r
1029 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1033 static float counter1 = 0;
\r
1034 counter1 -= dtime;
\r
1035 if(counter1 < 0.0)
\r
1037 counter1 = 0.1*Rand(1, 30);
\r
1038 leftclicked = true;
\r
1042 static float counter1 = 0;
\r
1043 counter1 -= dtime;
\r
1044 if(counter1 < 0.0)
\r
1046 counter1 = 0.1*Rand(1, 20);
\r
1047 rightclicked = true;
\r
1050 mousepos += mousespeed;
\r
1053 s32 Rand(s32 min, s32 max)
\r
1055 return (myrand()%(max-min+1))+min;
\r
1058 bool keydown[KEY_KEY_CODES_COUNT];
\r
1062 bool rightclicked;
\r
1065 void updateViewingRange(f32 frametime_in, Client *client)
\r
1067 if(draw_control.range_all == true)
\r
1070 static f32 added_frametime = 0;
\r
1071 static s16 added_frames = 0;
\r
1073 added_frametime += frametime_in;
\r
1074 added_frames += 1;
\r
1076 // Actually this counter kind of sucks because frametime is busytime
\r
1077 static f32 counter = 0;
\r
1078 counter -= frametime_in;
\r
1084 /*dstream<<__FUNCTION_NAME
\r
1085 <<": Collected "<<added_frames<<" frames, total of "
\r
1086 <<added_frametime<<"s."<<std::endl;*/
\r
1088 /*dstream<<"draw_control.blocks_drawn="
\r
1089 <<draw_control.blocks_drawn
\r
1090 <<", draw_control.blocks_would_have_drawn="
\r
1091 <<draw_control.blocks_would_have_drawn
\r
1094 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1095 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1097 draw_control.wanted_min_range = range_min;
\r
1098 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1100 float block_draw_ratio = 1.0;
\r
1101 if(draw_control.blocks_would_have_drawn != 0)
\r
1103 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1104 / (float)draw_control.blocks_would_have_drawn;
\r
1107 // Calculate the average frametime in the case that all wanted
\r
1108 // blocks had been drawn
\r
1109 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1111 added_frametime = 0.0;
\r
1114 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1115 float wanted_frametime = 1.0 / wanted_fps;
\r
1117 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1118 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1120 // If needed frametime change is very small, just return
\r
1121 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1123 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1127 float range = draw_control.wanted_range;
\r
1128 float new_range = range;
\r
1130 static s16 range_old = 0;
\r
1131 static f32 frametime_old = 0;
\r
1133 float d_range = range - range_old;
\r
1134 f32 d_frametime = frametime - frametime_old;
\r
1135 // A sane default of 30ms per 50 nodes of range
\r
1136 static f32 time_per_range = 30. / 50;
\r
1139 time_per_range = d_frametime / d_range;
\r
1142 // The minimum allowed calculated frametime-range derivative:
\r
1143 // Practically this sets the maximum speed of changing the range.
\r
1144 // The lower this value, the higher the maximum changing speed.
\r
1145 // A low value here results in wobbly range (0.001)
\r
1146 // A high value here results in slow changing range (0.0025)
\r
1147 // SUGG: This could be dynamically adjusted so that when
\r
1148 // the camera is turning, this is lower
\r
1149 //float min_time_per_range = 0.0015;
\r
1150 float min_time_per_range = 0.0010;
\r
1151 //float min_time_per_range = 0.05 / range;
\r
1152 if(time_per_range < min_time_per_range)
\r
1154 time_per_range = min_time_per_range;
\r
1155 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1159 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1162 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1163 // Dampen the change a bit to kill oscillations
\r
1164 //wanted_range_change *= 0.9;
\r
1165 //wanted_range_change *= 0.75;
\r
1166 wanted_range_change *= 0.5;
\r
1167 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1169 // If needed range change is very small, just return
\r
1170 if(fabs(wanted_range_change) < 0.001)
\r
1172 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1176 new_range += wanted_range_change;
\r
1177 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1179 //float new_range_unclamped = new_range;
\r
1180 if(new_range < range_min)
\r
1181 new_range = range_min;
\r
1182 if(new_range > range_max)
\r
1183 new_range = range_max;
\r
1185 /*if(new_range != new_range_unclamped)
\r
1186 dstream<<", clamped to "<<new_range<<std::endl;
\r
1188 dstream<<std::endl;*/
\r
1190 draw_control.wanted_range = new_range;
\r
1192 range_old = new_range;
\r
1193 frametime_old = frametime;
\r
1198 class GUIQuickInventory
\r
1201 GUIQuickInventory(
\r
1202 gui::IGUIEnvironment* env,
\r
1203 gui::IGUIElement* parent,
\r
1206 Inventory *inventory):
\r
1207 m_itemcount(itemcount),
\r
1208 m_inventory(inventory)
\r
1210 core::rect<s32> imgsize(0,0,quickinv_size,quickinv_size);
\r
1211 core::rect<s32> textsize(0,0,quickinv_size,quickinv_size);
\r
1212 bgtext = env->addStaticText(L"", core::rect<s32>(0,0,1,1), false, false);
\r
1213 bgtext->setBackgroundColor(
\r
1214 video::SColor(128,0,0,0));
\r
1215 for(s32 i=0; i<m_itemcount; i++)
\r
1217 m_images.push_back(env->addImage(
\r
1220 m_images[i]->setScaleImage(true);
\r
1221 m_texts.push_back(env->addStaticText(
\r
1226 /*m_texts[i]->setBackgroundColor(
\r
1227 video::SColor(128,0,0,0));*/
\r
1228 m_texts[i]->setTextAlignment(
\r
1229 gui::EGUIA_LOWERRIGHT,
\r
1230 gui::EGUIA_LOWERRIGHT);
\r
1233 updatePosition(pos);
\r
1236 ~GUIQuickInventory()
\r
1238 for(u32 i=0; i<m_texts.size(); i++)
\r
1240 m_texts[i]->remove();
\r
1242 for(u32 i=0; i<m_images.size(); i++)
\r
1244 m_images[i]->remove();
\r
1249 void updatePosition(v2s32 pos)
\r
1251 v2s32 spacing(quickinv_spacing, 0);
\r
1252 for(s32 i=0; i<m_itemcount; i++)
\r
1254 m_images[i]->setRelativePosition(pos + spacing*i);
\r
1255 m_texts[i]->setRelativePosition(pos + spacing*i);
\r
1257 core::rect<s32> bgrect(-quickinv_outer_padding,-quickinv_outer_padding,
\r
1258 (quickinv_itemcount-1)*quickinv_spacing+quickinv_size+quickinv_outer_padding,
\r
1259 quickinv_size+quickinv_outer_padding);
\r
1260 bgtext->setRelativePosition(bgrect+pos);
\r
1263 void setSelection(s32 i)
\r
1272 //start = m_selection - m_itemcount / 2;
\r
1274 InventoryList *mainlist = m_inventory->getList("main");
\r
1276 for(s32 i=0; i<m_itemcount; i++)
\r
1278 s32 j = i + start;
\r
1280 if(j > (s32)mainlist->getSize() - 1)
\r
1281 j -= mainlist->getSize();
\r
1283 j += mainlist->getSize();
\r
1285 InventoryItem *item = mainlist->getItem(j);
\r
1289 m_images[i]->setImage(NULL);
\r
1291 if(m_selection == j)
\r
1292 m_texts[i]->setText(L"->");
\r
1294 m_texts[i]->setText(L"");
\r
1296 // The next ifs will segfault with a NULL pointer
\r
1301 m_images[i]->setImage(item->getImage());
\r
1303 std::ostringstream os;
\r
1304 if(m_selection == j)
\r
1306 os<<item->getText();
\r
1307 m_texts[i]->setText(narrow_to_wide(os.str()).c_str());
\r
1310 if(m_selection == j)
\r
1311 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1313 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1314 m_texts[i]->setText(t);*/
\r
1320 gui::IGUIStaticText *bgtext;
\r
1321 core::array<gui::IGUIStaticText*> m_texts;
\r
1322 core::array<gui::IGUIImage*> m_images;
\r
1323 Inventory *m_inventory;
\r
1328 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1329 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1330 Inventory *inventory)
\r
1332 InventoryList *mainlist = inventory->getList("main");
\r
1333 if(mainlist == NULL)
\r
1335 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1339 s32 padding = imgsize/12;
\r
1340 //s32 height = imgsize + padding*2;
\r
1341 s32 width = itemcount*(imgsize+padding*2);
\r
1343 // Position of upper left corner of bar
\r
1344 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1346 // Draw background color
\r
1347 /*core::rect<s32> barrect(0,0,width,height);
\r
1349 video::SColor bgcolor(255,128,128,128);
\r
1350 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1352 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1354 for(s32 i=0; i<itemcount; i++)
\r
1356 InventoryItem *item = mainlist->getItem(i);
\r
1358 core::rect<s32> rect = imgrect + pos
\r
1359 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1361 if(g_selected_item == i)
\r
1363 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1364 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1365 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1370 video::SColor bgcolor2(128,0,0,0);
\r
1371 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1376 drawInventoryItem(driver, font, item, rect, NULL);
\r
1388 ChatLine(const std::wstring &a_text):
\r
1394 std::wstring text;
\r
1397 // These are defined global so that they're not optimized too much.
\r
1398 // Can't change them to volatile.
\r
1403 std::string tempstring;
\r
1404 std::string tempstring2;
\r
1409 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1410 TimeTaker timer("Testing std::string speed");
\r
1411 const u32 jj = 10000;
\r
1412 for(u32 j=0; j<jj; j++)
\r
1416 const u32 ii = 10;
\r
1417 for(u32 i=0; i<ii; i++){
\r
1418 tempstring2 += "asd";
\r
1420 for(u32 i=0; i<ii+1; i++){
\r
1421 tempstring += "asd";
\r
1422 if(tempstring == tempstring2)
\r
1428 dstream<<"All of the following tests should take around 100ms each."
\r
1432 TimeTaker timer("Testing floating-point conversion speed");
\r
1434 for(u32 i=0; i<4000000; i++){
\r
1441 TimeTaker timer("Testing floating-point vector speed");
\r
1443 tempv3f1 = v3f(1,2,3);
\r
1444 tempv3f2 = v3f(4,5,6);
\r
1445 for(u32 i=0; i<10000000; i++){
\r
1446 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1447 tempv3f2 += v3f(7,8,9);
\r
1452 TimeTaker timer("Testing core::map speed");
\r
1454 core::map<v2s16, f32> map1;
\r
1457 for(s16 y=0; y<ii; y++){
\r
1458 for(s16 x=0; x<ii; x++){
\r
1459 map1.insert(v2s16(x,y), tempf);
\r
1463 for(s16 y=ii-1; y>=0; y--){
\r
1464 for(s16 x=0; x<ii; x++){
\r
1465 tempf = map1[v2s16(x,y)];
\r
1471 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1472 TimeTaker timer("Testing mutex speed");
\r
1485 // Do at least 10ms
\r
1486 while(timer.getTime() < 10);
\r
1488 u32 dtime = timer.stop();
\r
1489 u32 per_ms = n / dtime;
\r
1490 std::cout<<"Done. "<<dtime<<"ms, "
\r
1491 <<per_ms<<"/ms"<<std::endl;
\r
1495 int main(int argc, char *argv[])
\r
1498 Parse command line
\r
1501 // List all allowed options
\r
1502 core::map<std::string, ValueSpec> allowed_options;
\r
1503 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1504 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1505 "Run server directly"));
\r
1506 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1507 "Load configuration from specified file"));
\r
1508 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1509 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1510 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1511 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1512 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1513 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1515 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1517 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1519 Settings cmd_args;
\r
1521 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1523 if(ret == false || cmd_args.getFlag("help"))
\r
1525 dstream<<"Allowed options:"<<std::endl;
\r
1526 for(core::map<std::string, ValueSpec>::Iterator
\r
1527 i = allowed_options.getIterator();
\r
1528 i.atEnd() == false; i++)
\r
1530 dstream<<" --"<<i.getNode()->getKey();
\r
1531 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1536 dstream<<" <value>";
\r
1538 dstream<<std::endl;
\r
1540 if(i.getNode()->getValue().help != NULL)
\r
1542 dstream<<" "<<i.getNode()->getValue().help
\r
1547 return cmd_args.getFlag("help") ? 0 : 1;
\r
1551 Low-level initialization
\r
1554 bool disable_stderr = false;
\r
1556 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1557 disable_stderr = true;
\r
1560 // Initialize debug streams
\r
1561 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1562 // Initialize debug stacks
\r
1563 debug_stacks_init();
\r
1565 DSTACK(__FUNCTION_NAME);
\r
1567 porting::signal_handler_init();
\r
1568 bool &kill = *porting::signal_handler_killstatus();
\r
1570 porting::initializePaths();
\r
1571 // Create user data directory
\r
1572 fs::CreateDir(porting::path_userdata);
\r
1574 // C-style stuff initialization
\r
1575 initializeMaterialProperties();
\r
1578 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1580 // Print startup message
\r
1581 dstream<<DTIME<<"minetest-c55"
\r
1582 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1583 <<", "<<BUILD_INFO
\r
1587 Basic initialization
\r
1590 // Initialize default settings
\r
1591 set_default_settings();
\r
1593 // Set locale. This is for forcing '.' as the decimal point.
\r
1594 std::locale::global(std::locale("C"));
\r
1595 // This enables printing all characters in bitmap font
\r
1596 setlocale(LC_CTYPE, "en_US");
\r
1598 // Initialize sockets
\r
1600 atexit(sockets_cleanup);
\r
1610 // Path of configuration file in use
\r
1611 std::string configpath = "";
\r
1613 if(cmd_args.exists("config"))
\r
1615 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1618 dstream<<"Could not read configuration from \""
\r
1619 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1622 configpath = cmd_args.get("config");
\r
1626 core::array<std::string> filenames;
\r
1627 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1628 #ifdef RUN_IN_PLACE
\r
1629 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1632 for(u32 i=0; i<filenames.size(); i++)
\r
1634 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1637 configpath = filenames[i];
\r
1642 // If no path found, use the first one (menu creates the file)
\r
1643 if(configpath == "")
\r
1644 configpath = filenames[0];
\r
1647 // Initialize random seed
\r
1652 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1654 These are needed for unit tests at least.
\r
1657 // Initial call with g_texturesource not set.
\r
1664 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1665 || cmd_args.getFlag("enable-unittests") == true)
\r
1670 /*for(s16 y=-100; y<100; y++)
\r
1671 for(s16 x=-100; x<100; x++)
\r
1673 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1683 if(cmd_args.exists("port"))
\r
1684 port = cmd_args.getU16("port");
\r
1685 else if(cmd_args.exists("port"))
\r
1686 port = g_settings.getU16("port");
\r
1689 std::string map_dir = porting::path_userdata+"/map";
\r
1690 if(cmd_args.exists("map-dir"))
\r
1691 map_dir = cmd_args.get("map-dir");
\r
1692 else if(g_settings.exists("map-dir"))
\r
1693 map_dir = g_settings.get("map-dir");
\r
1695 // Run dedicated server if asked to
\r
1696 if(cmd_args.getFlag("server"))
\r
1698 DSTACK("Dedicated server branch");
\r
1701 Server server(map_dir.c_str());
\r
1702 server.start(port);
\r
1705 dedicated_server_loop(server, kill);
\r
1714 // Address to connect to
\r
1715 std::string address = "";
\r
1717 if(cmd_args.exists("address"))
\r
1719 address = cmd_args.get("address");
\r
1723 address = g_settings.get("address");
\r
1726 std::string playername = g_settings.get("name");
\r
1729 Resolution selection
\r
1732 bool fullscreen = false;
\r
1733 u16 screenW = g_settings.getU16("screenW");
\r
1734 u16 screenH = g_settings.getU16("screenH");
\r
1738 MyEventReceiver receiver;
\r
1740 video::E_DRIVER_TYPE driverType;
\r
1743 //driverType = video::EDT_DIRECT3D9;
\r
1744 driverType = video::EDT_OPENGL;
\r
1746 driverType = video::EDT_OPENGL;
\r
1747 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1750 // create device and exit if creation failed
\r
1752 IrrlichtDevice *device;
\r
1753 device = createDevice(driverType,
\r
1754 core::dimension2d<u32>(screenW, screenH),
\r
1755 16, fullscreen, false, false, &receiver);
\r
1758 return 1; // could not create selected driver.
\r
1760 g_device = device;
\r
1761 g_irrlicht = new IrrlichtWrapper(device);
\r
1762 TextureSource *texturesource = new TextureSource(device);
\r
1763 g_texturesource = texturesource;
\r
1766 Speed tests (done after irrlicht is loaded to get timer)
\r
1768 if(cmd_args.getFlag("speedtests"))
\r
1770 dstream<<"Running speed tests"<<std::endl;
\r
1775 device->setResizable(true);
\r
1777 bool random_input = g_settings.getBool("random_input")
\r
1778 || cmd_args.getFlag("random-input");
\r
1780 g_input = new RandomInputHandler();
\r
1782 g_input = new RealInputHandler(device, &receiver);
\r
1785 Continue initialization
\r
1788 video::IVideoDriver* driver = device->getVideoDriver();
\r
1791 This changes the minimum allowed number of vertices in a VBO.
\r
1794 //driver->setMinHardwareBufferVertexCount(50);
\r
1796 scene::ISceneManager* smgr = device->getSceneManager();
\r
1798 guienv = device->getGUIEnvironment();
\r
1799 gui::IGUISkin* skin = guienv->getSkin();
\r
1800 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1802 skin->setFont(font);
\r
1804 dstream<<"WARNING: Font file was not found."
\r
1805 " Using default font."<<std::endl;
\r
1806 // If font was not found, this will get us one
\r
1807 font = skin->getFont();
\r
1810 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1811 dstream<<"text_height="<<text_height<<std::endl;
\r
1813 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1814 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1815 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1816 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1817 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1818 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1821 Preload some textures and stuff
\r
1824 init_content_inventory_texture_paths();
\r
1825 init_mapnode(); // Second call with g_texturesource set
\r
1833 We need some kind of a root node to be able to add
\r
1834 custom gui elements directly on the screen.
\r
1835 Otherwise they won't be automatically drawn.
\r
1837 guiroot = guienv->addStaticText(L"",
\r
1838 core::rect<s32>(0, 0, 10000, 10000));
\r
1840 // First line of debug text
\r
1841 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1843 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1845 // Second line of debug text
\r
1846 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1848 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1851 // At the middle of the screen
\r
1852 // Object infos are shown in this
\r
1853 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1855 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1859 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1861 core::rect<s32>(0,0,0,0),
\r
1862 false, false); // Disable word wrap as of now
\r
1864 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1865 core::list<ChatLine> chat_lines;
\r
1868 If an error occurs, this is set to something and the
\r
1869 menu-game loop is restarted. It is then displayed before
\r
1872 std::wstring error_message = L"";
\r
1877 while(g_device->run())
\r
1880 // This is used for catching disconnects
\r
1885 Out-of-game menu loop.
\r
1887 Loop quits when menu returns proper parameters.
\r
1891 // Cursor can be non-visible when coming from the game
\r
1892 device->getCursorControl()->setVisible(true);
\r
1893 // Some stuff are left to scene manager when coming from the game
\r
1894 // (map at least?)
\r
1896 // Reset or hide the debug gui texts
\r
1897 guitext->setText(L"Minetest-c55");
\r
1898 guitext2->setVisible(false);
\r
1899 guitext_info->setVisible(false);
\r
1900 guitext_chat->setVisible(false);
\r
1902 // Initialize menu data
\r
1903 MainMenuData menudata;
\r
1904 menudata.address = narrow_to_wide(address);
\r
1905 menudata.name = narrow_to_wide(playername);
\r
1906 menudata.port = narrow_to_wide(itos(port));
\r
1907 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1909 GUIMainMenu *menu =
\r
1910 new GUIMainMenu(guienv, guiroot, -1,
\r
1911 &g_menumgr, &menudata, &g_gamecallback);
\r
1912 menu->allowFocusRemoval(true);
\r
1914 if(error_message != L"")
\r
1916 GUIMessageMenu *menu2 =
\r
1917 new GUIMessageMenu(guienv, guiroot, -1,
\r
1918 &g_menumgr, error_message.c_str());
\r
1920 error_message = L"";
\r
1923 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1925 dstream<<"Created main menu"<<std::endl;
\r
1927 while(g_device->run())
\r
1929 // Run global IrrlichtWrapper's main thread processing stuff
\r
1930 g_irrlicht->Run();
\r
1932 if(menu->getStatus() == true)
\r
1935 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1936 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1937 guienv->drawAll();
\r
1938 driver->endScene();
\r
1941 // Break out of menu-game loop to shut down cleanly
\r
1942 if(g_device->run() == false)
\r
1945 dstream<<"Dropping main menu"<<std::endl;
\r
1949 // Delete map if requested
\r
1950 if(menudata.delete_map)
\r
1952 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1954 error_message = L"Delete failed";
\r
1958 playername = wide_to_narrow(menudata.name);
\r
1959 address = wide_to_narrow(menudata.address);
\r
1960 port = stoi(wide_to_narrow(menudata.port));
\r
1961 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1963 // Check for valid parameters, restart menu if invalid.
\r
1964 if(playername == "")
\r
1966 error_message = L"Name required.";
\r
1971 g_settings.set("name", playername);
\r
1972 g_settings.set("address", address);
\r
1973 g_settings.set("port", itos(port));
\r
1974 // Update configuration file
\r
1975 if(configpath != "")
\r
1976 g_settings.updateConfigFile(configpath.c_str());
\r
1978 // Continue to game
\r
1982 // Break out of menu-game loop to shut down cleanly
\r
1983 if(g_device->run() == false)
\r
1987 Make a scope here so that the client and the server and other
\r
1988 stuff gets removed when disconnected or the irrlicht device
\r
1993 // This is set to true at the end of the scope
\r
1994 g_irrlicht->Shutdown(false);
\r
1997 Draw "Loading" screen
\r
1999 const wchar_t *text = L"Loading and connecting...";
\r
2000 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2001 core::vector2d<s32> textsize(300, text_height);
\r
2002 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2004 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2005 text, textrect, false, false);
\r
2006 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2008 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2009 guienv->drawAll();
\r
2010 driver->endScene();
\r
2012 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2016 SharedPtr will delete it when it goes out of scope.
\r
2018 SharedPtr<Server> server;
\r
2019 if(address == ""){
\r
2020 server = new Server(map_dir);
\r
2021 server->start(port);
\r
2028 Client client(device, playername.c_str(), draw_control);
\r
2030 g_client = &client;
\r
2032 Address connect_address(0,0,0,0, port);
\r
2035 //connect_address.Resolve("localhost");
\r
2036 connect_address.setAddress(127,0,0,1);
\r
2038 connect_address.Resolve(address.c_str());
\r
2040 catch(ResolveError &e)
\r
2042 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2044 error_message = L"Couldn't resolve address";
\r
2045 gui_loadingtext->remove();
\r
2049 dstream<<DTIME<<"Connecting to server at ";
\r
2050 connect_address.print(&dstream);
\r
2051 dstream<<std::endl;
\r
2052 client.connect(connect_address);
\r
2055 while(client.connectedAndInitialized() == false)
\r
2058 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2059 guienv->drawAll();
\r
2060 driver->endScene();
\r
2062 // Update client and server
\r
2066 if(server != NULL)
\r
2067 server->step(0.1);
\r
2073 catch(con::PeerNotFoundException &e)
\r
2075 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2077 error_message = L"Connection timed out.";
\r
2078 gui_loadingtext->remove();
\r
2085 /*scene::ISceneNode* skybox;
\r
2086 skybox = smgr->addSkyBoxSceneNode(
\r
2087 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2088 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2089 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2090 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2091 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2092 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2095 Create the camera node
\r
2098 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2099 0, // Camera parent
\r
2100 v3f(BS*100, BS*2, BS*100), // Look from
\r
2101 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2105 if(camera == NULL)
\r
2108 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2109 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2110 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2112 camera->setFOV(FOV_ANGLE);
\r
2114 // Just so big a value that everything rendered is visible
\r
2115 camera->setFarValue(100000*BS);
\r
2117 f32 camera_yaw = 0; // "right/left"
\r
2118 f32 camera_pitch = 0; // "up/down"
\r
2124 gui_loadingtext->remove();
\r
2127 Add some gui stuff
\r
2130 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2131 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2132 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2133 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2135 // Test the text input system
\r
2136 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2138 /*GUIMessageMenu *menu =
\r
2139 new GUIMessageMenu(guienv, guiroot, -1,
\r
2144 // Launch pause menu
\r
2145 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2146 &g_menumgr))->drop();
\r
2149 guitext2->setVisible(true);
\r
2150 guitext_info->setVisible(true);
\r
2151 guitext_chat->setVisible(true);
\r
2153 //s32 guitext_chat_pad_bottom = 70;
\r
2155 v2u32 screensize(0,0);
\r
2156 v2u32 last_screensize(0,0);
\r
2159 Some statistics are collected in these
\r
2162 u32 beginscenetime = 0;
\r
2163 u32 scenetime = 0;
\r
2164 u32 endscenetime = 0;
\r
2167 //throw con::PeerNotFoundException("lol");
\r
2169 core::list<float> frametime_log;
\r
2175 bool first_loop_after_window_activation = true;
\r
2177 // Time is in milliseconds
\r
2178 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2179 // NOTE: So we have to use getTime() and call run()s between them
\r
2180 u32 lasttime = device->getTimer()->getTime();
\r
2182 while(device->run())
\r
2184 if(g_disconnect_requested)
\r
2186 g_disconnect_requested = false;
\r
2191 Run global IrrlichtWrapper's main thread processing stuff
\r
2193 g_irrlicht->Run();
\r
2196 Process TextureSource's queue
\r
2198 texturesource->processQueue();
\r
2201 Random calculations
\r
2203 last_screensize = screensize;
\r
2204 screensize = driver->getScreenSize();
\r
2205 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2206 //bool screensize_changed = screensize != last_screensize;
\r
2208 // Hilight boxes collected during the loop and displayed
\r
2209 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2212 std::wstring infotext;
\r
2214 // When screen size changes, update positions and sizes of stuff
\r
2215 /*if(screensize_changed)
\r
2217 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2218 quick_inventory->updatePosition(pos);
\r
2221 //TimeTaker //timer1("//timer1");
\r
2223 // Time of frame without fps limit
\r
2227 // not using getRealTime is necessary for wine
\r
2228 u32 time = device->getTimer()->getTime();
\r
2229 if(time > lasttime)
\r
2230 busytime_u32 = time - lasttime;
\r
2233 busytime = busytime_u32 / 1000.0;
\r
2236 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2238 // Absolutelu necessary for wine!
\r
2245 updateViewingRange(busytime, &client);
\r
2252 float fps_max = g_settings.getFloat("fps_max");
\r
2253 u32 frametime_min = 1000./fps_max;
\r
2255 if(busytime_u32 < frametime_min)
\r
2257 u32 sleeptime = frametime_min - busytime_u32;
\r
2258 device->sleep(sleeptime);
\r
2262 // Absolutelu necessary for wine!
\r
2266 Time difference calculation
\r
2268 f32 dtime; // in seconds
\r
2270 u32 time = device->getTimer()->getTime();
\r
2271 if(time > lasttime)
\r
2272 dtime = (time - lasttime) / 1000.0;
\r
2278 Log frametime for visualization
\r
2280 frametime_log.push_back(dtime);
\r
2281 if(frametime_log.size() > 100)
\r
2283 core::list<float>::Iterator i = frametime_log.begin();
\r
2284 frametime_log.erase(i);
\r
2288 Visualize frametime in terminal
\r
2290 /*for(u32 i=0; i<dtime*400; i++)
\r
2292 std::cout<<std::endl;*/
\r
2295 Time average and jitter calculation
\r
2298 static f32 dtime_avg1 = 0.0;
\r
2299 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2300 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2302 static f32 dtime_jitter1_max_sample = 0.0;
\r
2303 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2305 static f32 jitter1_max = 0.0;
\r
2306 static f32 counter = 0.0;
\r
2307 if(dtime_jitter1 > jitter1_max)
\r
2308 jitter1_max = dtime_jitter1;
\r
2313 dtime_jitter1_max_sample = jitter1_max;
\r
2314 dtime_jitter1_max_fraction
\r
2315 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2316 jitter1_max = 0.0;
\r
2321 Busytime average and jitter calculation
\r
2324 static f32 busytime_avg1 = 0.0;
\r
2325 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2326 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2328 static f32 busytime_jitter1_max_sample = 0.0;
\r
2329 static f32 busytime_jitter1_min_sample = 0.0;
\r
2331 static f32 jitter1_max = 0.0;
\r
2332 static f32 jitter1_min = 0.0;
\r
2333 static f32 counter = 0.0;
\r
2334 if(busytime_jitter1 > jitter1_max)
\r
2335 jitter1_max = busytime_jitter1;
\r
2336 if(busytime_jitter1 < jitter1_min)
\r
2337 jitter1_min = busytime_jitter1;
\r
2339 if(counter > 0.0){
\r
2341 busytime_jitter1_max_sample = jitter1_max;
\r
2342 busytime_jitter1_min_sample = jitter1_min;
\r
2343 jitter1_max = 0.0;
\r
2344 jitter1_min = 0.0;
\r
2349 Debug info for client
\r
2352 static float counter = 0.0;
\r
2357 client.printDebugInfo(std::cout);
\r
2362 Input handler step()
\r
2364 g_input->step(dtime);
\r
2367 Player speed control
\r
2376 bool a_superspeed,
\r
2380 PlayerControl control(
\r
2381 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2382 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2383 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2384 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2385 g_input->isKeyDown(irr::KEY_SPACE),
\r
2386 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2387 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2388 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2392 client.setPlayerControl(control);
\r
2396 Process environment
\r
2400 //TimeTaker timer("client.step(dtime)");
\r
2401 client.step(dtime);
\r
2402 //client.step(dtime_avg1);
\r
2405 if(server != NULL)
\r
2407 //TimeTaker timer("server->step(dtime)");
\r
2408 server->step(dtime);
\r
2411 v3f player_position = client.getPlayerPosition();
\r
2413 //TimeTaker //timer2("//timer2");
\r
2416 Mouse and camera control
\r
2419 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2422 device->getCursorControl()->setVisible(false);
\r
2424 if(first_loop_after_window_activation){
\r
2425 //std::cout<<"window active, first loop"<<std::endl;
\r
2426 first_loop_after_window_activation = false;
\r
2429 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2430 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2431 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2432 camera_yaw -= dx*0.2;
\r
2433 camera_pitch += dy*0.2;
\r
2434 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2435 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2437 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2440 device->getCursorControl()->setVisible(true);
\r
2442 //std::cout<<"window inactive"<<std::endl;
\r
2443 first_loop_after_window_activation = true;
\r
2446 camera_yaw = wrapDegrees(camera_yaw);
\r
2447 camera_pitch = wrapDegrees(camera_pitch);
\r
2449 v3f camera_direction = v3f(0,0,1);
\r
2450 camera_direction.rotateYZBy(camera_pitch);
\r
2451 camera_direction.rotateXZBy(camera_yaw);
\r
2453 // This is at the height of the eyes of the current figure
\r
2454 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2455 // This is more like in minecraft
\r
2456 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2458 camera->setPosition(camera_position);
\r
2459 // *100.0 helps in large map coordinates
\r
2460 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2462 if(FIELD_OF_VIEW_TEST){
\r
2463 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2464 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2467 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2468 //TimeTaker timer("client.updateCamera");
\r
2469 client.updateCamera(camera_position, camera_direction);
\r
2473 //TimeTaker //timer3("//timer3");
\r
2476 Calculate what block is the crosshair pointing to
\r
2479 //u32 t1 = device->getTimer()->getRealTime();
\r
2481 //f32 d = 4; // max. distance
\r
2482 f32 d = 4; // max. distance
\r
2483 core::line3d<f32> shootline(camera_position,
\r
2484 camera_position + camera_direction * BS * (d+1));
\r
2486 MapBlockObject *selected_object = client.getSelectedObject
\r
2487 (d*BS, camera_position, shootline);
\r
2490 If it's pointing to a MapBlockObject
\r
2493 if(selected_object != NULL)
\r
2495 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2497 core::aabbox3d<f32> box_on_map
\r
2498 = selected_object->getSelectionBoxOnMap();
\r
2500 hilightboxes.push_back(box_on_map);
\r
2502 infotext = narrow_to_wide(selected_object->infoText());
\r
2504 if(g_input->getLeftClicked())
\r
2506 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2507 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2508 selected_object->getId(), g_selected_item);
\r
2510 else if(g_input->getRightClicked())
\r
2512 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2514 Check if we want to modify the object ourselves
\r
2516 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2518 dstream<<"Sign object right-clicked"<<std::endl;
\r
2520 if(random_input == false)
\r
2522 // Get a new text for it
\r
2524 TextDest *dest = new TextDestSign(
\r
2525 selected_object->getBlock()->getPos(),
\r
2526 selected_object->getId(),
\r
2529 SignObject *sign_object = (SignObject*)selected_object;
\r
2531 std::wstring wtext =
\r
2532 narrow_to_wide(sign_object->getText());
\r
2534 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2540 Otherwise pass the event to the server as-is
\r
2544 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2545 selected_object->getId(), g_selected_item);
\r
2549 else // selected_object == NULL
\r
2553 Find out which node we are pointing at
\r
2556 bool nodefound = false;
\r
2558 v3s16 neighbourpos;
\r
2559 core::aabbox3d<f32> nodehilightbox;
\r
2560 f32 mindistance = BS * 1001;
\r
2562 v3s16 pos_i = floatToInt(player_position);
\r
2564 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2568 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2569 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2570 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2571 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2572 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2573 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2575 for(s16 y = ystart; y <= yend; y++)
\r
2576 for(s16 z = zstart; z <= zend; z++)
\r
2577 for(s16 x = xstart; x <= xend; x++)
\r
2582 n = client.getNode(v3s16(x,y,z));
\r
2583 if(content_pointable(n.d) == false)
\r
2586 catch(InvalidPositionException &e)
\r
2592 v3f npf = intToFloat(np);
\r
2597 v3s16(0,0,1), // back
\r
2598 v3s16(0,1,0), // top
\r
2599 v3s16(1,0,0), // right
\r
2600 v3s16(0,0,-1), // front
\r
2601 v3s16(0,-1,0), // bottom
\r
2602 v3s16(-1,0,0), // left
\r
2608 if(n.d == CONTENT_TORCH)
\r
2610 v3s16 dir = unpackDir(n.dir);
\r
2611 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2612 dir_f *= BS/2 - BS/6 - BS/20;
\r
2613 v3f cpf = npf + dir_f;
\r
2614 f32 distance = (cpf - camera_position).getLength();
\r
2616 core::aabbox3d<f32> box;
\r
2619 if(dir == v3s16(0,-1,0))
\r
2621 box = core::aabbox3d<f32>(
\r
2622 npf - v3f(BS/6, BS/2, BS/6),
\r
2623 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2627 else if(dir == v3s16(0,1,0))
\r
2629 box = core::aabbox3d<f32>(
\r
2630 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2631 npf + v3f(BS/6, BS/2, BS/6)
\r
2637 box = core::aabbox3d<f32>(
\r
2638 cpf - v3f(BS/6, BS/3, BS/6),
\r
2639 cpf + v3f(BS/6, BS/3, BS/6)
\r
2643 if(distance < mindistance)
\r
2645 if(box.intersectsWithLine(shootline))
\r
2649 neighbourpos = np;
\r
2650 mindistance = distance;
\r
2651 nodehilightbox = box;
\r
2660 for(u16 i=0; i<6; i++)
\r
2662 v3f dir_f = v3f(dirs[i].X,
\r
2663 dirs[i].Y, dirs[i].Z);
\r
2664 v3f centerpoint = npf + dir_f * BS/2;
\r
2666 (centerpoint - camera_position).getLength();
\r
2668 if(distance < mindistance)
\r
2670 core::CMatrix4<f32> m;
\r
2671 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2673 // This is the back face
\r
2674 v3f corners[2] = {
\r
2675 v3f(BS/2, BS/2, BS/2),
\r
2676 v3f(-BS/2, -BS/2, BS/2+d)
\r
2679 for(u16 j=0; j<2; j++)
\r
2681 m.rotateVect(corners[j]);
\r
2682 corners[j] += npf;
\r
2685 core::aabbox3d<f32> facebox(corners[0]);
\r
2686 facebox.addInternalPoint(corners[1]);
\r
2688 if(facebox.intersectsWithLine(shootline))
\r
2692 neighbourpos = np + dirs[i];
\r
2693 mindistance = distance;
\r
2695 //nodehilightbox = facebox;
\r
2697 const float d = 0.502;
\r
2698 core::aabbox3d<f32> nodebox
\r
2699 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2700 v3f nodepos_f = intToFloat(nodepos);
\r
2701 nodebox.MinEdge += nodepos_f;
\r
2702 nodebox.MaxEdge += nodepos_f;
\r
2703 nodehilightbox = nodebox;
\r
2705 } // if distance < mindistance
\r
2707 } // regular block
\r
2710 static float nodig_delay_counter = 0.0;
\r
2714 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2716 static float dig_time = 0.0;
\r
2717 static u16 dig_index = 0;
\r
2719 // Visualize selection
\r
2721 hilightboxes.push_back(nodehilightbox);
\r
2725 if(g_input->getLeftReleased())
\r
2727 client.clearTempMod(nodepos);
\r
2731 if(nodig_delay_counter > 0.0)
\r
2733 nodig_delay_counter -= dtime;
\r
2737 if(nodepos != nodepos_old)
\r
2739 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2740 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2742 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2744 client.clearTempMod(nodepos_old);
\r
2749 if(g_input->getLeftClicked() ||
\r
2750 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2752 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2753 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2755 if(g_input->getLeftClicked())
\r
2757 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2759 if(g_input->getLeftState())
\r
2761 MapNode n = client.getNode(nodepos);
\r
2763 // Get tool name. Default is "" = bare hands
\r
2764 std::string toolname = "";
\r
2765 InventoryList *mlist = local_inventory.getList("main");
\r
2768 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2769 if(item && (std::string)item->getName() == "ToolItem")
\r
2771 ToolItem *titem = (ToolItem*)item;
\r
2772 toolname = titem->getToolName();
\r
2776 // Get digging properties for material and tool
\r
2777 u8 material = n.d;
\r
2778 DiggingProperties prop =
\r
2779 getDiggingProperties(material, toolname);
\r
2781 float dig_time_complete = 0.0;
\r
2783 if(prop.diggable == false)
\r
2785 /*dstream<<"Material "<<(int)material
\r
2786 <<" not diggable with \""
\r
2787 <<toolname<<"\""<<std::endl;*/
\r
2788 // I guess nobody will wait for this long
\r
2789 dig_time_complete = 10000000.0;
\r
2793 dig_time_complete = prop.time;
\r
2796 if(dig_time_complete >= 0.001)
\r
2798 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2799 * dig_time/dig_time_complete);
\r
2801 // This is for torches
\r
2804 dig_index = CRACK_ANIMATION_LENGTH;
\r
2807 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2809 //TimeTaker timer("client.setTempMod");
\r
2810 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2811 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2815 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2816 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2817 client.clearTempMod(nodepos);
\r
2818 client.removeNode(nodepos);
\r
2822 nodig_delay_counter = dig_time_complete
\r
2823 / (float)CRACK_ANIMATION_LENGTH;
\r
2825 // We don't want a corresponding delay to
\r
2826 // very time consuming nodes
\r
2827 if(nodig_delay_counter > 0.5)
\r
2829 nodig_delay_counter = 0.5;
\r
2831 // We want a slight delay to very little
\r
2832 // time consuming nodes
\r
2833 float mindelay = 0.15;
\r
2834 if(nodig_delay_counter < mindelay)
\r
2836 nodig_delay_counter = mindelay;
\r
2840 dig_time += dtime;
\r
2844 if(g_input->getRightClicked())
\r
2846 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2847 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2850 nodepos_old = nodepos;
\r
2855 } // selected_object == NULL
\r
2857 g_input->resetLeftClicked();
\r
2858 g_input->resetRightClicked();
\r
2860 if(g_input->getLeftReleased())
\r
2862 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2864 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2866 if(g_input->getRightReleased())
\r
2868 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2872 g_input->resetLeftReleased();
\r
2873 g_input->resetRightReleased();
\r
2876 Calculate stuff for drawing
\r
2879 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2881 u32 daynight_ratio = client.getDayNightRatio();
\r
2882 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2883 video::SColor bgcolor = video::SColor(
\r
2885 skycolor.getRed() * l / 255,
\r
2886 skycolor.getGreen() * l / 255,
\r
2887 skycolor.getBlue() * l / 255);
\r
2893 if(g_settings.getBool("enable_fog") == true)
\r
2895 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2896 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2897 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2898 if(draw_control.range_all)
\r
2899 range = 100000*BS;
\r
2903 video::EFT_FOG_LINEAR,
\r
2907 false, // pixel fog
\r
2908 false // range fog
\r
2915 video::EFT_FOG_LINEAR,
\r
2919 false, // pixel fog
\r
2920 false // range fog
\r
2926 Update gui stuff (0ms)
\r
2929 //TimeTaker guiupdatetimer("Gui updating");
\r
2932 static float drawtime_avg = 0;
\r
2933 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2934 static float beginscenetime_avg = 0;
\r
2935 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2936 static float scenetime_avg = 0;
\r
2937 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2938 static float endscenetime_avg = 0;
\r
2939 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2941 char temptext[300];
\r
2942 snprintf(temptext, 300, "Minetest-c55 ("
\r
2944 ", R: range_all=%i"
\r
2946 " drawtime=%.0f, beginscenetime=%.0f"
\r
2947 ", scenetime=%.0f, endscenetime=%.0f",
\r
2949 draw_control.range_all,
\r
2951 beginscenetime_avg,
\r
2956 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2960 char temptext[300];
\r
2961 snprintf(temptext, 300,
\r
2962 "(% .1f, % .1f, % .1f)"
\r
2963 " (% .3f < btime_jitter < % .3f"
\r
2964 ", dtime_jitter = % .1f %%"
\r
2965 ", v_range = %.1f)",
\r
2966 player_position.X/BS,
\r
2967 player_position.Y/BS,
\r
2968 player_position.Z/BS,
\r
2969 busytime_jitter1_min_sample,
\r
2970 busytime_jitter1_max_sample,
\r
2971 dtime_jitter1_max_fraction * 100.0,
\r
2972 draw_control.wanted_range
\r
2975 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2979 guitext_info->setText(infotext.c_str());
\r
2983 Get chat messages from client
\r
2986 // Get new messages
\r
2987 std::wstring message;
\r
2988 while(client.getChatMessage(message))
\r
2990 chat_lines.push_back(ChatLine(message));
\r
2991 /*if(chat_lines.size() > 6)
\r
2993 core::list<ChatLine>::Iterator
\r
2994 i = chat_lines.begin();
\r
2995 chat_lines.erase(i);
\r
2998 // Append them to form the whole static text and throw
\r
2999 // it to the gui element
\r
3000 std::wstring whole;
\r
3001 // This will correspond to the line number counted from
\r
3002 // top to bottom, from size-1 to 0
\r
3003 s16 line_number = chat_lines.size();
\r
3004 // Count of messages to be removed from the top
\r
3005 u16 to_be_removed_count = 0;
\r
3006 for(core::list<ChatLine>::Iterator
\r
3007 i = chat_lines.begin();
\r
3008 i != chat_lines.end(); i++)
\r
3010 // After this, line number is valid for this loop
\r
3013 (*i).age += dtime;
\r
3015 This results in a maximum age of 60*6 to the
\r
3016 lowermost line and a maximum of 6 lines
\r
3018 float allowed_age = (6-line_number) * 60.0;
\r
3020 if((*i).age > allowed_age)
\r
3022 to_be_removed_count++;
\r
3025 whole += (*i).text + L'\n';
\r
3027 for(u16 i=0; i<to_be_removed_count; i++)
\r
3029 core::list<ChatLine>::Iterator
\r
3030 it = chat_lines.begin();
\r
3031 chat_lines.erase(it);
\r
3033 guitext_chat->setText(whole.c_str());
\r
3035 // Update gui element size and position
\r
3037 /*core::rect<s32> rect(
\r
3039 screensize.Y - guitext_chat_pad_bottom
\r
3040 - text_height*chat_lines.size(),
\r
3041 screensize.X - 10,
\r
3042 screensize.Y - guitext_chat_pad_bottom
\r
3044 core::rect<s32> rect(
\r
3047 screensize.X - 10,
\r
3048 50 + text_height*chat_lines.size()
\r
3051 guitext_chat->setRelativePosition(rect);
\r
3053 if(chat_lines.size() == 0)
\r
3054 guitext_chat->setVisible(false);
\r
3056 guitext_chat->setVisible(true);
\r
3063 static u16 old_selected_item = 65535;
\r
3064 if(client.getLocalInventoryUpdated()
\r
3065 || g_selected_item != old_selected_item)
\r
3067 old_selected_item = g_selected_item;
\r
3068 //std::cout<<"Updating local inventory"<<std::endl;
\r
3069 client.getLocalInventory(local_inventory);
\r
3070 /*quick_inventory->setSelection(g_selected_item);
\r
3071 quick_inventory->update();*/
\r
3075 Send actions returned by the inventory menu
\r
3077 while(inventory_action_queue.size() != 0)
\r
3079 InventoryAction *a = inventory_action_queue.pop_front();
\r
3081 client.sendInventoryAction(a);
\r
3090 TimeTaker drawtimer("Drawing");
\r
3094 TimeTaker timer("beginScene");
\r
3095 driver->beginScene(true, true, bgcolor);
\r
3096 //driver->beginScene(false, true, bgcolor);
\r
3097 beginscenetime = timer.stop(true);
\r
3102 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3105 TimeTaker timer("smgr");
\r
3107 scenetime = timer.stop(true);
\r
3111 //TimeTaker timer9("auxiliary drawings");
\r
3115 //TimeTaker //timer10("//timer10");
\r
3117 video::SMaterial m;
\r
3118 //m.Thickness = 10;
\r
3120 m.Lighting = false;
\r
3121 driver->setMaterial(m);
\r
3123 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3125 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3126 i != hilightboxes.end(); i++)
\r
3128 /*std::cout<<"hilightbox min="
\r
3129 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3131 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3133 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3139 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3140 displaycenter + core::vector2d<s32>(10,0),
\r
3141 video::SColor(255,255,255,255));
\r
3142 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3143 displaycenter + core::vector2d<s32>(0,10),
\r
3144 video::SColor(255,255,255,255));
\r
3149 if(g_settings.getBool("frametime_graph") == true)
\r
3152 for(core::list<float>::Iterator
\r
3153 i = frametime_log.begin();
\r
3154 i != frametime_log.end();
\r
3157 driver->draw2DLine(v2s32(x,50),
\r
3158 v2s32(x,50+(*i)*1000),
\r
3159 video::SColor(255,255,255,255));
\r
3167 //TimeTaker //timer11("//timer11");
\r
3173 guienv->drawAll();
\r
3179 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3180 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3185 TimeTaker timer("endScene");
\r
3186 driver->endScene();
\r
3187 endscenetime = timer.stop(true);
\r
3190 drawtime = drawtimer.stop(true);
\r
3196 static s16 lastFPS = 0;
\r
3197 //u16 fps = driver->getFPS();
\r
3198 u16 fps = (1.0/dtime_avg1);
\r
3200 if (lastFPS != fps)
\r
3202 core::stringw str = L"Minetest [";
\r
3203 str += driver->getName();
\r
3207 device->setWindowCaption(str.c_str());
\r
3213 device->yield();*/
\r
3216 //delete quick_inventory;
\r
3219 Disable texture fetches and other stuff that is queued
\r
3220 to be processed by the main loop.
\r
3222 This has to be done before client goes out of scope.
\r
3224 g_irrlicht->Shutdown(true);
\r
3226 } // client and server are deleted at this point
\r
3229 catch(con::PeerNotFoundException &e)
\r
3231 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3232 error_message = L"Connection timed out.";
\r
3235 } // Menu-game loop
\r
3240 In the end, delete the Irrlicht device.
\r
3245 Update configuration file
\r
3247 /*if(configpath != "")
\r
3249 g_settings.updateConfigFile(configpath.c_str());
\r
3252 END_DEBUG_EXCEPTION_HANDLER
\r
3254 debugstreams_deinit();
\r