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
271 === Stuff to do before release
\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
284 * On Kray's machine, the new find_library(XXF86VM_LIBRARY, Xxf86vm)
\r
285 line doesn't find the library.
\r
287 === Stuff to do after release
\r
288 * Make an "environment metafile" to store at least time of day
\r
289 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
290 - Or maybe move content_features to material.{h,cpp}?
\r
291 * Add some kind of erosion and other stuff that now is possible
\r
292 * Make client to fetch stuff asynchronously
\r
293 - Needs method SyncProcessData
\r
294 * Fix the problem with the server constantly saving one or a few
\r
295 blocks? List the first saved block, maybe it explains.
\r
296 - It is probably caused by oscillating water
\r
297 * Water doesn't start flowing after map generation like it should
\r
298 - Are there still problems?
\r
299 * Better water generation (spread it to underwater caverns but don't
\r
300 fill dungeons that don't touch big water masses)
\r
301 * When generating a chunk and the neighboring chunk doesn't have mud
\r
302 and stuff yet and the ground is fairly flat, the mud will flow to
\r
303 the other chunk making nasty straight walls when the other chunk
\r
304 is generated. Fix it.
\r
305 * Make a small history check to transformLiquids to detect and log
\r
306 continuous oscillations, in such detail that they can be fixed.
\r
307 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
309 ======================================================================
\r
314 Setting this to 1 enables a special camera mode that forces
\r
315 the renderers to think that the camera statically points from
\r
316 the starting place to a static direction.
\r
318 This allows one to move around with the player and see what
\r
319 is actually drawn behind solid things and behind the player.
\r
321 #define FIELD_OF_VIEW_TEST 0
\r
325 #pragma message ("Disabling unit tests")
\r
327 #warning "Disabling unit tests"
\r
329 // Disable unit tests
\r
330 #define ENABLE_TESTS 0
\r
332 // Enable unit tests
\r
333 #define ENABLE_TESTS 1
\r
337 #pragma comment(lib, "Irrlicht.lib")
\r
338 //#pragma comment(lib, "jthread.lib")
\r
339 #pragma comment(lib, "zlibwapi.lib")
\r
340 #pragma comment(lib, "Shell32.lib")
\r
341 // This would get rid of the console window
\r
342 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
345 #include <iostream>
\r
347 #include <jmutexautolock.h>
\r
348 #include <locale.h>
\r
349 #include "common_irrlicht.h"
\r
352 #include "player.h"
\r
355 #include "environment.h"
\r
356 #include "server.h"
\r
357 #include "client.h"
\r
358 #include "serialization.h"
\r
359 #include "constants.h"
\r
360 #include "strfnd.h"
\r
361 #include "porting.h"
\r
362 #include "irrlichtwrapper.h"
\r
363 #include "gettime.h"
\r
364 #include "porting.h"
\r
365 #include "guiPauseMenu.h"
\r
366 #include "guiInventoryMenu.h"
\r
367 #include "guiTextInputMenu.h"
\r
368 #include "materials.h"
\r
369 #include "guiMessageMenu.h"
\r
370 #include "filesys.h"
\r
371 #include "config.h"
\r
372 #include "guiMainMenu.h"
\r
373 #include "mineral.h"
\r
377 // TODO: Remove this
\r
378 IrrlichtWrapper *g_irrlicht = NULL;
\r
380 // This makes textures
\r
381 ITextureSource *g_texturesource = NULL;
\r
383 MapDrawControl draw_control;
\r
387 These are loaded from the config file.
\r
390 Settings g_settings;
\r
392 extern void set_default_settings();
\r
398 IrrlichtDevice *g_device = NULL;
\r
399 Client *g_client = NULL;
\r
405 gui::IGUIEnvironment* guienv = NULL;
\r
406 gui::IGUIStaticText *guiroot = NULL;
\r
408 class MainMenuManager : public IMenuManager
\r
411 virtual void createdMenu(GUIModalMenu *menu)
\r
413 for(core::list<GUIModalMenu*>::Iterator
\r
414 i = m_stack.begin();
\r
415 i != m_stack.end(); i++)
\r
417 assert(*i != menu);
\r
420 if(m_stack.size() != 0)
\r
421 (*m_stack.getLast())->setVisible(false);
\r
422 m_stack.push_back(menu);
\r
425 virtual void deletingMenu(GUIModalMenu *menu)
\r
427 // Remove all entries if there are duplicates
\r
428 bool removed_entry;
\r
430 removed_entry = false;
\r
431 for(core::list<GUIModalMenu*>::Iterator
\r
432 i = m_stack.begin();
\r
433 i != m_stack.end(); i++)
\r
438 removed_entry = true;
\r
442 }while(removed_entry);
\r
444 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
445 assert(*i == menu);
\r
446 m_stack.erase(i);*/
\r
448 if(m_stack.size() != 0)
\r
449 (*m_stack.getLast())->setVisible(true);
\r
454 return m_stack.size();
\r
457 core::list<GUIModalMenu*> m_stack;
\r
460 MainMenuManager g_menumgr;
\r
462 bool noMenuActive()
\r
464 return (g_menumgr.menuCount() == 0);
\r
467 bool g_disconnect_requested = false;
\r
469 class MainGameCallback : public IGameCallback
\r
472 virtual void exitToOS()
\r
474 g_device->closeDevice();
\r
477 virtual void disconnect()
\r
479 g_disconnect_requested = true;
\r
483 MainGameCallback g_gamecallback;
\r
485 // Inventory actions from the menu are buffered here before sending
\r
486 Queue<InventoryAction*> inventory_action_queue;
\r
487 // This is a copy of the inventory that the client's environment has
\r
488 Inventory local_inventory;
\r
490 u16 g_selected_item = 0;
\r
497 std::ostream *dout_con_ptr = &dummyout;
\r
498 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
499 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
500 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
501 //std::ostream *dout_con_ptr = &dstream;
\r
502 //std::ostream *derr_con_ptr = &dstream;
\r
505 std::ostream *dout_server_ptr = &dstream;
\r
506 std::ostream *derr_server_ptr = &dstream;
\r
509 std::ostream *dout_client_ptr = &dstream;
\r
510 std::ostream *derr_client_ptr = &dstream;
\r
513 gettime.h implementation
\r
519 Use irrlicht because it is more precise than porting.h's
\r
522 if(g_irrlicht == NULL)
\r
524 return g_irrlicht->getTime();
\r
531 struct TextDestSign : public TextDest
\r
533 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
535 m_blockpos = blockpos;
\r
539 void gotText(std::wstring text)
\r
541 std::string ntext = wide_to_narrow(text);
\r
542 dstream<<"Changing text of a sign object: "
\r
543 <<ntext<<std::endl;
\r
544 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
552 struct TextDestChat : public TextDest
\r
554 TextDestChat(Client *client)
\r
558 void gotText(std::wstring text)
\r
560 m_client->sendChatMessage(text);
\r
561 m_client->addChatMessage(text);
\r
567 class MyEventReceiver : public IEventReceiver
\r
570 // This is the one method that we have to implement
\r
571 virtual bool OnEvent(const SEvent& event)
\r
574 React to nothing here if a menu is active
\r
576 if(noMenuActive() == false)
\r
582 // Remember whether each key is down or up
\r
583 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
585 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
587 if(event.KeyInput.PressedDown)
\r
589 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
595 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
597 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
599 dstream<<DTIME<<"MyEventReceiver: "
\r
600 <<"Launching pause menu"<<std::endl;
\r
601 // It will delete itself by itself
\r
602 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
603 &g_menumgr))->drop();
\r
606 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
608 dstream<<DTIME<<"MyEventReceiver: "
\r
609 <<"Launching inventory"<<std::endl;
\r
610 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
611 &local_inventory, &inventory_action_queue,
\r
612 &g_menumgr))->drop();
\r
615 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
617 TextDest *dest = new TextDestChat(g_client);
\r
619 (new GUITextInputMenu(guienv, guiroot, -1,
\r
625 // Material selection
\r
626 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
628 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
631 g_selected_item = 0;
\r
632 dstream<<DTIME<<"Selected item: "
\r
633 <<g_selected_item<<std::endl;
\r
636 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
637 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
639 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
640 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
642 if(s1 < PLAYER_INVENTORY_SIZE)
\r
643 g_selected_item = s1-1;
\r
644 dstream<<DTIME<<"Selected item: "
\r
645 <<g_selected_item<<std::endl;
\r
648 // Viewing range selection
\r
649 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
651 if(draw_control.range_all)
\r
653 draw_control.range_all = false;
\r
654 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
658 draw_control.range_all = true;
\r
659 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
663 // Print debug stacks
\r
664 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
666 dstream<<"-----------------------------------------"
\r
668 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
669 dstream<<"-----------------------------------------"
\r
671 debug_stacks_print();
\r
676 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
678 if(noMenuActive() == false)
\r
680 left_active = false;
\r
681 middle_active = false;
\r
682 right_active = false;
\r
686 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
687 left_active = event.MouseInput.isLeftPressed();
\r
688 middle_active = event.MouseInput.isMiddlePressed();
\r
689 right_active = event.MouseInput.isRightPressed();
\r
691 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
693 leftclicked = true;
\r
695 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
697 rightclicked = true;
\r
699 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
701 leftreleased = true;
\r
703 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
705 rightreleased = true;
\r
707 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
709 /*dstream<<"event.MouseInput.Wheel="
\r
710 <<event.MouseInput.Wheel<<std::endl;*/
\r
711 if(event.MouseInput.Wheel < 0)
\r
713 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
716 g_selected_item = 0;
\r
718 else if(event.MouseInput.Wheel > 0)
\r
720 if(g_selected_item > 0)
\r
723 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
732 // This is used to check whether a key is being held down
\r
733 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
735 return keyIsDown[keyCode];
\r
740 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
741 keyIsDown[i] = false;
\r
743 leftclicked = false;
\r
744 rightclicked = false;
\r
745 leftreleased = false;
\r
746 rightreleased = false;
\r
748 left_active = false;
\r
749 middle_active = false;
\r
750 right_active = false;
\r
761 bool rightreleased;
\r
764 bool middle_active;
\r
768 // We use this array to store the current state of each key
\r
769 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
772 IrrlichtDevice *m_device;
\r
781 virtual ~InputHandler()
\r
785 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
787 virtual v2s32 getMousePos() = 0;
\r
788 virtual void setMousePos(s32 x, s32 y) = 0;
\r
790 virtual bool getLeftState() = 0;
\r
791 virtual bool getRightState() = 0;
\r
793 virtual bool getLeftClicked() = 0;
\r
794 virtual bool getRightClicked() = 0;
\r
795 virtual void resetLeftClicked() = 0;
\r
796 virtual void resetRightClicked() = 0;
\r
798 virtual bool getLeftReleased() = 0;
\r
799 virtual bool getRightReleased() = 0;
\r
800 virtual void resetLeftReleased() = 0;
\r
801 virtual void resetRightReleased() = 0;
\r
803 virtual void step(float dtime) {};
\r
805 virtual void clear() {};
\r
808 InputHandler *g_input = NULL;
\r
810 class RealInputHandler : public InputHandler
\r
813 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
815 m_receiver(receiver)
\r
818 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
820 return m_receiver->IsKeyDown(keyCode);
\r
822 virtual v2s32 getMousePos()
\r
824 return m_device->getCursorControl()->getPosition();
\r
826 virtual void setMousePos(s32 x, s32 y)
\r
828 m_device->getCursorControl()->setPosition(x, y);
\r
831 virtual bool getLeftState()
\r
833 return m_receiver->left_active;
\r
835 virtual bool getRightState()
\r
837 return m_receiver->right_active;
\r
840 virtual bool getLeftClicked()
\r
842 return m_receiver->leftclicked;
\r
844 virtual bool getRightClicked()
\r
846 return m_receiver->rightclicked;
\r
848 virtual void resetLeftClicked()
\r
850 m_receiver->leftclicked = false;
\r
852 virtual void resetRightClicked()
\r
854 m_receiver->rightclicked = false;
\r
857 virtual bool getLeftReleased()
\r
859 return m_receiver->leftreleased;
\r
861 virtual bool getRightReleased()
\r
863 return m_receiver->rightreleased;
\r
865 virtual void resetLeftReleased()
\r
867 m_receiver->leftreleased = false;
\r
869 virtual void resetRightReleased()
\r
871 m_receiver->rightreleased = false;
\r
876 resetRightClicked();
\r
877 resetLeftClicked();
\r
880 IrrlichtDevice *m_device;
\r
881 MyEventReceiver *m_receiver;
\r
884 class RandomInputHandler : public InputHandler
\r
887 RandomInputHandler()
\r
889 leftclicked = false;
\r
890 rightclicked = false;
\r
891 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
892 keydown[i] = false;
\r
894 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
896 return keydown[keyCode];
\r
898 virtual v2s32 getMousePos()
\r
902 virtual void setMousePos(s32 x, s32 y)
\r
904 mousepos = v2s32(x,y);
\r
907 virtual bool getLeftState()
\r
911 virtual bool getRightState()
\r
916 virtual bool getLeftClicked()
\r
918 return leftclicked;
\r
920 virtual bool getRightClicked()
\r
922 return rightclicked;
\r
924 virtual void resetLeftClicked()
\r
926 leftclicked = false;
\r
928 virtual void resetRightClicked()
\r
930 rightclicked = false;
\r
933 virtual bool getLeftReleased()
\r
937 virtual bool getRightReleased()
\r
941 virtual void resetLeftReleased()
\r
944 virtual void resetRightReleased()
\r
948 virtual void step(float dtime)
\r
951 static float counter1 = 0;
\r
955 counter1 = 0.1*Rand(1,10);
\r
956 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
957 g_selected_material++;
\r
959 g_selected_material = 0;*/
\r
960 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
963 g_selected_item = 0;
\r
967 static float counter1 = 0;
\r
971 counter1 = 0.1*Rand(1, 40);
\r
972 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
976 static float counter1 = 0;
\r
980 counter1 = 0.1*Rand(1, 40);
\r
981 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
985 static float counter1 = 0;
\r
989 counter1 = 0.1*Rand(1, 40);
\r
990 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
994 static float counter1 = 0;
\r
998 counter1 = 0.1*Rand(1, 40);
\r
999 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1003 static float counter1 = 0;
\r
1004 counter1 -= dtime;
\r
1005 if(counter1 < 0.0)
\r
1007 counter1 = 0.1*Rand(1, 20);
\r
1008 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1012 static float counter1 = 0;
\r
1013 counter1 -= dtime;
\r
1014 if(counter1 < 0.0)
\r
1016 counter1 = 0.1*Rand(1, 30);
\r
1017 leftclicked = true;
\r
1021 static float counter1 = 0;
\r
1022 counter1 -= dtime;
\r
1023 if(counter1 < 0.0)
\r
1025 counter1 = 0.1*Rand(1, 20);
\r
1026 rightclicked = true;
\r
1029 mousepos += mousespeed;
\r
1032 s32 Rand(s32 min, s32 max)
\r
1034 return (myrand()%(max-min+1))+min;
\r
1037 bool keydown[KEY_KEY_CODES_COUNT];
\r
1041 bool rightclicked;
\r
1044 void updateViewingRange(f32 frametime_in, Client *client)
\r
1046 if(draw_control.range_all == true)
\r
1049 static f32 added_frametime = 0;
\r
1050 static s16 added_frames = 0;
\r
1052 added_frametime += frametime_in;
\r
1053 added_frames += 1;
\r
1055 // Actually this counter kind of sucks because frametime is busytime
\r
1056 static f32 counter = 0;
\r
1057 counter -= frametime_in;
\r
1063 /*dstream<<__FUNCTION_NAME
\r
1064 <<": Collected "<<added_frames<<" frames, total of "
\r
1065 <<added_frametime<<"s."<<std::endl;*/
\r
1067 /*dstream<<"draw_control.blocks_drawn="
\r
1068 <<draw_control.blocks_drawn
\r
1069 <<", draw_control.blocks_would_have_drawn="
\r
1070 <<draw_control.blocks_would_have_drawn
\r
1073 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1074 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1076 draw_control.wanted_min_range = range_min;
\r
1077 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1079 float block_draw_ratio = 1.0;
\r
1080 if(draw_control.blocks_would_have_drawn != 0)
\r
1082 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1083 / (float)draw_control.blocks_would_have_drawn;
\r
1086 // Calculate the average frametime in the case that all wanted
\r
1087 // blocks had been drawn
\r
1088 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1090 added_frametime = 0.0;
\r
1093 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1094 float wanted_frametime = 1.0 / wanted_fps;
\r
1096 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1097 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1099 // If needed frametime change is very small, just return
\r
1100 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1102 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1106 float range = draw_control.wanted_range;
\r
1107 float new_range = range;
\r
1109 static s16 range_old = 0;
\r
1110 static f32 frametime_old = 0;
\r
1112 float d_range = range - range_old;
\r
1113 f32 d_frametime = frametime - frametime_old;
\r
1114 // A sane default of 30ms per 50 nodes of range
\r
1115 static f32 time_per_range = 30. / 50;
\r
1118 time_per_range = d_frametime / d_range;
\r
1121 // The minimum allowed calculated frametime-range derivative:
\r
1122 // Practically this sets the maximum speed of changing the range.
\r
1123 // The lower this value, the higher the maximum changing speed.
\r
1124 // A low value here results in wobbly range (0.001)
\r
1125 // A high value here results in slow changing range (0.0025)
\r
1126 // SUGG: This could be dynamically adjusted so that when
\r
1127 // the camera is turning, this is lower
\r
1128 //float min_time_per_range = 0.0015;
\r
1129 float min_time_per_range = 0.0010;
\r
1130 //float min_time_per_range = 0.05 / range;
\r
1131 if(time_per_range < min_time_per_range)
\r
1133 time_per_range = min_time_per_range;
\r
1134 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1138 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1141 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1142 // Dampen the change a bit to kill oscillations
\r
1143 //wanted_range_change *= 0.9;
\r
1144 //wanted_range_change *= 0.75;
\r
1145 wanted_range_change *= 0.5;
\r
1146 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1148 // If needed range change is very small, just return
\r
1149 if(fabs(wanted_range_change) < 0.001)
\r
1151 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1155 new_range += wanted_range_change;
\r
1156 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1158 //float new_range_unclamped = new_range;
\r
1159 if(new_range < range_min)
\r
1160 new_range = range_min;
\r
1161 if(new_range > range_max)
\r
1162 new_range = range_max;
\r
1164 /*if(new_range != new_range_unclamped)
\r
1165 dstream<<", clamped to "<<new_range<<std::endl;
\r
1167 dstream<<std::endl;*/
\r
1169 draw_control.wanted_range = new_range;
\r
1171 range_old = new_range;
\r
1172 frametime_old = frametime;
\r
1175 class GUIQuickInventory : public IEventReceiver
\r
1178 GUIQuickInventory(
\r
1179 gui::IGUIEnvironment* env,
\r
1180 gui::IGUIElement* parent,
\r
1183 Inventory *inventory):
\r
1184 m_itemcount(itemcount),
\r
1185 m_inventory(inventory)
\r
1187 core::rect<s32> imgsize(0,0,48,48);
\r
1188 core::rect<s32> textsize(0,0,48,16);
\r
1189 v2s32 spacing(0, 64);
\r
1190 for(s32 i=0; i<m_itemcount; i++)
\r
1192 m_images.push_back(env->addImage(
\r
1193 imgsize + pos + spacing*i
\r
1195 m_images[i]->setScaleImage(true);
\r
1196 m_texts.push_back(env->addStaticText(
\r
1198 textsize + pos + spacing*i,
\r
1201 m_texts[i]->setBackgroundColor(
\r
1202 video::SColor(128,0,0,0));
\r
1203 m_texts[i]->setTextAlignment(
\r
1204 gui::EGUIA_CENTER,
\r
1205 gui::EGUIA_UPPERLEFT);
\r
1209 ~GUIQuickInventory()
\r
1211 for(u32 i=0; i<m_texts.size(); i++)
\r
1213 m_texts[i]->remove();
\r
1215 for(u32 i=0; i<m_images.size(); i++)
\r
1217 m_images[i]->remove();
\r
1221 virtual bool OnEvent(const SEvent& event)
\r
1226 void setSelection(s32 i)
\r
1235 start = m_selection - m_itemcount / 2;
\r
1237 InventoryList *mainlist = m_inventory->getList("main");
\r
1239 for(s32 i=0; i<m_itemcount; i++)
\r
1241 s32 j = i + start;
\r
1243 if(j > (s32)mainlist->getSize() - 1)
\r
1244 j -= mainlist->getSize();
\r
1246 j += mainlist->getSize();
\r
1248 InventoryItem *item = mainlist->getItem(j);
\r
1252 m_images[i]->setImage(NULL);
\r
1254 if(m_selection == j)
\r
1255 m_texts[i]->setText(L"<-");
\r
1257 m_texts[i]->setText(L"");
\r
1259 // The next ifs will segfault with a NULL pointer
\r
1264 m_images[i]->setImage(item->getImage());
\r
1266 std::ostringstream os;
\r
1267 os<<item->getText();
\r
1268 if(m_selection == j)
\r
1270 m_texts[i]->setText(narrow_to_wide(os.str()).c_str());
\r
1273 if(m_selection == j)
\r
1274 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1276 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1277 m_texts[i]->setText(t);*/
\r
1283 core::array<gui::IGUIStaticText*> m_texts;
\r
1284 core::array<gui::IGUIImage*> m_images;
\r
1285 Inventory *m_inventory;
\r
1296 ChatLine(const std::wstring &a_text):
\r
1302 std::wstring text;
\r
1305 // These are defined global so that they're not optimized too much.
\r
1306 // Can't change them to volatile.
\r
1311 std::string tempstring;
\r
1312 std::string tempstring2;
\r
1317 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1318 TimeTaker timer("Testing std::string speed");
\r
1319 const u32 jj = 10000;
\r
1320 for(u32 j=0; j<jj; j++)
\r
1324 const u32 ii = 10;
\r
1325 for(u32 i=0; i<ii; i++){
\r
1326 tempstring2 += "asd";
\r
1328 for(u32 i=0; i<ii+1; i++){
\r
1329 tempstring += "asd";
\r
1330 if(tempstring == tempstring2)
\r
1336 dstream<<"All of the following tests should take around 100ms each."
\r
1340 TimeTaker timer("Testing floating-point conversion speed");
\r
1342 for(u32 i=0; i<4000000; i++){
\r
1349 TimeTaker timer("Testing floating-point vector speed");
\r
1351 tempv3f1 = v3f(1,2,3);
\r
1352 tempv3f2 = v3f(4,5,6);
\r
1353 for(u32 i=0; i<10000000; i++){
\r
1354 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1355 tempv3f2 += v3f(7,8,9);
\r
1360 TimeTaker timer("Testing core::map speed");
\r
1362 core::map<v2s16, f32> map1;
\r
1365 for(s16 y=0; y<ii; y++){
\r
1366 for(s16 x=0; x<ii; x++){
\r
1367 map1.insert(v2s16(x,y), tempf);
\r
1371 for(s16 y=ii-1; y>=0; y--){
\r
1372 for(s16 x=0; x<ii; x++){
\r
1373 tempf = map1[v2s16(x,y)];
\r
1379 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1380 TimeTaker timer("Testing mutex speed");
\r
1393 // Do at least 10ms
\r
1394 while(timer.getTime() < 10);
\r
1396 u32 dtime = timer.stop();
\r
1397 u32 per_ms = n / dtime;
\r
1398 std::cout<<"Done. "<<dtime<<"ms, "
\r
1399 <<per_ms<<"/ms"<<std::endl;
\r
1403 int main(int argc, char *argv[])
\r
1406 Parse command line
\r
1409 // List all allowed options
\r
1410 core::map<std::string, ValueSpec> allowed_options;
\r
1411 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1412 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1413 "Run server directly"));
\r
1414 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1415 "Load configuration from specified file"));
\r
1416 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1417 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1418 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1419 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1420 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1421 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1423 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1425 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1427 Settings cmd_args;
\r
1429 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1431 if(ret == false || cmd_args.getFlag("help"))
\r
1433 dstream<<"Allowed options:"<<std::endl;
\r
1434 for(core::map<std::string, ValueSpec>::Iterator
\r
1435 i = allowed_options.getIterator();
\r
1436 i.atEnd() == false; i++)
\r
1438 dstream<<" --"<<i.getNode()->getKey();
\r
1439 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1444 dstream<<" <value>";
\r
1446 dstream<<std::endl;
\r
1448 if(i.getNode()->getValue().help != NULL)
\r
1450 dstream<<" "<<i.getNode()->getValue().help
\r
1455 return cmd_args.getFlag("help") ? 0 : 1;
\r
1459 Low-level initialization
\r
1462 bool disable_stderr = false;
\r
1464 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1465 disable_stderr = true;
\r
1468 // Initialize debug streams
\r
1469 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1470 // Initialize debug stacks
\r
1471 debug_stacks_init();
\r
1473 DSTACK(__FUNCTION_NAME);
\r
1475 porting::initializePaths();
\r
1476 // Create user data directory
\r
1477 fs::CreateDir(porting::path_userdata);
\r
1479 // C-style stuff initialization
\r
1480 initializeMaterialProperties();
\r
1483 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1485 // Print startup message
\r
1486 dstream<<DTIME<<"minetest-c55"
\r
1487 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1488 <<", "<<BUILD_INFO
\r
1492 Basic initialization
\r
1495 // Initialize default settings
\r
1496 set_default_settings();
\r
1498 // Set locale. This is for forcing '.' as the decimal point.
\r
1499 std::locale::global(std::locale("C"));
\r
1500 // This enables printing all characters in bitmap font
\r
1501 setlocale(LC_CTYPE, "en_US");
\r
1503 // Initialize sockets
\r
1505 atexit(sockets_cleanup);
\r
1515 // Path of configuration file in use
\r
1516 std::string configpath = "";
\r
1518 if(cmd_args.exists("config"))
\r
1520 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1523 dstream<<"Could not read configuration from \""
\r
1524 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1527 configpath = cmd_args.get("config");
\r
1531 core::array<std::string> filenames;
\r
1532 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1533 #ifdef RUN_IN_PLACE
\r
1534 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1537 for(u32 i=0; i<filenames.size(); i++)
\r
1539 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1542 configpath = filenames[i];
\r
1547 // If no path found, use the first one (menu creates the file)
\r
1548 if(configpath == "")
\r
1549 configpath = filenames[0];
\r
1552 // Initialize random seed
\r
1557 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1559 These are needed for unit tests at least.
\r
1562 // Initial call with g_texturesource not set.
\r
1569 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1570 || cmd_args.getFlag("enable-unittests") == true)
\r
1575 /*for(s16 y=-100; y<100; y++)
\r
1576 for(s16 x=-100; x<100; x++)
\r
1578 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1588 if(cmd_args.exists("port"))
\r
1589 port = cmd_args.getU16("port");
\r
1590 else if(cmd_args.exists("port"))
\r
1591 port = g_settings.getU16("port");
\r
1594 std::string map_dir = porting::path_userdata+"/map";
\r
1595 if(cmd_args.exists("map-dir"))
\r
1596 map_dir = cmd_args.get("map-dir");
\r
1597 else if(g_settings.exists("map-dir"))
\r
1598 map_dir = g_settings.get("map-dir");
\r
1600 // Run dedicated server if asked to
\r
1601 if(cmd_args.getFlag("server"))
\r
1603 DSTACK("Dedicated server branch");
\r
1606 Server server(map_dir.c_str());
\r
1607 server.start(port);
\r
1610 dedicated_server_loop(server);
\r
1619 // Address to connect to
\r
1620 std::string address = "";
\r
1622 if(cmd_args.exists("address"))
\r
1624 address = cmd_args.get("address");
\r
1628 address = g_settings.get("address");
\r
1631 std::string playername = g_settings.get("name");
\r
1634 Resolution selection
\r
1637 bool fullscreen = false;
\r
1638 u16 screenW = g_settings.getU16("screenW");
\r
1639 u16 screenH = g_settings.getU16("screenH");
\r
1643 MyEventReceiver receiver;
\r
1645 video::E_DRIVER_TYPE driverType;
\r
1648 //driverType = video::EDT_DIRECT3D9;
\r
1649 driverType = video::EDT_OPENGL;
\r
1651 driverType = video::EDT_OPENGL;
\r
1652 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1655 // create device and exit if creation failed
\r
1657 IrrlichtDevice *device;
\r
1658 device = createDevice(driverType,
\r
1659 core::dimension2d<u32>(screenW, screenH),
\r
1660 16, fullscreen, false, false, &receiver);
\r
1663 return 1; // could not create selected driver.
\r
1665 g_device = device;
\r
1666 g_irrlicht = new IrrlichtWrapper(device);
\r
1667 TextureSource *texturesource = new TextureSource(device);
\r
1668 g_texturesource = texturesource;
\r
1671 Speed tests (done after irrlicht is loaded to get timer)
\r
1673 if(cmd_args.getFlag("speedtests"))
\r
1675 dstream<<"Running speed tests"<<std::endl;
\r
1680 device->setResizable(true);
\r
1682 bool random_input = g_settings.getBool("random_input")
\r
1683 || cmd_args.getFlag("random-input");
\r
1685 g_input = new RandomInputHandler();
\r
1687 g_input = new RealInputHandler(device, &receiver);
\r
1690 Continue initialization
\r
1693 video::IVideoDriver* driver = device->getVideoDriver();
\r
1696 This changes the minimum allowed number of vertices in a VBO.
\r
1699 //driver->setMinHardwareBufferVertexCount(50);
\r
1701 scene::ISceneManager* smgr = device->getSceneManager();
\r
1703 guienv = device->getGUIEnvironment();
\r
1704 gui::IGUISkin* skin = guienv->getSkin();
\r
1705 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1707 skin->setFont(font);
\r
1709 dstream<<"WARNING: Font file was not found."
\r
1710 " Using default font."<<std::endl;
\r
1711 // If font was not found, this will get us one
\r
1712 font = skin->getFont();
\r
1715 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1716 dstream<<"text_height="<<text_height<<std::endl;
\r
1718 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1719 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1720 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1721 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1722 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1723 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1726 Preload some textures and stuff
\r
1729 init_content_inventory_texture_paths();
\r
1730 init_mapnode(); // Second call with g_texturesource set
\r
1738 We need some kind of a root node to be able to add
\r
1739 custom gui elements directly on the screen.
\r
1740 Otherwise they won't be automatically drawn.
\r
1742 guiroot = guienv->addStaticText(L"",
\r
1743 core::rect<s32>(0, 0, 10000, 10000));
\r
1745 // First line of debug text
\r
1746 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1748 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1750 // Second line of debug text
\r
1751 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1753 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1756 // At the middle of the screen
\r
1757 // Object infos are shown in this
\r
1758 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1760 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1764 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1766 core::rect<s32>(0,0,0,0),
\r
1767 false, false); // Disable word wrap as of now
\r
1769 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1770 core::list<ChatLine> chat_lines;
\r
1773 If an error occurs, this is set to something and the
\r
1774 menu-game loop is restarted. It is then displayed before
\r
1777 std::wstring error_message = L"";
\r
1782 while(g_device->run())
\r
1785 // This is used for catching disconnects
\r
1790 Out-of-game menu loop.
\r
1792 Loop quits when menu returns proper parameters.
\r
1796 // Cursor can be non-visible when coming from the game
\r
1797 device->getCursorControl()->setVisible(true);
\r
1798 // Some stuff are left to scene manager when coming from the game
\r
1799 // (map at least?)
\r
1801 // Reset or hide the debug gui texts
\r
1802 guitext->setText(L"Minetest-c55");
\r
1803 guitext2->setVisible(false);
\r
1804 guitext_info->setVisible(false);
\r
1805 guitext_chat->setVisible(false);
\r
1807 // Initialize menu data
\r
1808 MainMenuData menudata;
\r
1809 menudata.address = narrow_to_wide(address);
\r
1810 menudata.name = narrow_to_wide(playername);
\r
1811 menudata.port = narrow_to_wide(itos(port));
\r
1812 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1814 GUIMainMenu *menu =
\r
1815 new GUIMainMenu(guienv, guiroot, -1,
\r
1816 &g_menumgr, &menudata, &g_gamecallback);
\r
1817 menu->allowFocusRemoval(true);
\r
1819 if(error_message != L"")
\r
1821 GUIMessageMenu *menu2 =
\r
1822 new GUIMessageMenu(guienv, guiroot, -1,
\r
1823 &g_menumgr, error_message.c_str());
\r
1825 error_message = L"";
\r
1828 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1830 dstream<<"Created main menu"<<std::endl;
\r
1832 while(g_device->run())
\r
1834 // Run global IrrlichtWrapper's main thread processing stuff
\r
1835 g_irrlicht->Run();
\r
1837 if(menu->getStatus() == true)
\r
1840 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1841 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1842 guienv->drawAll();
\r
1843 driver->endScene();
\r
1846 // Break out of menu-game loop to shut down cleanly
\r
1847 if(g_device->run() == false)
\r
1850 dstream<<"Dropping main menu"<<std::endl;
\r
1854 // Delete map if requested
\r
1855 if(menudata.delete_map)
\r
1857 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1859 error_message = L"Delete failed";
\r
1863 playername = wide_to_narrow(menudata.name);
\r
1864 address = wide_to_narrow(menudata.address);
\r
1865 port = stoi(wide_to_narrow(menudata.port));
\r
1866 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1868 // Check for valid parameters, restart menu if invalid.
\r
1869 if(playername == "")
\r
1871 error_message = L"Name required.";
\r
1876 g_settings.set("name", playername);
\r
1877 g_settings.set("address", address);
\r
1878 g_settings.set("port", itos(port));
\r
1879 // Update configuration file
\r
1880 if(configpath != "")
\r
1881 g_settings.updateConfigFile(configpath.c_str());
\r
1883 // Continue to game
\r
1887 // Break out of menu-game loop to shut down cleanly
\r
1888 if(g_device->run() == false)
\r
1892 Make a scope here so that the client and the server and other
\r
1893 stuff gets removed when disconnected or the irrlicht device
\r
1898 // This is set to true at the end of the scope
\r
1899 g_irrlicht->Shutdown(false);
\r
1902 Draw "Loading" screen
\r
1904 const wchar_t *text = L"Loading and connecting...";
\r
1905 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1906 core::vector2d<s32> textsize(300, text_height);
\r
1907 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1909 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1910 text, textrect, false, false);
\r
1911 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1913 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1914 guienv->drawAll();
\r
1915 driver->endScene();
\r
1917 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1921 SharedPtr will delete it when it goes out of scope.
\r
1923 SharedPtr<Server> server;
\r
1924 if(address == ""){
\r
1925 server = new Server(map_dir);
\r
1926 server->start(port);
\r
1933 Client client(device, playername.c_str(), draw_control);
\r
1935 g_client = &client;
\r
1937 Address connect_address(0,0,0,0, port);
\r
1940 //connect_address.Resolve("localhost");
\r
1941 connect_address.setAddress(127,0,0,1);
\r
1943 connect_address.Resolve(address.c_str());
\r
1945 catch(ResolveError &e)
\r
1947 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1949 error_message = L"Couldn't resolve address";
\r
1950 gui_loadingtext->remove();
\r
1954 dstream<<DTIME<<"Connecting to server at ";
\r
1955 connect_address.print(&dstream);
\r
1956 dstream<<std::endl;
\r
1957 client.connect(connect_address);
\r
1960 while(client.connectedAndInitialized() == false)
\r
1963 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1964 guienv->drawAll();
\r
1965 driver->endScene();
\r
1967 // Update client and server
\r
1971 if(server != NULL)
\r
1972 server->step(0.1);
\r
1978 catch(con::PeerNotFoundException &e)
\r
1980 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1982 error_message = L"Connection timed out.";
\r
1983 gui_loadingtext->remove();
\r
1990 /*scene::ISceneNode* skybox;
\r
1991 skybox = smgr->addSkyBoxSceneNode(
\r
1992 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1993 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1994 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1995 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1996 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1997 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2000 Create the camera node
\r
2003 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2004 0, // Camera parent
\r
2005 v3f(BS*100, BS*2, BS*100), // Look from
\r
2006 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2010 if(camera == NULL)
\r
2013 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2014 video::SColor skycolor = video::SColor(255,166,202,244);
\r
2016 camera->setFOV(FOV_ANGLE);
\r
2018 // Just so big a value that everything rendered is visible
\r
2019 camera->setFarValue(100000*BS);
\r
2021 f32 camera_yaw = 0; // "right/left"
\r
2022 f32 camera_pitch = 0; // "up/down"
\r
2028 gui_loadingtext->remove();
\r
2031 Add some gui stuff
\r
2034 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2035 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2037 // Test the text input system
\r
2038 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2040 /*GUIMessageMenu *menu =
\r
2041 new GUIMessageMenu(guienv, guiroot, -1,
\r
2046 // Launch pause menu
\r
2047 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2048 &g_menumgr))->drop();
\r
2051 guitext2->setVisible(true);
\r
2052 guitext_info->setVisible(true);
\r
2053 guitext_chat->setVisible(true);
\r
2056 Some statistics are collected in these
\r
2059 u32 beginscenetime = 0;
\r
2060 u32 scenetime = 0;
\r
2061 u32 endscenetime = 0;
\r
2064 //throw con::PeerNotFoundException("lol");
\r
2066 core::list<float> frametime_log;
\r
2072 bool first_loop_after_window_activation = true;
\r
2074 // Time is in milliseconds
\r
2075 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2076 // NOTE: So we have to use getTime() and call run()s between them
\r
2077 u32 lasttime = device->getTimer()->getTime();
\r
2079 while(device->run())
\r
2081 if(g_disconnect_requested)
\r
2083 g_disconnect_requested = false;
\r
2088 Run global IrrlichtWrapper's main thread processing stuff
\r
2090 g_irrlicht->Run();
\r
2093 Process TextureSource's queue
\r
2095 texturesource->processQueue();
\r
2098 Random calculations
\r
2100 v2u32 screensize = driver->getScreenSize();
\r
2101 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2103 // Hilight boxes collected during the loop and displayed
\r
2104 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2107 std::wstring infotext;
\r
2109 //TimeTaker //timer1("//timer1");
\r
2111 // Time of frame without fps limit
\r
2115 // not using getRealTime is necessary for wine
\r
2116 u32 time = device->getTimer()->getTime();
\r
2117 if(time > lasttime)
\r
2118 busytime_u32 = time - lasttime;
\r
2121 busytime = busytime_u32 / 1000.0;
\r
2124 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2126 // Absolutelu necessary for wine!
\r
2133 updateViewingRange(busytime, &client);
\r
2140 float fps_max = g_settings.getFloat("fps_max");
\r
2141 u32 frametime_min = 1000./fps_max;
\r
2143 if(busytime_u32 < frametime_min)
\r
2145 u32 sleeptime = frametime_min - busytime_u32;
\r
2146 device->sleep(sleeptime);
\r
2150 // Absolutelu necessary for wine!
\r
2154 Time difference calculation
\r
2156 f32 dtime; // in seconds
\r
2158 u32 time = device->getTimer()->getTime();
\r
2159 if(time > lasttime)
\r
2160 dtime = (time - lasttime) / 1000.0;
\r
2166 Log frametime for visualization
\r
2168 frametime_log.push_back(dtime);
\r
2169 if(frametime_log.size() > 100)
\r
2171 core::list<float>::Iterator i = frametime_log.begin();
\r
2172 frametime_log.erase(i);
\r
2176 Visualize frametime in terminal
\r
2178 /*for(u32 i=0; i<dtime*400; i++)
\r
2180 std::cout<<std::endl;*/
\r
2183 Time average and jitter calculation
\r
2186 static f32 dtime_avg1 = 0.0;
\r
2187 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2188 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2190 static f32 dtime_jitter1_max_sample = 0.0;
\r
2191 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2193 static f32 jitter1_max = 0.0;
\r
2194 static f32 counter = 0.0;
\r
2195 if(dtime_jitter1 > jitter1_max)
\r
2196 jitter1_max = dtime_jitter1;
\r
2201 dtime_jitter1_max_sample = jitter1_max;
\r
2202 dtime_jitter1_max_fraction
\r
2203 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2204 jitter1_max = 0.0;
\r
2209 Busytime average and jitter calculation
\r
2212 static f32 busytime_avg1 = 0.0;
\r
2213 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2214 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2216 static f32 busytime_jitter1_max_sample = 0.0;
\r
2217 static f32 busytime_jitter1_min_sample = 0.0;
\r
2219 static f32 jitter1_max = 0.0;
\r
2220 static f32 jitter1_min = 0.0;
\r
2221 static f32 counter = 0.0;
\r
2222 if(busytime_jitter1 > jitter1_max)
\r
2223 jitter1_max = busytime_jitter1;
\r
2224 if(busytime_jitter1 < jitter1_min)
\r
2225 jitter1_min = busytime_jitter1;
\r
2227 if(counter > 0.0){
\r
2229 busytime_jitter1_max_sample = jitter1_max;
\r
2230 busytime_jitter1_min_sample = jitter1_min;
\r
2231 jitter1_max = 0.0;
\r
2232 jitter1_min = 0.0;
\r
2237 Debug info for client
\r
2240 static float counter = 0.0;
\r
2245 client.printDebugInfo(std::cout);
\r
2250 Input handler step()
\r
2252 g_input->step(dtime);
\r
2255 Player speed control
\r
2264 bool a_superspeed,
\r
2268 PlayerControl control(
\r
2269 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2270 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2271 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2272 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2273 g_input->isKeyDown(irr::KEY_SPACE),
\r
2274 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2275 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2276 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2280 client.setPlayerControl(control);
\r
2284 Process environment
\r
2288 //TimeTaker timer("client.step(dtime)");
\r
2289 client.step(dtime);
\r
2290 //client.step(dtime_avg1);
\r
2293 if(server != NULL)
\r
2295 //TimeTaker timer("server->step(dtime)");
\r
2296 server->step(dtime);
\r
2299 v3f player_position = client.getPlayerPosition();
\r
2301 //TimeTaker //timer2("//timer2");
\r
2304 Mouse and camera control
\r
2307 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2310 device->getCursorControl()->setVisible(false);
\r
2312 if(first_loop_after_window_activation){
\r
2313 //std::cout<<"window active, first loop"<<std::endl;
\r
2314 first_loop_after_window_activation = false;
\r
2317 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2318 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2319 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2320 camera_yaw -= dx*0.2;
\r
2321 camera_pitch += dy*0.2;
\r
2322 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2323 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2325 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2328 device->getCursorControl()->setVisible(true);
\r
2330 //std::cout<<"window inactive"<<std::endl;
\r
2331 first_loop_after_window_activation = true;
\r
2334 camera_yaw = wrapDegrees(camera_yaw);
\r
2335 camera_pitch = wrapDegrees(camera_pitch);
\r
2337 v3f camera_direction = v3f(0,0,1);
\r
2338 camera_direction.rotateYZBy(camera_pitch);
\r
2339 camera_direction.rotateXZBy(camera_yaw);
\r
2341 // This is at the height of the eyes of the current figure
\r
2342 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2343 // This is more like in minecraft
\r
2344 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2346 camera->setPosition(camera_position);
\r
2347 // *100.0 helps in large map coordinates
\r
2348 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2350 if(FIELD_OF_VIEW_TEST){
\r
2351 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2352 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2355 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2356 //TimeTaker timer("client.updateCamera");
\r
2357 client.updateCamera(camera_position, camera_direction);
\r
2361 //TimeTaker //timer3("//timer3");
\r
2364 Calculate what block is the crosshair pointing to
\r
2367 //u32 t1 = device->getTimer()->getRealTime();
\r
2369 //f32 d = 4; // max. distance
\r
2370 f32 d = 4; // max. distance
\r
2371 core::line3d<f32> shootline(camera_position,
\r
2372 camera_position + camera_direction * BS * (d+1));
\r
2374 MapBlockObject *selected_object = client.getSelectedObject
\r
2375 (d*BS, camera_position, shootline);
\r
2378 If it's pointing to a MapBlockObject
\r
2381 if(selected_object != NULL)
\r
2383 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2385 core::aabbox3d<f32> box_on_map
\r
2386 = selected_object->getSelectionBoxOnMap();
\r
2388 hilightboxes.push_back(box_on_map);
\r
2390 infotext = narrow_to_wide(selected_object->infoText());
\r
2392 if(g_input->getLeftClicked())
\r
2394 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2395 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2396 selected_object->getId(), g_selected_item);
\r
2398 else if(g_input->getRightClicked())
\r
2400 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2402 Check if we want to modify the object ourselves
\r
2404 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2406 dstream<<"Sign object right-clicked"<<std::endl;
\r
2408 if(random_input == false)
\r
2410 // Get a new text for it
\r
2412 TextDest *dest = new TextDestSign(
\r
2413 selected_object->getBlock()->getPos(),
\r
2414 selected_object->getId(),
\r
2417 SignObject *sign_object = (SignObject*)selected_object;
\r
2419 std::wstring wtext =
\r
2420 narrow_to_wide(sign_object->getText());
\r
2422 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2428 Otherwise pass the event to the server as-is
\r
2432 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2433 selected_object->getId(), g_selected_item);
\r
2437 else // selected_object == NULL
\r
2441 Find out which node we are pointing at
\r
2444 bool nodefound = false;
\r
2446 v3s16 neighbourpos;
\r
2447 core::aabbox3d<f32> nodehilightbox;
\r
2448 f32 mindistance = BS * 1001;
\r
2450 v3s16 pos_i = floatToInt(player_position);
\r
2452 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2456 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2457 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2458 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2459 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2460 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2461 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2463 for(s16 y = ystart; y <= yend; y++)
\r
2464 for(s16 z = zstart; z <= zend; z++)
\r
2465 for(s16 x = xstart; x <= xend; x++)
\r
2470 n = client.getNode(v3s16(x,y,z));
\r
2471 if(content_pointable(n.d) == false)
\r
2474 catch(InvalidPositionException &e)
\r
2480 v3f npf = intToFloat(np);
\r
2485 v3s16(0,0,1), // back
\r
2486 v3s16(0,1,0), // top
\r
2487 v3s16(1,0,0), // right
\r
2488 v3s16(0,0,-1), // front
\r
2489 v3s16(0,-1,0), // bottom
\r
2490 v3s16(-1,0,0), // left
\r
2496 if(n.d == CONTENT_TORCH)
\r
2498 v3s16 dir = unpackDir(n.dir);
\r
2499 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2500 dir_f *= BS/2 - BS/6 - BS/20;
\r
2501 v3f cpf = npf + dir_f;
\r
2502 f32 distance = (cpf - camera_position).getLength();
\r
2504 core::aabbox3d<f32> box;
\r
2507 if(dir == v3s16(0,-1,0))
\r
2509 box = core::aabbox3d<f32>(
\r
2510 npf - v3f(BS/6, BS/2, BS/6),
\r
2511 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2515 else if(dir == v3s16(0,1,0))
\r
2517 box = core::aabbox3d<f32>(
\r
2518 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2519 npf + v3f(BS/6, BS/2, BS/6)
\r
2525 box = core::aabbox3d<f32>(
\r
2526 cpf - v3f(BS/6, BS/3, BS/6),
\r
2527 cpf + v3f(BS/6, BS/3, BS/6)
\r
2531 if(distance < mindistance)
\r
2533 if(box.intersectsWithLine(shootline))
\r
2537 neighbourpos = np;
\r
2538 mindistance = distance;
\r
2539 nodehilightbox = box;
\r
2548 for(u16 i=0; i<6; i++)
\r
2550 v3f dir_f = v3f(dirs[i].X,
\r
2551 dirs[i].Y, dirs[i].Z);
\r
2552 v3f centerpoint = npf + dir_f * BS/2;
\r
2554 (centerpoint - camera_position).getLength();
\r
2556 if(distance < mindistance)
\r
2558 core::CMatrix4<f32> m;
\r
2559 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2561 // This is the back face
\r
2562 v3f corners[2] = {
\r
2563 v3f(BS/2, BS/2, BS/2),
\r
2564 v3f(-BS/2, -BS/2, BS/2+d)
\r
2567 for(u16 j=0; j<2; j++)
\r
2569 m.rotateVect(corners[j]);
\r
2570 corners[j] += npf;
\r
2573 core::aabbox3d<f32> facebox(corners[0]);
\r
2574 facebox.addInternalPoint(corners[1]);
\r
2576 if(facebox.intersectsWithLine(shootline))
\r
2580 neighbourpos = np + dirs[i];
\r
2581 mindistance = distance;
\r
2583 //nodehilightbox = facebox;
\r
2585 const float d = 0.502;
\r
2586 core::aabbox3d<f32> nodebox
\r
2587 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2588 v3f nodepos_f = intToFloat(nodepos);
\r
2589 nodebox.MinEdge += nodepos_f;
\r
2590 nodebox.MaxEdge += nodepos_f;
\r
2591 nodehilightbox = nodebox;
\r
2593 } // if distance < mindistance
\r
2595 } // regular block
\r
2598 static float nodig_delay_counter = 0.0;
\r
2602 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2604 static float dig_time = 0.0;
\r
2605 static u16 dig_index = 0;
\r
2607 // Visualize selection
\r
2609 hilightboxes.push_back(nodehilightbox);
\r
2613 if(g_input->getLeftReleased())
\r
2615 client.clearTempMod(nodepos);
\r
2619 if(nodig_delay_counter > 0.0)
\r
2621 nodig_delay_counter -= dtime;
\r
2625 if(nodepos != nodepos_old)
\r
2627 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2628 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2630 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2632 client.clearTempMod(nodepos_old);
\r
2637 if(g_input->getLeftClicked() ||
\r
2638 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2640 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2641 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2643 if(g_input->getLeftClicked())
\r
2645 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2647 if(g_input->getLeftState())
\r
2649 MapNode n = client.getNode(nodepos);
\r
2651 // Get tool name. Default is "" = bare hands
\r
2652 std::string toolname = "";
\r
2653 InventoryList *mlist = local_inventory.getList("main");
\r
2656 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2657 if(item && (std::string)item->getName() == "ToolItem")
\r
2659 ToolItem *titem = (ToolItem*)item;
\r
2660 toolname = titem->getToolName();
\r
2664 // Get digging properties for material and tool
\r
2665 u8 material = n.d;
\r
2666 DiggingProperties prop =
\r
2667 getDiggingProperties(material, toolname);
\r
2669 float dig_time_complete = 0.0;
\r
2671 if(prop.diggable == false)
\r
2673 /*dstream<<"Material "<<(int)material
\r
2674 <<" not diggable with \""
\r
2675 <<toolname<<"\""<<std::endl;*/
\r
2676 // I guess nobody will wait for this long
\r
2677 dig_time_complete = 10000000.0;
\r
2681 dig_time_complete = prop.time;
\r
2684 if(dig_time_complete >= 0.001)
\r
2686 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2687 * dig_time/dig_time_complete);
\r
2689 // This is for torches
\r
2692 dig_index = CRACK_ANIMATION_LENGTH;
\r
2695 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2697 //TimeTaker timer("client.setTempMod");
\r
2698 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2699 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2703 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2704 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2705 client.clearTempMod(nodepos);
\r
2706 client.removeNode(nodepos);
\r
2710 nodig_delay_counter = dig_time_complete
\r
2711 / (float)CRACK_ANIMATION_LENGTH;
\r
2713 // We don't want a corresponding delay to
\r
2714 // very time consuming nodes
\r
2715 if(nodig_delay_counter > 0.5)
\r
2717 nodig_delay_counter = 0.5;
\r
2719 // We want a slight delay to very little
\r
2720 // time consuming nodes
\r
2721 float mindelay = 0.15;
\r
2722 if(nodig_delay_counter < mindelay)
\r
2724 nodig_delay_counter = mindelay;
\r
2728 dig_time += dtime;
\r
2732 if(g_input->getRightClicked())
\r
2734 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2735 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2738 nodepos_old = nodepos;
\r
2743 } // selected_object == NULL
\r
2745 g_input->resetLeftClicked();
\r
2746 g_input->resetRightClicked();
\r
2748 if(g_input->getLeftReleased())
\r
2750 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2752 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2754 if(g_input->getRightReleased())
\r
2756 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2760 g_input->resetLeftReleased();
\r
2761 g_input->resetRightReleased();
\r
2764 Calculate stuff for drawing
\r
2767 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2769 u32 daynight_ratio = client.getDayNightRatio();
\r
2770 /*video::SColor bgcolor = video::SColor(
\r
2772 skycolor.getRed() * daynight_ratio / 1000,
\r
2773 skycolor.getGreen() * daynight_ratio / 1000,
\r
2774 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2776 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2777 video::SColor bgcolor = video::SColor(
\r
2779 skycolor.getRed() * l / 255,
\r
2780 skycolor.getGreen() * l / 255,
\r
2781 skycolor.getBlue() * l / 255);
\r
2787 if(g_settings.getBool("enable_fog") == true)
\r
2789 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2790 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2791 if(draw_control.range_all)
\r
2792 range = 100000*BS;
\r
2796 video::EFT_FOG_LINEAR,
\r
2800 false, // pixel fog
\r
2801 false // range fog
\r
2807 Update gui stuff (0ms)
\r
2810 //TimeTaker guiupdatetimer("Gui updating");
\r
2813 static float drawtime_avg = 0;
\r
2814 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2815 static float beginscenetime_avg = 0;
\r
2816 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2817 static float scenetime_avg = 0;
\r
2818 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2819 static float endscenetime_avg = 0;
\r
2820 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2822 char temptext[300];
\r
2823 snprintf(temptext, 300, "Minetest-c55 ("
\r
2825 ", R: range_all=%i"
\r
2827 " drawtime=%.0f, beginscenetime=%.0f"
\r
2828 ", scenetime=%.0f, endscenetime=%.0f",
\r
2830 draw_control.range_all,
\r
2832 beginscenetime_avg,
\r
2837 guitext->setText(narrow_to_wide(temptext).c_str());
\r
2841 char temptext[300];
\r
2842 snprintf(temptext, 300,
\r
2843 "(% .1f, % .1f, % .1f)"
\r
2844 " (% .3f < btime_jitter < % .3f"
\r
2845 ", dtime_jitter = % .1f %%"
\r
2846 ", v_range = %.1f)",
\r
2847 player_position.X/BS,
\r
2848 player_position.Y/BS,
\r
2849 player_position.Z/BS,
\r
2850 busytime_jitter1_min_sample,
\r
2851 busytime_jitter1_max_sample,
\r
2852 dtime_jitter1_max_fraction * 100.0,
\r
2853 draw_control.wanted_range
\r
2856 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
2860 guitext_info->setText(infotext.c_str());
\r
2864 Get chat messages from client
\r
2867 // Get new messages
\r
2868 std::wstring message;
\r
2869 while(client.getChatMessage(message))
\r
2871 chat_lines.push_back(ChatLine(message));
\r
2872 /*if(chat_lines.size() > 6)
\r
2874 core::list<ChatLine>::Iterator
\r
2875 i = chat_lines.begin();
\r
2876 chat_lines.erase(i);
\r
2879 // Append them to form the whole static text and throw
\r
2880 // it to the gui element
\r
2881 std::wstring whole;
\r
2882 // This will correspond to the line number counted from
\r
2883 // top to bottom, from size-1 to 0
\r
2884 s16 line_number = chat_lines.size();
\r
2885 // Count of messages to be removed from the top
\r
2886 u16 to_be_removed_count = 0;
\r
2887 for(core::list<ChatLine>::Iterator
\r
2888 i = chat_lines.begin();
\r
2889 i != chat_lines.end(); i++)
\r
2891 // After this, line number is valid for this loop
\r
2894 (*i).age += dtime;
\r
2896 This results in a maximum age of 60*6 to the
\r
2897 lowermost line and a maximum of 6 lines
\r
2899 float allowed_age = (6-line_number) * 60.0;
\r
2901 if((*i).age > allowed_age)
\r
2903 to_be_removed_count++;
\r
2906 whole += (*i).text + L'\n';
\r
2908 for(u16 i=0; i<to_be_removed_count; i++)
\r
2910 core::list<ChatLine>::Iterator
\r
2911 it = chat_lines.begin();
\r
2912 chat_lines.erase(it);
\r
2914 guitext_chat->setText(whole.c_str());
\r
2915 // Update gui element size and position
\r
2916 core::rect<s32> rect(
\r
2918 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2919 screensize.X - 10,
\r
2922 guitext_chat->setRelativePosition(rect);
\r
2924 if(chat_lines.size() == 0)
\r
2925 guitext_chat->setVisible(false);
\r
2927 guitext_chat->setVisible(true);
\r
2934 static u16 old_selected_item = 65535;
\r
2935 if(client.getLocalInventoryUpdated()
\r
2936 || g_selected_item != old_selected_item)
\r
2938 old_selected_item = g_selected_item;
\r
2939 //std::cout<<"Updating local inventory"<<std::endl;
\r
2940 client.getLocalInventory(local_inventory);
\r
2941 quick_inventory->setSelection(g_selected_item);
\r
2942 quick_inventory->update();
\r
2946 Send actions returned by the inventory menu
\r
2948 while(inventory_action_queue.size() != 0)
\r
2950 InventoryAction *a = inventory_action_queue.pop_front();
\r
2952 client.sendInventoryAction(a);
\r
2961 TimeTaker drawtimer("Drawing");
\r
2965 TimeTaker timer("beginScene");
\r
2966 driver->beginScene(true, true, bgcolor);
\r
2967 //driver->beginScene(false, true, bgcolor);
\r
2968 beginscenetime = timer.stop(true);
\r
2973 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2976 TimeTaker timer("smgr");
\r
2978 scenetime = timer.stop(true);
\r
2982 //TimeTaker timer9("auxiliary drawings");
\r
2986 //TimeTaker //timer10("//timer10");
\r
2988 video::SMaterial m;
\r
2989 //m.Thickness = 10;
\r
2991 m.Lighting = false;
\r
2992 driver->setMaterial(m);
\r
2994 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2996 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2997 i != hilightboxes.end(); i++)
\r
2999 /*std::cout<<"hilightbox min="
\r
3000 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3002 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3004 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3010 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3011 displaycenter + core::vector2d<s32>(10,0),
\r
3012 video::SColor(255,255,255,255));
\r
3013 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3014 displaycenter + core::vector2d<s32>(0,10),
\r
3015 video::SColor(255,255,255,255));
\r
3020 if(g_settings.getBool("frametime_graph") == true)
\r
3023 for(core::list<float>::Iterator
\r
3024 i = frametime_log.begin();
\r
3025 i != frametime_log.end();
\r
3028 driver->draw2DLine(v2s32(x,50),
\r
3029 v2s32(x,50+(*i)*1000),
\r
3030 video::SColor(255,255,255,255));
\r
3038 //TimeTaker //timer11("//timer11");
\r
3044 guienv->drawAll();
\r
3048 TimeTaker timer("endScene");
\r
3049 driver->endScene();
\r
3050 endscenetime = timer.stop(true);
\r
3053 drawtime = drawtimer.stop(true);
\r
3059 static s16 lastFPS = 0;
\r
3060 //u16 fps = driver->getFPS();
\r
3061 u16 fps = (1.0/dtime_avg1);
\r
3063 if (lastFPS != fps)
\r
3065 core::stringw str = L"Minetest [";
\r
3066 str += driver->getName();
\r
3070 device->setWindowCaption(str.c_str());
\r
3076 device->yield();*/
\r
3079 delete quick_inventory;
\r
3082 Disable texture fetches and other stuff that is queued
\r
3083 to be processed by the main loop.
\r
3085 This has to be done before client goes out of scope.
\r
3087 g_irrlicht->Shutdown(true);
\r
3089 } // client and server are deleted at this point
\r
3092 catch(con::PeerNotFoundException &e)
\r
3094 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3095 error_message = L"Connection timed out.";
\r
3098 } // Menu-game loop
\r
3103 In the end, delete the Irrlicht device.
\r
3108 Update configuration file
\r
3110 /*if(configpath != "")
\r
3112 g_settings.updateConfigFile(configpath.c_str());
\r
3115 END_DEBUG_EXCEPTION_HANDLER
\r
3117 debugstreams_deinit();
\r