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
281 === Making it more portable
\r
282 * Some MSVC: std::sto* are defined without a namespace and collide
\r
283 with the ones in utility.h
\r
286 * Add mud underground
\r
287 * Make an "environment metafile" to store at least time of day
\r
288 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
289 - Or maybe move content_features to material.{h,cpp}?
\r
290 * Add some kind of erosion and other stuff that now is possible
\r
291 * Make client to fetch stuff asynchronously
\r
292 - Needs method SyncProcessData
\r
293 * Fix the problem with the server constantly saving one or a few
\r
294 blocks? List the first saved block, maybe it explains.
\r
295 - It is probably caused by oscillating water
\r
296 * Water doesn't start flowing after map generation like it should
\r
297 - Are there still problems?
\r
298 * Better water generation (spread it to underwater caverns but don't
\r
299 fill dungeons that don't touch big water masses)
\r
300 * When generating a chunk and the neighboring chunk doesn't have mud
\r
301 and stuff yet and the ground is fairly flat, the mud will flow to
\r
302 the other chunk making nasty straight walls when the other chunk
\r
303 is generated. Fix it.
\r
304 * Make a small history check to transformLiquids to detect and log
\r
305 continuous oscillations, in such detail that they can be fixed.
\r
306 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
308 ======================================================================
\r
313 Setting this to 1 enables a special camera mode that forces
\r
314 the renderers to think that the camera statically points from
\r
315 the starting place to a static direction.
\r
317 This allows one to move around with the player and see what
\r
318 is actually drawn behind solid things and behind the player.
\r
320 #define FIELD_OF_VIEW_TEST 0
\r
324 #pragma message ("Disabling unit tests")
\r
326 #warning "Disabling unit tests"
\r
328 // Disable unit tests
\r
329 #define ENABLE_TESTS 0
\r
331 // Enable unit tests
\r
332 #define ENABLE_TESTS 1
\r
336 #pragma comment(lib, "Irrlicht.lib")
\r
337 //#pragma comment(lib, "jthread.lib")
\r
338 #pragma comment(lib, "zlibwapi.lib")
\r
339 #pragma comment(lib, "Shell32.lib")
\r
340 // This would get rid of the console window
\r
341 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
344 #include <iostream>
\r
346 #include <jmutexautolock.h>
\r
347 #include <locale.h>
\r
348 #include "common_irrlicht.h"
\r
351 #include "player.h"
\r
354 #include "environment.h"
\r
355 #include "server.h"
\r
356 #include "client.h"
\r
357 #include "serialization.h"
\r
358 #include "constants.h"
\r
359 #include "strfnd.h"
\r
360 #include "porting.h"
\r
361 #include "irrlichtwrapper.h"
\r
362 #include "gettime.h"
\r
363 #include "porting.h"
\r
364 #include "guiPauseMenu.h"
\r
365 #include "guiInventoryMenu.h"
\r
366 #include "guiTextInputMenu.h"
\r
367 #include "materials.h"
\r
368 #include "guiMessageMenu.h"
\r
369 #include "filesys.h"
\r
370 #include "config.h"
\r
371 #include "guiMainMenu.h"
\r
372 #include "mineral.h"
\r
376 // TODO: Remove this
\r
377 IrrlichtWrapper *g_irrlicht = NULL;
\r
379 // This makes textures
\r
380 ITextureSource *g_texturesource = NULL;
\r
382 MapDrawControl draw_control;
\r
386 These are loaded from the config file.
\r
389 Settings g_settings;
\r
391 extern void set_default_settings();
\r
397 IrrlichtDevice *g_device = NULL;
\r
398 Client *g_client = NULL;
\r
400 /*const s16 quickinv_size = 40;
\r
401 const s16 quickinv_padding = 8;
\r
402 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
403 const s16 quickinv_outer_padding = 4;
\r
404 const s16 quickinv_itemcount = 8;*/
\r
406 const s32 hotbar_itemcount = 8;
\r
407 const s32 hotbar_imagesize = 36;
\r
413 gui::IGUIEnvironment* guienv = NULL;
\r
414 gui::IGUIStaticText *guiroot = NULL;
\r
416 class MainMenuManager : public IMenuManager
\r
419 virtual void createdMenu(GUIModalMenu *menu)
\r
421 for(core::list<GUIModalMenu*>::Iterator
\r
422 i = m_stack.begin();
\r
423 i != m_stack.end(); i++)
\r
425 assert(*i != menu);
\r
428 if(m_stack.size() != 0)
\r
429 (*m_stack.getLast())->setVisible(false);
\r
430 m_stack.push_back(menu);
\r
433 virtual void deletingMenu(GUIModalMenu *menu)
\r
435 // Remove all entries if there are duplicates
\r
436 bool removed_entry;
\r
438 removed_entry = false;
\r
439 for(core::list<GUIModalMenu*>::Iterator
\r
440 i = m_stack.begin();
\r
441 i != m_stack.end(); i++)
\r
446 removed_entry = true;
\r
450 }while(removed_entry);
\r
452 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
453 assert(*i == menu);
\r
454 m_stack.erase(i);*/
\r
456 if(m_stack.size() != 0)
\r
457 (*m_stack.getLast())->setVisible(true);
\r
462 return m_stack.size();
\r
465 core::list<GUIModalMenu*> m_stack;
\r
468 MainMenuManager g_menumgr;
\r
470 bool noMenuActive()
\r
472 return (g_menumgr.menuCount() == 0);
\r
475 bool g_disconnect_requested = false;
\r
477 class MainGameCallback : public IGameCallback
\r
480 virtual void exitToOS()
\r
482 g_device->closeDevice();
\r
485 virtual void disconnect()
\r
487 g_disconnect_requested = true;
\r
491 MainGameCallback g_gamecallback;
\r
493 // Inventory actions from the menu are buffered here before sending
\r
494 Queue<InventoryAction*> inventory_action_queue;
\r
495 // This is a copy of the inventory that the client's environment has
\r
496 Inventory local_inventory;
\r
498 u16 g_selected_item = 0;
\r
505 std::ostream *dout_con_ptr = &dummyout;
\r
506 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
507 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
508 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
509 //std::ostream *dout_con_ptr = &dstream;
\r
510 //std::ostream *derr_con_ptr = &dstream;
\r
513 std::ostream *dout_server_ptr = &dstream;
\r
514 std::ostream *derr_server_ptr = &dstream;
\r
517 std::ostream *dout_client_ptr = &dstream;
\r
518 std::ostream *derr_client_ptr = &dstream;
\r
521 gettime.h implementation
\r
527 Use irrlicht because it is more precise than porting.h's
\r
530 if(g_irrlicht == NULL)
\r
532 return g_irrlicht->getTime();
\r
539 struct TextDestSign : public TextDest
\r
541 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
543 m_blockpos = blockpos;
\r
547 void gotText(std::wstring text)
\r
549 std::string ntext = wide_to_narrow(text);
\r
550 dstream<<"Changing text of a sign object: "
\r
551 <<ntext<<std::endl;
\r
552 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
560 struct TextDestChat : public TextDest
\r
562 TextDestChat(Client *client)
\r
566 void gotText(std::wstring text)
\r
568 // Discard empty line
\r
572 // Parse command (server command starts with "/#")
\r
573 if(text[0] == L'/' && text[1] != L'#')
\r
575 std::wstring reply = L"Local: ";
\r
577 reply += L"Local commands not yet supported. "
\r
578 L"Server prefix is \"/#\".";
\r
580 m_client->addChatMessage(reply);
\r
585 m_client->sendChatMessage(text);
\r
587 m_client->addChatMessage(text);
\r
593 class MyEventReceiver : public IEventReceiver
\r
596 // This is the one method that we have to implement
\r
597 virtual bool OnEvent(const SEvent& event)
\r
600 React to nothing here if a menu is active
\r
602 if(noMenuActive() == false)
\r
608 // Remember whether each key is down or up
\r
609 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
611 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
613 if(event.KeyInput.PressedDown)
\r
615 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
621 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
623 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
625 dstream<<DTIME<<"MyEventReceiver: "
\r
626 <<"Launching pause menu"<<std::endl;
\r
627 // It will delete itself by itself
\r
628 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
629 &g_menumgr))->drop();
\r
632 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
634 dstream<<DTIME<<"MyEventReceiver: "
\r
635 <<"Launching inventory"<<std::endl;
\r
636 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
637 &local_inventory, &inventory_action_queue,
\r
638 &g_menumgr))->drop();
\r
641 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
643 TextDest *dest = new TextDestChat(g_client);
\r
645 (new GUITextInputMenu(guienv, guiroot, -1,
\r
652 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
653 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
655 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
656 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
658 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
659 g_selected_item = s1-1;
\r
660 dstream<<DTIME<<"Selected item: "
\r
661 <<g_selected_item<<std::endl;
\r
664 // Viewing range selection
\r
665 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
667 if(draw_control.range_all)
\r
669 draw_control.range_all = false;
\r
670 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
674 draw_control.range_all = true;
\r
675 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
679 // Print debug stacks
\r
680 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
682 dstream<<"-----------------------------------------"
\r
684 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
685 dstream<<"-----------------------------------------"
\r
687 debug_stacks_print();
\r
692 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
694 if(noMenuActive() == false)
\r
696 left_active = false;
\r
697 middle_active = false;
\r
698 right_active = false;
\r
702 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
703 left_active = event.MouseInput.isLeftPressed();
\r
704 middle_active = event.MouseInput.isMiddlePressed();
\r
705 right_active = event.MouseInput.isRightPressed();
\r
707 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
709 leftclicked = true;
\r
711 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
713 rightclicked = true;
\r
715 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
717 leftreleased = true;
\r
719 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
721 rightreleased = true;
\r
723 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
725 /*dstream<<"event.MouseInput.Wheel="
\r
726 <<event.MouseInput.Wheel<<std::endl;*/
\r
728 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
729 hotbar_itemcount-1);
\r
730 if(event.MouseInput.Wheel < 0)
\r
732 if(g_selected_item < max_item)
\r
735 g_selected_item = 0;
\r
737 else if(event.MouseInput.Wheel > 0)
\r
739 if(g_selected_item > 0)
\r
742 g_selected_item = max_item;
\r
751 // This is used to check whether a key is being held down
\r
752 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
754 return keyIsDown[keyCode];
\r
759 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
760 keyIsDown[i] = false;
\r
762 leftclicked = false;
\r
763 rightclicked = false;
\r
764 leftreleased = false;
\r
765 rightreleased = false;
\r
767 left_active = false;
\r
768 middle_active = false;
\r
769 right_active = false;
\r
780 bool rightreleased;
\r
783 bool middle_active;
\r
787 // We use this array to store the current state of each key
\r
788 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
791 IrrlichtDevice *m_device;
\r
800 virtual ~InputHandler()
\r
804 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
806 virtual v2s32 getMousePos() = 0;
\r
807 virtual void setMousePos(s32 x, s32 y) = 0;
\r
809 virtual bool getLeftState() = 0;
\r
810 virtual bool getRightState() = 0;
\r
812 virtual bool getLeftClicked() = 0;
\r
813 virtual bool getRightClicked() = 0;
\r
814 virtual void resetLeftClicked() = 0;
\r
815 virtual void resetRightClicked() = 0;
\r
817 virtual bool getLeftReleased() = 0;
\r
818 virtual bool getRightReleased() = 0;
\r
819 virtual void resetLeftReleased() = 0;
\r
820 virtual void resetRightReleased() = 0;
\r
822 virtual void step(float dtime) {};
\r
824 virtual void clear() {};
\r
827 InputHandler *g_input = NULL;
\r
829 class RealInputHandler : public InputHandler
\r
832 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
834 m_receiver(receiver)
\r
837 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
839 return m_receiver->IsKeyDown(keyCode);
\r
841 virtual v2s32 getMousePos()
\r
843 return m_device->getCursorControl()->getPosition();
\r
845 virtual void setMousePos(s32 x, s32 y)
\r
847 m_device->getCursorControl()->setPosition(x, y);
\r
850 virtual bool getLeftState()
\r
852 return m_receiver->left_active;
\r
854 virtual bool getRightState()
\r
856 return m_receiver->right_active;
\r
859 virtual bool getLeftClicked()
\r
861 return m_receiver->leftclicked;
\r
863 virtual bool getRightClicked()
\r
865 return m_receiver->rightclicked;
\r
867 virtual void resetLeftClicked()
\r
869 m_receiver->leftclicked = false;
\r
871 virtual void resetRightClicked()
\r
873 m_receiver->rightclicked = false;
\r
876 virtual bool getLeftReleased()
\r
878 return m_receiver->leftreleased;
\r
880 virtual bool getRightReleased()
\r
882 return m_receiver->rightreleased;
\r
884 virtual void resetLeftReleased()
\r
886 m_receiver->leftreleased = false;
\r
888 virtual void resetRightReleased()
\r
890 m_receiver->rightreleased = false;
\r
895 resetRightClicked();
\r
896 resetLeftClicked();
\r
899 IrrlichtDevice *m_device;
\r
900 MyEventReceiver *m_receiver;
\r
903 class RandomInputHandler : public InputHandler
\r
906 RandomInputHandler()
\r
908 leftclicked = false;
\r
909 rightclicked = false;
\r
910 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
911 keydown[i] = false;
\r
913 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
915 return keydown[keyCode];
\r
917 virtual v2s32 getMousePos()
\r
921 virtual void setMousePos(s32 x, s32 y)
\r
923 mousepos = v2s32(x,y);
\r
926 virtual bool getLeftState()
\r
930 virtual bool getRightState()
\r
935 virtual bool getLeftClicked()
\r
937 return leftclicked;
\r
939 virtual bool getRightClicked()
\r
941 return rightclicked;
\r
943 virtual void resetLeftClicked()
\r
945 leftclicked = false;
\r
947 virtual void resetRightClicked()
\r
949 rightclicked = false;
\r
952 virtual bool getLeftReleased()
\r
956 virtual bool getRightReleased()
\r
960 virtual void resetLeftReleased()
\r
963 virtual void resetRightReleased()
\r
967 virtual void step(float dtime)
\r
970 static float counter1 = 0;
\r
974 counter1 = 0.1*Rand(1,10);
\r
975 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
976 g_selected_material++;
\r
978 g_selected_material = 0;*/
\r
979 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
982 g_selected_item = 0;
\r
986 static float counter1 = 0;
\r
990 counter1 = 0.1*Rand(1, 40);
\r
991 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
995 static float counter1 = 0;
\r
999 counter1 = 0.1*Rand(1, 40);
\r
1000 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1004 static float counter1 = 0;
\r
1005 counter1 -= dtime;
\r
1006 if(counter1 < 0.0)
\r
1008 counter1 = 0.1*Rand(1, 40);
\r
1009 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1013 static float counter1 = 0;
\r
1014 counter1 -= dtime;
\r
1015 if(counter1 < 0.0)
\r
1017 counter1 = 0.1*Rand(1, 40);
\r
1018 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1022 static float counter1 = 0;
\r
1023 counter1 -= dtime;
\r
1024 if(counter1 < 0.0)
\r
1026 counter1 = 0.1*Rand(1, 20);
\r
1027 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1031 static float counter1 = 0;
\r
1032 counter1 -= dtime;
\r
1033 if(counter1 < 0.0)
\r
1035 counter1 = 0.1*Rand(1, 30);
\r
1036 leftclicked = true;
\r
1040 static float counter1 = 0;
\r
1041 counter1 -= dtime;
\r
1042 if(counter1 < 0.0)
\r
1044 counter1 = 0.1*Rand(1, 20);
\r
1045 rightclicked = true;
\r
1048 mousepos += mousespeed;
\r
1051 s32 Rand(s32 min, s32 max)
\r
1053 return (myrand()%(max-min+1))+min;
\r
1056 bool keydown[KEY_KEY_CODES_COUNT];
\r
1060 bool rightclicked;
\r
1063 void updateViewingRange(f32 frametime_in, Client *client)
\r
1065 if(draw_control.range_all == true)
\r
1068 static f32 added_frametime = 0;
\r
1069 static s16 added_frames = 0;
\r
1071 added_frametime += frametime_in;
\r
1072 added_frames += 1;
\r
1074 // Actually this counter kind of sucks because frametime is busytime
\r
1075 static f32 counter = 0;
\r
1076 counter -= frametime_in;
\r
1082 /*dstream<<__FUNCTION_NAME
\r
1083 <<": Collected "<<added_frames<<" frames, total of "
\r
1084 <<added_frametime<<"s."<<std::endl;*/
\r
1086 /*dstream<<"draw_control.blocks_drawn="
\r
1087 <<draw_control.blocks_drawn
\r
1088 <<", draw_control.blocks_would_have_drawn="
\r
1089 <<draw_control.blocks_would_have_drawn
\r
1092 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1093 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1095 draw_control.wanted_min_range = range_min;
\r
1096 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1098 float block_draw_ratio = 1.0;
\r
1099 if(draw_control.blocks_would_have_drawn != 0)
\r
1101 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1102 / (float)draw_control.blocks_would_have_drawn;
\r
1105 // Calculate the average frametime in the case that all wanted
\r
1106 // blocks had been drawn
\r
1107 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1109 added_frametime = 0.0;
\r
1112 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1113 float wanted_frametime = 1.0 / wanted_fps;
\r
1115 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1116 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1118 // If needed frametime change is very small, just return
\r
1119 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1121 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1125 float range = draw_control.wanted_range;
\r
1126 float new_range = range;
\r
1128 static s16 range_old = 0;
\r
1129 static f32 frametime_old = 0;
\r
1131 float d_range = range - range_old;
\r
1132 f32 d_frametime = frametime - frametime_old;
\r
1133 // A sane default of 30ms per 50 nodes of range
\r
1134 static f32 time_per_range = 30. / 50;
\r
1137 time_per_range = d_frametime / d_range;
\r
1140 // The minimum allowed calculated frametime-range derivative:
\r
1141 // Practically this sets the maximum speed of changing the range.
\r
1142 // The lower this value, the higher the maximum changing speed.
\r
1143 // A low value here results in wobbly range (0.001)
\r
1144 // A high value here results in slow changing range (0.0025)
\r
1145 // SUGG: This could be dynamically adjusted so that when
\r
1146 // the camera is turning, this is lower
\r
1147 //float min_time_per_range = 0.0015;
\r
1148 float min_time_per_range = 0.0010;
\r
1149 //float min_time_per_range = 0.05 / range;
\r
1150 if(time_per_range < min_time_per_range)
\r
1152 time_per_range = min_time_per_range;
\r
1153 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1157 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1160 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1161 // Dampen the change a bit to kill oscillations
\r
1162 //wanted_range_change *= 0.9;
\r
1163 //wanted_range_change *= 0.75;
\r
1164 wanted_range_change *= 0.5;
\r
1165 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1167 // If needed range change is very small, just return
\r
1168 if(fabs(wanted_range_change) < 0.001)
\r
1170 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1174 new_range += wanted_range_change;
\r
1175 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1177 //float new_range_unclamped = new_range;
\r
1178 if(new_range < range_min)
\r
1179 new_range = range_min;
\r
1180 if(new_range > range_max)
\r
1181 new_range = range_max;
\r
1183 /*if(new_range != new_range_unclamped)
\r
1184 dstream<<", clamped to "<<new_range<<std::endl;
\r
1186 dstream<<std::endl;*/
\r
1188 draw_control.wanted_range = new_range;
\r
1190 range_old = new_range;
\r
1191 frametime_old = frametime;
\r
1196 class GUIQuickInventory
\r
1199 GUIQuickInventory(
\r
1200 gui::IGUIEnvironment* env,
\r
1201 gui::IGUIElement* parent,
\r
1204 Inventory *inventory):
\r
1205 m_itemcount(itemcount),
\r
1206 m_inventory(inventory)
\r
1208 core::rect<s32> imgsize(0,0,quickinv_size,quickinv_size);
\r
1209 core::rect<s32> textsize(0,0,quickinv_size,quickinv_size);
\r
1210 bgtext = env->addStaticText(L"", core::rect<s32>(0,0,1,1), false, false);
\r
1211 bgtext->setBackgroundColor(
\r
1212 video::SColor(128,0,0,0));
\r
1213 for(s32 i=0; i<m_itemcount; i++)
\r
1215 m_images.push_back(env->addImage(
\r
1218 m_images[i]->setScaleImage(true);
\r
1219 m_texts.push_back(env->addStaticText(
\r
1224 /*m_texts[i]->setBackgroundColor(
\r
1225 video::SColor(128,0,0,0));*/
\r
1226 m_texts[i]->setTextAlignment(
\r
1227 gui::EGUIA_LOWERRIGHT,
\r
1228 gui::EGUIA_LOWERRIGHT);
\r
1231 updatePosition(pos);
\r
1234 ~GUIQuickInventory()
\r
1236 for(u32 i=0; i<m_texts.size(); i++)
\r
1238 m_texts[i]->remove();
\r
1240 for(u32 i=0; i<m_images.size(); i++)
\r
1242 m_images[i]->remove();
\r
1247 void updatePosition(v2s32 pos)
\r
1249 v2s32 spacing(quickinv_spacing, 0);
\r
1250 for(s32 i=0; i<m_itemcount; i++)
\r
1252 m_images[i]->setRelativePosition(pos + spacing*i);
\r
1253 m_texts[i]->setRelativePosition(pos + spacing*i);
\r
1255 core::rect<s32> bgrect(-quickinv_outer_padding,-quickinv_outer_padding,
\r
1256 (quickinv_itemcount-1)*quickinv_spacing+quickinv_size+quickinv_outer_padding,
\r
1257 quickinv_size+quickinv_outer_padding);
\r
1258 bgtext->setRelativePosition(bgrect+pos);
\r
1261 void setSelection(s32 i)
\r
1270 //start = m_selection - m_itemcount / 2;
\r
1272 InventoryList *mainlist = m_inventory->getList("main");
\r
1274 for(s32 i=0; i<m_itemcount; i++)
\r
1276 s32 j = i + start;
\r
1278 if(j > (s32)mainlist->getSize() - 1)
\r
1279 j -= mainlist->getSize();
\r
1281 j += mainlist->getSize();
\r
1283 InventoryItem *item = mainlist->getItem(j);
\r
1287 m_images[i]->setImage(NULL);
\r
1289 if(m_selection == j)
\r
1290 m_texts[i]->setText(L"->");
\r
1292 m_texts[i]->setText(L"");
\r
1294 // The next ifs will segfault with a NULL pointer
\r
1299 m_images[i]->setImage(item->getImage());
\r
1301 std::ostringstream os;
\r
1302 if(m_selection == j)
\r
1304 os<<item->getText();
\r
1305 m_texts[i]->setText(narrow_to_wide(os.str()).c_str());
\r
1308 if(m_selection == j)
\r
1309 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1311 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1312 m_texts[i]->setText(t);*/
\r
1318 gui::IGUIStaticText *bgtext;
\r
1319 core::array<gui::IGUIStaticText*> m_texts;
\r
1320 core::array<gui::IGUIImage*> m_images;
\r
1321 Inventory *m_inventory;
\r
1326 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1327 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1328 Inventory *inventory)
\r
1330 InventoryList *mainlist = inventory->getList("main");
\r
1331 if(mainlist == NULL)
\r
1333 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1337 s32 padding = imgsize/12;
\r
1338 //s32 height = imgsize + padding*2;
\r
1339 s32 width = itemcount*(imgsize+padding*2);
\r
1341 // Position of upper left corner of bar
\r
1342 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1344 // Draw background color
\r
1345 /*core::rect<s32> barrect(0,0,width,height);
\r
1347 video::SColor bgcolor(255,128,128,128);
\r
1348 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1350 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1352 for(s32 i=0; i<itemcount; i++)
\r
1354 InventoryItem *item = mainlist->getItem(i);
\r
1356 core::rect<s32> rect = imgrect + pos
\r
1357 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1359 if(g_selected_item == i)
\r
1361 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1362 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1363 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1368 video::SColor bgcolor2(128,0,0,0);
\r
1369 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1374 drawInventoryItem(driver, font, item, rect, NULL);
\r
1386 ChatLine(const std::wstring &a_text):
\r
1392 std::wstring text;
\r
1395 // These are defined global so that they're not optimized too much.
\r
1396 // Can't change them to volatile.
\r
1401 std::string tempstring;
\r
1402 std::string tempstring2;
\r
1407 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1408 TimeTaker timer("Testing std::string speed");
\r
1409 const u32 jj = 10000;
\r
1410 for(u32 j=0; j<jj; j++)
\r
1414 const u32 ii = 10;
\r
1415 for(u32 i=0; i<ii; i++){
\r
1416 tempstring2 += "asd";
\r
1418 for(u32 i=0; i<ii+1; i++){
\r
1419 tempstring += "asd";
\r
1420 if(tempstring == tempstring2)
\r
1426 dstream<<"All of the following tests should take around 100ms each."
\r
1430 TimeTaker timer("Testing floating-point conversion speed");
\r
1432 for(u32 i=0; i<4000000; i++){
\r
1439 TimeTaker timer("Testing floating-point vector speed");
\r
1441 tempv3f1 = v3f(1,2,3);
\r
1442 tempv3f2 = v3f(4,5,6);
\r
1443 for(u32 i=0; i<10000000; i++){
\r
1444 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1445 tempv3f2 += v3f(7,8,9);
\r
1450 TimeTaker timer("Testing core::map speed");
\r
1452 core::map<v2s16, f32> map1;
\r
1455 for(s16 y=0; y<ii; y++){
\r
1456 for(s16 x=0; x<ii; x++){
\r
1457 map1.insert(v2s16(x,y), tempf);
\r
1461 for(s16 y=ii-1; y>=0; y--){
\r
1462 for(s16 x=0; x<ii; x++){
\r
1463 tempf = map1[v2s16(x,y)];
\r
1469 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1470 TimeTaker timer("Testing mutex speed");
\r
1483 // Do at least 10ms
\r
1484 while(timer.getTime() < 10);
\r
1486 u32 dtime = timer.stop();
\r
1487 u32 per_ms = n / dtime;
\r
1488 std::cout<<"Done. "<<dtime<<"ms, "
\r
1489 <<per_ms<<"/ms"<<std::endl;
\r
1493 int main(int argc, char *argv[])
\r
1496 Parse command line
\r
1499 // List all allowed options
\r
1500 core::map<std::string, ValueSpec> allowed_options;
\r
1501 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1502 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1503 "Run server directly"));
\r
1504 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1505 "Load configuration from specified file"));
\r
1506 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1507 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1508 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1509 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1510 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1511 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1513 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1515 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1517 Settings cmd_args;
\r
1519 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1521 if(ret == false || cmd_args.getFlag("help"))
\r
1523 dstream<<"Allowed options:"<<std::endl;
\r
1524 for(core::map<std::string, ValueSpec>::Iterator
\r
1525 i = allowed_options.getIterator();
\r
1526 i.atEnd() == false; i++)
\r
1528 dstream<<" --"<<i.getNode()->getKey();
\r
1529 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1534 dstream<<" <value>";
\r
1536 dstream<<std::endl;
\r
1538 if(i.getNode()->getValue().help != NULL)
\r
1540 dstream<<" "<<i.getNode()->getValue().help
\r
1545 return cmd_args.getFlag("help") ? 0 : 1;
\r
1549 Low-level initialization
\r
1552 bool disable_stderr = false;
\r
1554 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1555 disable_stderr = true;
\r
1558 // Initialize debug streams
\r
1559 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1560 // Initialize debug stacks
\r
1561 debug_stacks_init();
\r
1563 DSTACK(__FUNCTION_NAME);
\r
1565 porting::signal_handler_init();
\r
1566 bool &kill = *porting::signal_handler_killstatus();
\r
1568 porting::initializePaths();
\r
1569 // Create user data directory
\r
1570 fs::CreateDir(porting::path_userdata);
\r
1572 // C-style stuff initialization
\r
1573 initializeMaterialProperties();
\r
1576 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1578 // Print startup message
\r
1579 dstream<<DTIME<<"minetest-c55"
\r
1580 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1581 <<", "<<BUILD_INFO
\r
1585 Basic initialization
\r
1588 // Initialize default settings
\r
1589 set_default_settings();
\r
1591 // Set locale. This is for forcing '.' as the decimal point.
\r
1592 std::locale::global(std::locale("C"));
\r
1593 // This enables printing all characters in bitmap font
\r
1594 setlocale(LC_CTYPE, "en_US");
\r
1596 // Initialize sockets
\r
1598 atexit(sockets_cleanup);
\r
1608 // Path of configuration file in use
\r
1609 std::string configpath = "";
\r
1611 if(cmd_args.exists("config"))
\r
1613 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1616 dstream<<"Could not read configuration from \""
\r
1617 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1620 configpath = cmd_args.get("config");
\r
1624 core::array<std::string> filenames;
\r
1625 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1626 #ifdef RUN_IN_PLACE
\r
1627 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1630 for(u32 i=0; i<filenames.size(); i++)
\r
1632 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1635 configpath = filenames[i];
\r
1640 // If no path found, use the first one (menu creates the file)
\r
1641 if(configpath == "")
\r
1642 configpath = filenames[0];
\r
1645 // Initialize random seed
\r
1650 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1652 These are needed for unit tests at least.
\r
1655 // Initial call with g_texturesource not set.
\r
1662 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1663 || cmd_args.getFlag("enable-unittests") == true)
\r
1668 /*for(s16 y=-100; y<100; y++)
\r
1669 for(s16 x=-100; x<100; x++)
\r
1671 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1681 if(cmd_args.exists("port"))
\r
1682 port = cmd_args.getU16("port");
\r
1683 else if(cmd_args.exists("port"))
\r
1684 port = g_settings.getU16("port");
\r
1687 std::string map_dir = porting::path_userdata+"/map";
\r
1688 if(cmd_args.exists("map-dir"))
\r
1689 map_dir = cmd_args.get("map-dir");
\r
1690 else if(g_settings.exists("map-dir"))
\r
1691 map_dir = g_settings.get("map-dir");
\r
1693 // Run dedicated server if asked to
\r
1694 if(cmd_args.getFlag("server"))
\r
1696 DSTACK("Dedicated server branch");
\r
1699 Server server(map_dir.c_str());
\r
1700 server.start(port);
\r
1703 dedicated_server_loop(server, kill);
\r
1712 // Address to connect to
\r
1713 std::string address = "";
\r
1715 if(cmd_args.exists("address"))
\r
1717 address = cmd_args.get("address");
\r
1721 address = g_settings.get("address");
\r
1724 std::string playername = g_settings.get("name");
\r
1726 // Resolution selection
\r
1728 bool fullscreen = false;
\r
1729 u16 screenW = g_settings.getU16("screenW");
\r
1730 u16 screenH = g_settings.getU16("screenH");
\r
1732 // Determine driver
\r
1734 video::E_DRIVER_TYPE driverType;
\r
1736 std::string driverstring = g_settings.get("video_driver");
\r
1738 if(driverstring == "null")
\r
1739 driverType = video::EDT_NULL;
\r
1740 else if(driverstring == "software")
\r
1741 driverType = video::EDT_SOFTWARE;
\r
1742 else if(driverstring == "burningsvideo")
\r
1743 driverType = video::EDT_BURNINGSVIDEO;
\r
1744 else if(driverstring == "direct3d8")
\r
1745 driverType = video::EDT_DIRECT3D8;
\r
1746 else if(driverstring == "direct3d9")
\r
1747 driverType = video::EDT_DIRECT3D9;
\r
1748 else if(driverstring == "opengl")
\r
1749 driverType = video::EDT_OPENGL;
\r
1752 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1753 "to opengl"<<std::endl;
\r
1754 driverType = video::EDT_OPENGL;
\r
1757 // create device and exit if creation failed
\r
1759 MyEventReceiver receiver;
\r
1761 IrrlichtDevice *device;
\r
1762 device = createDevice(driverType,
\r
1763 core::dimension2d<u32>(screenW, screenH),
\r
1764 16, fullscreen, false, false, &receiver);
\r
1767 return 1; // could not create selected driver.
\r
1769 g_device = device;
\r
1770 g_irrlicht = new IrrlichtWrapper(device);
\r
1771 TextureSource *texturesource = new TextureSource(device);
\r
1772 g_texturesource = texturesource;
\r
1775 Speed tests (done after irrlicht is loaded to get timer)
\r
1777 if(cmd_args.getFlag("speedtests"))
\r
1779 dstream<<"Running speed tests"<<std::endl;
\r
1784 device->setResizable(true);
\r
1786 bool random_input = g_settings.getBool("random_input")
\r
1787 || cmd_args.getFlag("random-input");
\r
1789 g_input = new RandomInputHandler();
\r
1791 g_input = new RealInputHandler(device, &receiver);
\r
1794 Continue initialization
\r
1797 video::IVideoDriver* driver = device->getVideoDriver();
\r
1800 This changes the minimum allowed number of vertices in a VBO.
\r
1803 //driver->setMinHardwareBufferVertexCount(50);
\r
1805 scene::ISceneManager* smgr = device->getSceneManager();
\r
1807 guienv = device->getGUIEnvironment();
\r
1808 gui::IGUISkin* skin = guienv->getSkin();
\r
1809 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1811 skin->setFont(font);
\r
1813 dstream<<"WARNING: Font file was not found."
\r
1814 " Using default font."<<std::endl;
\r
1815 // If font was not found, this will get us one
\r
1816 font = skin->getFont();
\r
1819 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1820 dstream<<"text_height="<<text_height<<std::endl;
\r
1822 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1823 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1824 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1825 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1826 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1827 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1830 Preload some textures and stuff
\r
1833 init_content_inventory_texture_paths();
\r
1834 init_mapnode(); // Second call with g_texturesource set
\r
1842 We need some kind of a root node to be able to add
\r
1843 custom gui elements directly on the screen.
\r
1844 Otherwise they won't be automatically drawn.
\r
1846 guiroot = guienv->addStaticText(L"",
\r
1847 core::rect<s32>(0, 0, 10000, 10000));
\r
1849 // First line of debug text
\r
1850 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1852 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1854 // Second line of debug text
\r
1855 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1857 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1860 // At the middle of the screen
\r
1861 // Object infos are shown in this
\r
1862 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1864 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1868 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1870 core::rect<s32>(0,0,0,0),
\r
1871 false, false); // Disable word wrap as of now
\r
1873 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1874 core::list<ChatLine> chat_lines;
\r
1877 If an error occurs, this is set to something and the
\r
1878 menu-game loop is restarted. It is then displayed before
\r
1881 std::wstring error_message = L"";
\r
1886 while(g_device->run() && kill == false)
\r
1889 // This is used for catching disconnects
\r
1894 Out-of-game menu loop.
\r
1896 Loop quits when menu returns proper parameters.
\r
1898 while(kill == false)
\r
1900 // Cursor can be non-visible when coming from the game
\r
1901 device->getCursorControl()->setVisible(true);
\r
1902 // Some stuff are left to scene manager when coming from the game
\r
1903 // (map at least?)
\r
1905 // Reset or hide the debug gui texts
\r
1906 guitext->setText(L"Minetest-c55");
\r
1907 guitext2->setVisible(false);
\r
1908 guitext_info->setVisible(false);
\r
1909 guitext_chat->setVisible(false);
\r
1911 // Initialize menu data
\r
1912 MainMenuData menudata;
\r
1913 menudata.address = narrow_to_wide(address);
\r
1914 menudata.name = narrow_to_wide(playername);
\r
1915 menudata.port = narrow_to_wide(itos(port));
\r
1916 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1918 GUIMainMenu *menu =
\r
1919 new GUIMainMenu(guienv, guiroot, -1,
\r
1920 &g_menumgr, &menudata, &g_gamecallback);
\r
1921 menu->allowFocusRemoval(true);
\r
1923 if(error_message != L"")
\r
1925 GUIMessageMenu *menu2 =
\r
1926 new GUIMessageMenu(guienv, guiroot, -1,
\r
1927 &g_menumgr, error_message.c_str());
\r
1929 error_message = L"";
\r
1932 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1934 dstream<<"Created main menu"<<std::endl;
\r
1936 while(g_device->run())
\r
1938 // Run global IrrlichtWrapper's main thread processing stuff
\r
1939 g_irrlicht->Run();
\r
1941 if(menu->getStatus() == true)
\r
1944 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1945 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1946 guienv->drawAll();
\r
1947 driver->endScene();
\r
1950 // Break out of menu-game loop to shut down cleanly
\r
1951 if(g_device->run() == false)
\r
1954 dstream<<"Dropping main menu"<<std::endl;
\r
1958 // Delete map if requested
\r
1959 if(menudata.delete_map)
\r
1961 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1963 error_message = L"Delete failed";
\r
1967 playername = wide_to_narrow(menudata.name);
\r
1968 address = wide_to_narrow(menudata.address);
\r
1969 port = stoi(wide_to_narrow(menudata.port));
\r
1970 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1972 // Check for valid parameters, restart menu if invalid.
\r
1973 if(playername == "")
\r
1975 error_message = L"Name required.";
\r
1980 g_settings.set("name", playername);
\r
1981 g_settings.set("address", address);
\r
1982 g_settings.set("port", itos(port));
\r
1983 // Update configuration file
\r
1984 if(configpath != "")
\r
1985 g_settings.updateConfigFile(configpath.c_str());
\r
1987 // Continue to game
\r
1991 // Break out of menu-game loop to shut down cleanly
\r
1992 if(g_device->run() == false)
\r
1996 Make a scope here so that the client and the server and other
\r
1997 stuff gets removed when disconnected or the irrlicht device
\r
2002 // This is set to true at the end of the scope
\r
2003 g_irrlicht->Shutdown(false);
\r
2006 Draw "Loading" screen
\r
2008 const wchar_t *text = L"Loading and connecting...";
\r
2009 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2010 core::vector2d<s32> textsize(300, text_height);
\r
2011 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2013 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2014 text, textrect, false, false);
\r
2015 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2017 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2018 guienv->drawAll();
\r
2019 driver->endScene();
\r
2021 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2025 SharedPtr will delete it when it goes out of scope.
\r
2027 SharedPtr<Server> server;
\r
2028 if(address == ""){
\r
2029 server = new Server(map_dir);
\r
2030 server->start(port);
\r
2037 Client client(device, playername.c_str(), draw_control);
\r
2039 g_client = &client;
\r
2041 Address connect_address(0,0,0,0, port);
\r
2044 //connect_address.Resolve("localhost");
\r
2045 connect_address.setAddress(127,0,0,1);
\r
2047 connect_address.Resolve(address.c_str());
\r
2049 catch(ResolveError &e)
\r
2051 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2053 error_message = L"Couldn't resolve address";
\r
2054 gui_loadingtext->remove();
\r
2058 dstream<<DTIME<<"Connecting to server at ";
\r
2059 connect_address.print(&dstream);
\r
2060 dstream<<std::endl;
\r
2061 client.connect(connect_address);
\r
2064 while(client.connectedAndInitialized() == false)
\r
2067 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2068 guienv->drawAll();
\r
2069 driver->endScene();
\r
2071 // Update client and server
\r
2075 if(server != NULL)
\r
2076 server->step(0.1);
\r
2082 catch(con::PeerNotFoundException &e)
\r
2084 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2086 error_message = L"Connection timed out.";
\r
2087 gui_loadingtext->remove();
\r
2094 /*scene::ISceneNode* skybox;
\r
2095 skybox = smgr->addSkyBoxSceneNode(
\r
2096 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2097 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2098 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2099 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2100 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2101 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2104 Create the camera node
\r
2107 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2108 0, // Camera parent
\r
2109 v3f(BS*100, BS*2, BS*100), // Look from
\r
2110 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2114 if(camera == NULL)
\r
2117 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2118 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2119 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2121 camera->setFOV(FOV_ANGLE);
\r
2123 // Just so big a value that everything rendered is visible
\r
2124 camera->setFarValue(100000*BS);
\r
2127 Lighting test code. Doesn't quite work this way.
\r
2128 The CPU-computed lighting is good.
\r
2132 smgr->addLightSceneNode(NULL,
\r
2133 v3f(0, BS*1000000, 0),
\r
2134 video::SColorf(0.3,0.3,0.3),
\r
2137 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2139 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2140 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2143 f32 camera_yaw = 0; // "right/left"
\r
2144 f32 camera_pitch = 0; // "up/down"
\r
2150 gui_loadingtext->remove();
\r
2153 Add some gui stuff
\r
2156 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2157 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2158 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2159 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2161 // Test the text input system
\r
2162 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2164 /*GUIMessageMenu *menu =
\r
2165 new GUIMessageMenu(guienv, guiroot, -1,
\r
2170 // Launch pause menu
\r
2171 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2172 &g_menumgr))->drop();
\r
2175 guitext2->setVisible(true);
\r
2176 guitext_info->setVisible(true);
\r
2177 guitext_chat->setVisible(true);
\r
2179 //s32 guitext_chat_pad_bottom = 70;
\r
2181 v2u32 screensize(0,0);
\r
2182 v2u32 last_screensize(0,0);
\r
2185 Some statistics are collected in these
\r
2188 u32 beginscenetime = 0;
\r
2189 u32 scenetime = 0;
\r
2190 u32 endscenetime = 0;
\r
2193 //throw con::PeerNotFoundException("lol");
\r
2195 core::list<float> frametime_log;
\r
2201 bool first_loop_after_window_activation = true;
\r
2203 // Time is in milliseconds
\r
2204 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2205 // NOTE: So we have to use getTime() and call run()s between them
\r
2206 u32 lasttime = device->getTimer()->getTime();
\r
2208 while(device->run() && kill == false)
\r
2210 if(g_disconnect_requested)
\r
2212 g_disconnect_requested = false;
\r
2217 Run global IrrlichtWrapper's main thread processing stuff
\r
2219 g_irrlicht->Run();
\r
2222 Process TextureSource's queue
\r
2224 texturesource->processQueue();
\r
2227 Random calculations
\r
2229 last_screensize = screensize;
\r
2230 screensize = driver->getScreenSize();
\r
2231 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2232 //bool screensize_changed = screensize != last_screensize;
\r
2234 // Hilight boxes collected during the loop and displayed
\r
2235 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2238 std::wstring infotext;
\r
2240 // When screen size changes, update positions and sizes of stuff
\r
2241 /*if(screensize_changed)
\r
2243 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2244 quick_inventory->updatePosition(pos);
\r
2247 //TimeTaker //timer1("//timer1");
\r
2249 // Time of frame without fps limit
\r
2253 // not using getRealTime is necessary for wine
\r
2254 u32 time = device->getTimer()->getTime();
\r
2255 if(time > lasttime)
\r
2256 busytime_u32 = time - lasttime;
\r
2259 busytime = busytime_u32 / 1000.0;
\r
2262 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2264 // Absolutelu necessary for wine!
\r
2271 updateViewingRange(busytime, &client);
\r
2278 float fps_max = g_settings.getFloat("fps_max");
\r
2279 u32 frametime_min = 1000./fps_max;
\r
2281 if(busytime_u32 < frametime_min)
\r
2283 u32 sleeptime = frametime_min - busytime_u32;
\r
2284 device->sleep(sleeptime);
\r
2288 // Absolutelu necessary for wine!
\r
2292 Time difference calculation
\r
2294 f32 dtime; // in seconds
\r
2296 u32 time = device->getTimer()->getTime();
\r
2297 if(time > lasttime)
\r
2298 dtime = (time - lasttime) / 1000.0;
\r
2304 Log frametime for visualization
\r
2306 frametime_log.push_back(dtime);
\r
2307 if(frametime_log.size() > 100)
\r
2309 core::list<float>::Iterator i = frametime_log.begin();
\r
2310 frametime_log.erase(i);
\r
2314 Visualize frametime in terminal
\r
2316 /*for(u32 i=0; i<dtime*400; i++)
\r
2318 std::cout<<std::endl;*/
\r
2321 Time average and jitter calculation
\r
2324 static f32 dtime_avg1 = 0.0;
\r
2325 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2326 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2328 static f32 dtime_jitter1_max_sample = 0.0;
\r
2329 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2331 static f32 jitter1_max = 0.0;
\r
2332 static f32 counter = 0.0;
\r
2333 if(dtime_jitter1 > jitter1_max)
\r
2334 jitter1_max = dtime_jitter1;
\r
2339 dtime_jitter1_max_sample = jitter1_max;
\r
2340 dtime_jitter1_max_fraction
\r
2341 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2342 jitter1_max = 0.0;
\r
2347 Busytime average and jitter calculation
\r
2350 static f32 busytime_avg1 = 0.0;
\r
2351 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2352 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2354 static f32 busytime_jitter1_max_sample = 0.0;
\r
2355 static f32 busytime_jitter1_min_sample = 0.0;
\r
2357 static f32 jitter1_max = 0.0;
\r
2358 static f32 jitter1_min = 0.0;
\r
2359 static f32 counter = 0.0;
\r
2360 if(busytime_jitter1 > jitter1_max)
\r
2361 jitter1_max = busytime_jitter1;
\r
2362 if(busytime_jitter1 < jitter1_min)
\r
2363 jitter1_min = busytime_jitter1;
\r
2365 if(counter > 0.0){
\r
2367 busytime_jitter1_max_sample = jitter1_max;
\r
2368 busytime_jitter1_min_sample = jitter1_min;
\r
2369 jitter1_max = 0.0;
\r
2370 jitter1_min = 0.0;
\r
2375 Debug info for client
\r
2378 static float counter = 0.0;
\r
2383 client.printDebugInfo(std::cout);
\r
2388 Input handler step()
\r
2390 g_input->step(dtime);
\r
2393 Player speed control
\r
2402 bool a_superspeed,
\r
2406 PlayerControl control(
\r
2407 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2408 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2409 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2410 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2411 g_input->isKeyDown(irr::KEY_SPACE),
\r
2412 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2413 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2414 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2418 client.setPlayerControl(control);
\r
2422 Process environment
\r
2426 //TimeTaker timer("client.step(dtime)");
\r
2427 client.step(dtime);
\r
2428 //client.step(dtime_avg1);
\r
2431 if(server != NULL)
\r
2433 //TimeTaker timer("server->step(dtime)");
\r
2434 server->step(dtime);
\r
2437 v3f player_position = client.getPlayerPosition();
\r
2439 //TimeTaker //timer2("//timer2");
\r
2442 Mouse and camera control
\r
2445 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2448 device->getCursorControl()->setVisible(false);
\r
2450 if(first_loop_after_window_activation){
\r
2451 //std::cout<<"window active, first loop"<<std::endl;
\r
2452 first_loop_after_window_activation = false;
\r
2455 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2456 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2457 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2458 camera_yaw -= dx*0.2;
\r
2459 camera_pitch += dy*0.2;
\r
2460 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2461 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2463 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2466 device->getCursorControl()->setVisible(true);
\r
2468 //std::cout<<"window inactive"<<std::endl;
\r
2469 first_loop_after_window_activation = true;
\r
2472 camera_yaw = wrapDegrees(camera_yaw);
\r
2473 camera_pitch = wrapDegrees(camera_pitch);
\r
2475 v3f camera_direction = v3f(0,0,1);
\r
2476 camera_direction.rotateYZBy(camera_pitch);
\r
2477 camera_direction.rotateXZBy(camera_yaw);
\r
2479 // This is at the height of the eyes of the current figure
\r
2480 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2481 // This is more like in minecraft
\r
2482 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2484 camera->setPosition(camera_position);
\r
2485 // *100.0 helps in large map coordinates
\r
2486 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2488 if(FIELD_OF_VIEW_TEST){
\r
2489 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2490 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2493 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2494 //TimeTaker timer("client.updateCamera");
\r
2495 client.updateCamera(camera_position, camera_direction);
\r
2499 //TimeTaker //timer3("//timer3");
\r
2502 Calculate what block is the crosshair pointing to
\r
2505 //u32 t1 = device->getTimer()->getRealTime();
\r
2507 //f32 d = 4; // max. distance
\r
2508 f32 d = 4; // max. distance
\r
2509 core::line3d<f32> shootline(camera_position,
\r
2510 camera_position + camera_direction * BS * (d+1));
\r
2512 MapBlockObject *selected_object = client.getSelectedObject
\r
2513 (d*BS, camera_position, shootline);
\r
2516 If it's pointing to a MapBlockObject
\r
2519 if(selected_object != NULL)
\r
2521 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2523 core::aabbox3d<f32> box_on_map
\r
2524 = selected_object->getSelectionBoxOnMap();
\r
2526 hilightboxes.push_back(box_on_map);
\r
2528 infotext = narrow_to_wide(selected_object->infoText());
\r
2530 if(g_input->getLeftClicked())
\r
2532 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2533 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2534 selected_object->getId(), g_selected_item);
\r
2536 else if(g_input->getRightClicked())
\r
2538 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2540 Check if we want to modify the object ourselves
\r
2542 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2544 dstream<<"Sign object right-clicked"<<std::endl;
\r
2546 if(random_input == false)
\r
2548 // Get a new text for it
\r
2550 TextDest *dest = new TextDestSign(
\r
2551 selected_object->getBlock()->getPos(),
\r
2552 selected_object->getId(),
\r
2555 SignObject *sign_object = (SignObject*)selected_object;
\r
2557 std::wstring wtext =
\r
2558 narrow_to_wide(sign_object->getText());
\r
2560 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2566 Otherwise pass the event to the server as-is
\r
2570 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2571 selected_object->getId(), g_selected_item);
\r
2575 else // selected_object == NULL
\r
2579 Find out which node we are pointing at
\r
2582 bool nodefound = false;
\r
2584 v3s16 neighbourpos;
\r
2585 core::aabbox3d<f32> nodehilightbox;
\r
2586 f32 mindistance = BS * 1001;
\r
2588 v3s16 pos_i = floatToInt(player_position);
\r
2590 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2594 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2595 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2596 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2597 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2598 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2599 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2601 for(s16 y = ystart; y <= yend; y++)
\r
2602 for(s16 z = zstart; z <= zend; z++)
\r
2603 for(s16 x = xstart; x <= xend; x++)
\r
2608 n = client.getNode(v3s16(x,y,z));
\r
2609 if(content_pointable(n.d) == false)
\r
2612 catch(InvalidPositionException &e)
\r
2618 v3f npf = intToFloat(np);
\r
2623 v3s16(0,0,1), // back
\r
2624 v3s16(0,1,0), // top
\r
2625 v3s16(1,0,0), // right
\r
2626 v3s16(0,0,-1), // front
\r
2627 v3s16(0,-1,0), // bottom
\r
2628 v3s16(-1,0,0), // left
\r
2634 if(n.d == CONTENT_TORCH)
\r
2636 v3s16 dir = unpackDir(n.dir);
\r
2637 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2638 dir_f *= BS/2 - BS/6 - BS/20;
\r
2639 v3f cpf = npf + dir_f;
\r
2640 f32 distance = (cpf - camera_position).getLength();
\r
2642 core::aabbox3d<f32> box;
\r
2645 if(dir == v3s16(0,-1,0))
\r
2647 box = core::aabbox3d<f32>(
\r
2648 npf - v3f(BS/6, BS/2, BS/6),
\r
2649 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2653 else if(dir == v3s16(0,1,0))
\r
2655 box = core::aabbox3d<f32>(
\r
2656 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2657 npf + v3f(BS/6, BS/2, BS/6)
\r
2663 box = core::aabbox3d<f32>(
\r
2664 cpf - v3f(BS/6, BS/3, BS/6),
\r
2665 cpf + v3f(BS/6, BS/3, BS/6)
\r
2669 if(distance < mindistance)
\r
2671 if(box.intersectsWithLine(shootline))
\r
2675 neighbourpos = np;
\r
2676 mindistance = distance;
\r
2677 nodehilightbox = box;
\r
2686 for(u16 i=0; i<6; i++)
\r
2688 v3f dir_f = v3f(dirs[i].X,
\r
2689 dirs[i].Y, dirs[i].Z);
\r
2690 v3f centerpoint = npf + dir_f * BS/2;
\r
2692 (centerpoint - camera_position).getLength();
\r
2694 if(distance < mindistance)
\r
2696 core::CMatrix4<f32> m;
\r
2697 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2699 // This is the back face
\r
2700 v3f corners[2] = {
\r
2701 v3f(BS/2, BS/2, BS/2),
\r
2702 v3f(-BS/2, -BS/2, BS/2+d)
\r
2705 for(u16 j=0; j<2; j++)
\r
2707 m.rotateVect(corners[j]);
\r
2708 corners[j] += npf;
\r
2711 core::aabbox3d<f32> facebox(corners[0]);
\r
2712 facebox.addInternalPoint(corners[1]);
\r
2714 if(facebox.intersectsWithLine(shootline))
\r
2718 neighbourpos = np + dirs[i];
\r
2719 mindistance = distance;
\r
2721 //nodehilightbox = facebox;
\r
2723 const float d = 0.502;
\r
2724 core::aabbox3d<f32> nodebox
\r
2725 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2726 v3f nodepos_f = intToFloat(nodepos);
\r
2727 nodebox.MinEdge += nodepos_f;
\r
2728 nodebox.MaxEdge += nodepos_f;
\r
2729 nodehilightbox = nodebox;
\r
2731 } // if distance < mindistance
\r
2733 } // regular block
\r
2736 static float nodig_delay_counter = 0.0;
\r
2740 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2742 static float dig_time = 0.0;
\r
2743 static u16 dig_index = 0;
\r
2745 // Visualize selection
\r
2747 hilightboxes.push_back(nodehilightbox);
\r
2751 if(g_input->getLeftReleased())
\r
2753 client.clearTempMod(nodepos);
\r
2757 if(nodig_delay_counter > 0.0)
\r
2759 nodig_delay_counter -= dtime;
\r
2763 if(nodepos != nodepos_old)
\r
2765 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2766 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2768 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2770 client.clearTempMod(nodepos_old);
\r
2775 if(g_input->getLeftClicked() ||
\r
2776 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2778 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2779 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2781 if(g_input->getLeftClicked())
\r
2783 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2785 if(g_input->getLeftState())
\r
2787 MapNode n = client.getNode(nodepos);
\r
2789 // Get tool name. Default is "" = bare hands
\r
2790 std::string toolname = "";
\r
2791 InventoryList *mlist = local_inventory.getList("main");
\r
2794 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2795 if(item && (std::string)item->getName() == "ToolItem")
\r
2797 ToolItem *titem = (ToolItem*)item;
\r
2798 toolname = titem->getToolName();
\r
2802 // Get digging properties for material and tool
\r
2803 u8 material = n.d;
\r
2804 DiggingProperties prop =
\r
2805 getDiggingProperties(material, toolname);
\r
2807 float dig_time_complete = 0.0;
\r
2809 if(prop.diggable == false)
\r
2811 /*dstream<<"Material "<<(int)material
\r
2812 <<" not diggable with \""
\r
2813 <<toolname<<"\""<<std::endl;*/
\r
2814 // I guess nobody will wait for this long
\r
2815 dig_time_complete = 10000000.0;
\r
2819 dig_time_complete = prop.time;
\r
2822 if(dig_time_complete >= 0.001)
\r
2824 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2825 * dig_time/dig_time_complete);
\r
2827 // This is for torches
\r
2830 dig_index = CRACK_ANIMATION_LENGTH;
\r
2833 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2835 //TimeTaker timer("client.setTempMod");
\r
2836 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2837 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2841 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2842 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2843 client.clearTempMod(nodepos);
\r
2844 client.removeNode(nodepos);
\r
2848 nodig_delay_counter = dig_time_complete
\r
2849 / (float)CRACK_ANIMATION_LENGTH;
\r
2851 // We don't want a corresponding delay to
\r
2852 // very time consuming nodes
\r
2853 if(nodig_delay_counter > 0.5)
\r
2855 nodig_delay_counter = 0.5;
\r
2857 // We want a slight delay to very little
\r
2858 // time consuming nodes
\r
2859 float mindelay = 0.15;
\r
2860 if(nodig_delay_counter < mindelay)
\r
2862 nodig_delay_counter = mindelay;
\r
2866 dig_time += dtime;
\r
2870 if(g_input->getRightClicked())
\r
2872 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2873 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2876 nodepos_old = nodepos;
\r
2881 } // selected_object == NULL
\r
2883 g_input->resetLeftClicked();
\r
2884 g_input->resetRightClicked();
\r
2886 if(g_input->getLeftReleased())
\r
2888 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2890 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2892 if(g_input->getRightReleased())
\r
2894 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2898 g_input->resetLeftReleased();
\r
2899 g_input->resetRightReleased();
\r
2902 Calculate stuff for drawing
\r
2905 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2907 u32 daynight_ratio = client.getDayNightRatio();
\r
2908 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2909 video::SColor bgcolor = video::SColor(
\r
2911 skycolor.getRed() * l / 255,
\r
2912 skycolor.getGreen() * l / 255,
\r
2913 skycolor.getBlue() * l / 255);
\r
2919 if(g_settings.getBool("enable_fog") == true)
\r
2921 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2922 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
2923 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
2924 if(draw_control.range_all)
\r
2925 range = 100000*BS;
\r
2929 video::EFT_FOG_LINEAR,
\r
2933 false, // pixel fog
\r
2934 false // range fog
\r
2941 video::EFT_FOG_LINEAR,
\r
2945 false, // pixel fog
\r
2946 false // range fog
\r
2952 Update gui stuff (0ms)
\r
2955 //TimeTaker guiupdatetimer("Gui updating");
\r
2958 static float drawtime_avg = 0;
\r
2959 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2960 static float beginscenetime_avg = 0;
\r
2961 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2962 static float scenetime_avg = 0;
\r
2963 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2964 static float endscenetime_avg = 0;
\r
2965 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2967 char temptext[300];
\r
2968 snprintf(temptext, 300, "Minetest-c55 ("
\r
2970 ", R: range_all=%i"
\r
2972 " drawtime=%.0f, beginscenetime=%.0f"
\r
2973 ", scenetime=%.0f, endscenetime=%.0f",
\r
2975 draw_control.range_all,
\r
2977 beginscenetime_avg,
\r
2982 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2986 char temptext[300];
\r
2987 snprintf(temptext, 300,
\r
2988 "(% .1f, % .1f, % .1f)"
\r
2989 " (% .3f < btime_jitter < % .3f"
\r
2990 ", dtime_jitter = % .1f %%"
\r
2991 ", v_range = %.1f)",
\r
2992 player_position.X/BS,
\r
2993 player_position.Y/BS,
\r
2994 player_position.Z/BS,
\r
2995 busytime_jitter1_min_sample,
\r
2996 busytime_jitter1_max_sample,
\r
2997 dtime_jitter1_max_fraction * 100.0,
\r
2998 draw_control.wanted_range
\r
3001 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3005 guitext_info->setText(infotext.c_str());
\r
3009 Get chat messages from client
\r
3012 // Get new messages
\r
3013 std::wstring message;
\r
3014 while(client.getChatMessage(message))
\r
3016 chat_lines.push_back(ChatLine(message));
\r
3017 /*if(chat_lines.size() > 6)
\r
3019 core::list<ChatLine>::Iterator
\r
3020 i = chat_lines.begin();
\r
3021 chat_lines.erase(i);
\r
3024 // Append them to form the whole static text and throw
\r
3025 // it to the gui element
\r
3026 std::wstring whole;
\r
3027 // This will correspond to the line number counted from
\r
3028 // top to bottom, from size-1 to 0
\r
3029 s16 line_number = chat_lines.size();
\r
3030 // Count of messages to be removed from the top
\r
3031 u16 to_be_removed_count = 0;
\r
3032 for(core::list<ChatLine>::Iterator
\r
3033 i = chat_lines.begin();
\r
3034 i != chat_lines.end(); i++)
\r
3036 // After this, line number is valid for this loop
\r
3039 (*i).age += dtime;
\r
3041 This results in a maximum age of 60*6 to the
\r
3042 lowermost line and a maximum of 6 lines
\r
3044 float allowed_age = (6-line_number) * 60.0;
\r
3046 if((*i).age > allowed_age)
\r
3048 to_be_removed_count++;
\r
3051 whole += (*i).text + L'\n';
\r
3053 for(u16 i=0; i<to_be_removed_count; i++)
\r
3055 core::list<ChatLine>::Iterator
\r
3056 it = chat_lines.begin();
\r
3057 chat_lines.erase(it);
\r
3059 guitext_chat->setText(whole.c_str());
\r
3061 // Update gui element size and position
\r
3063 /*core::rect<s32> rect(
\r
3065 screensize.Y - guitext_chat_pad_bottom
\r
3066 - text_height*chat_lines.size(),
\r
3067 screensize.X - 10,
\r
3068 screensize.Y - guitext_chat_pad_bottom
\r
3070 core::rect<s32> rect(
\r
3073 screensize.X - 10,
\r
3074 50 + text_height*chat_lines.size()
\r
3077 guitext_chat->setRelativePosition(rect);
\r
3079 if(chat_lines.size() == 0)
\r
3080 guitext_chat->setVisible(false);
\r
3082 guitext_chat->setVisible(true);
\r
3089 static u16 old_selected_item = 65535;
\r
3090 if(client.getLocalInventoryUpdated()
\r
3091 || g_selected_item != old_selected_item)
\r
3093 old_selected_item = g_selected_item;
\r
3094 //std::cout<<"Updating local inventory"<<std::endl;
\r
3095 client.getLocalInventory(local_inventory);
\r
3096 /*quick_inventory->setSelection(g_selected_item);
\r
3097 quick_inventory->update();*/
\r
3101 Send actions returned by the inventory menu
\r
3103 while(inventory_action_queue.size() != 0)
\r
3105 InventoryAction *a = inventory_action_queue.pop_front();
\r
3107 client.sendInventoryAction(a);
\r
3116 TimeTaker drawtimer("Drawing");
\r
3120 TimeTaker timer("beginScene");
\r
3121 driver->beginScene(true, true, bgcolor);
\r
3122 //driver->beginScene(false, true, bgcolor);
\r
3123 beginscenetime = timer.stop(true);
\r
3128 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3131 TimeTaker timer("smgr");
\r
3133 scenetime = timer.stop(true);
\r
3137 //TimeTaker timer9("auxiliary drawings");
\r
3141 //TimeTaker //timer10("//timer10");
\r
3143 video::SMaterial m;
\r
3144 //m.Thickness = 10;
\r
3146 m.Lighting = false;
\r
3147 driver->setMaterial(m);
\r
3149 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3151 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3152 i != hilightboxes.end(); i++)
\r
3154 /*std::cout<<"hilightbox min="
\r
3155 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3157 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3159 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3165 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3166 displaycenter + core::vector2d<s32>(10,0),
\r
3167 video::SColor(255,255,255,255));
\r
3168 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3169 displaycenter + core::vector2d<s32>(0,10),
\r
3170 video::SColor(255,255,255,255));
\r
3175 if(g_settings.getBool("frametime_graph") == true)
\r
3178 for(core::list<float>::Iterator
\r
3179 i = frametime_log.begin();
\r
3180 i != frametime_log.end();
\r
3183 driver->draw2DLine(v2s32(x,50),
\r
3184 v2s32(x,50+(*i)*1000),
\r
3185 video::SColor(255,255,255,255));
\r
3193 //TimeTaker //timer11("//timer11");
\r
3199 guienv->drawAll();
\r
3205 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3206 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3211 TimeTaker timer("endScene");
\r
3212 driver->endScene();
\r
3213 endscenetime = timer.stop(true);
\r
3216 drawtime = drawtimer.stop(true);
\r
3222 static s16 lastFPS = 0;
\r
3223 //u16 fps = driver->getFPS();
\r
3224 u16 fps = (1.0/dtime_avg1);
\r
3226 if (lastFPS != fps)
\r
3228 core::stringw str = L"Minetest [";
\r
3229 str += driver->getName();
\r
3233 device->setWindowCaption(str.c_str());
\r
3239 device->yield();*/
\r
3242 //delete quick_inventory;
\r
3245 Disable texture fetches and other stuff that is queued
\r
3246 to be processed by the main loop.
\r
3248 This has to be done before client goes out of scope.
\r
3250 g_irrlicht->Shutdown(true);
\r
3252 } // client and server are deleted at this point
\r
3255 catch(con::PeerNotFoundException &e)
\r
3257 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3258 error_message = L"Connection timed out.";
\r
3261 } // Menu-game loop
\r
3266 In the end, delete the Irrlicht device.
\r
3271 Update configuration file
\r
3273 /*if(configpath != "")
\r
3275 g_settings.updateConfigFile(configpath.c_str());
\r
3278 END_DEBUG_EXCEPTION_HANDLER
\r
3280 debugstreams_deinit();
\r