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
82 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
83 - This is not doable because it is currently hand-made and not
\r
84 based on some mathematical function. Now it is not.
\r
86 SUGG: A version number to blocks, which increments when the block is
\r
87 modified (node add/remove, water update, lighting update)
\r
88 - This can then be used to make sure the most recent version of
\r
89 a block has been sent to client
\r
91 SUGG: Make the amount of blocks sending to client and the total
\r
92 amount of blocks dynamically limited. Transferring blocks is the
\r
93 main network eater of this system, so it is the one that has
\r
94 to be throttled so that RTTs stay low.
\r
96 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
97 different directions and then only those drawn that need to be
\r
98 - Also an 1-dimensional tile map would be nice probably
\r
103 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
105 - The player could go faster by a crafting a boat, or riding an animal
\r
107 - Random NPC traders. what else?
\r
112 Build system / running:
\r
113 -----------------------
\r
115 NOTE: The following fixme is not apparently valid, and it does work.
\r
116 FIXME: Graphical mode seems to segfault with Irrlicht 1.7.1 on 64-bit
\r
118 - http://pastebin.no/32bo
\r
119 - Might be just a bad build, too
\r
120 - Doesn't affect Irrlicht 1.7.2 or 32-bit 1.7.1. (Arch/Debian)
\r
121 - A similar error occurs when getTexture is called from a thread
\r
122 when the texture has not been already loaded from disk:
\r
123 http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=68830
\r
125 FIXME: Some network errors on Windows that cause local game to not work
\r
126 - See siggjen's emails.
\r
127 - Is this the famous "windows 7 problem"?
\r
129 Networking and serialization:
\r
130 -----------------------------
\r
132 TODO: Get rid of GotSplitPacketException
\r
137 TODO: Add gui option to remove map
\r
139 TODO: Configuration menu, at least for keys
\r
144 TODO: Optimize day/night mesh updating somehow
\r
145 - create copies of all textures for all lighting values and only
\r
146 change texture for material?
\r
147 - Umm... the collecting of the faces is the slow part
\r
148 -> what about just changing the color values of the existing
\r
149 meshbuffers? It should go quite fast.
\r
150 - This is not easy; There'd need to be a buffer somewhere
\r
151 that would contain the night and day lighting values.
\r
152 - Actually if FastFaces would be stored, they could
\r
155 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
157 - That is >500 vertices
\r
158 - This is not easy; all the MapBlocks close to the player would
\r
159 still need to be drawn separately and combining the blocks
\r
160 would have to happen in a background thread
\r
162 TODO: Make fetching sector's blocks more efficient when rendering
\r
163 sectors that have very large amounts of blocks (on client)
\r
164 - Is this necessary at all?
\r
166 TODO: Flowing water animation
\r
168 NOTE(FIXED): A lock condition is possible:
\r
169 1) MapBlock::updateMesh() is called from client asynchronously:
\r
170 - AsyncProcessData() -> Map::updateMeshes()
\r
171 2) Asynchronous locks m_temp_mods_mutex
\r
172 3) MapBlock::updateMesh() is called from client synchronously:
\r
173 - Client::step() -> Environment::step()
\r
174 4) Synchronous starts waiting for m_temp_mods_mutex
\r
175 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
180 TODO: Make the video backend selectable
\r
185 TODO: Untie client network operations from framerate
\r
186 - Needs some input queues or something
\r
188 TODO: Make morning and evening transition more smooth and maybe shorter
\r
190 TODO: Don't update all meshes always on single node changes, but
\r
191 check which ones should be updated
\r
192 - implement Map::updateNodeMeshes()
\r
197 TODO: When player dies, throw items on map
\r
199 TODO: Make an option to the server to disable building and digging near
\r
200 the starting position
\r
202 TODO: Save players with inventories to disk
\r
203 TODO: Players to be saved as text in map/players/<name>
\r
205 TODO: Copy the text of the last picked sign to inventory in creative
\r
208 TODO: Check what goes wrong with caching map to disk (Kray)
\r
211 TODO: When server sees that client is removing an inexistent block to
\r
212 an existent position, resend the MapBlock.
\r
214 FIXME: Server went into some infinite PeerNotFoundException loop
\r
219 TODO: Better handling of objects and mobs
\r
221 - There has to be some way to do it with less messy code
\r
222 - Make separate classes for client and server
\r
223 - Client should not discriminate between blocks, server should
\r
224 - Make other players utilize the same framework
\r
225 - This is also needed for objects that don't get sent to client
\r
226 but are used for triggers etc
\r
228 TODO: There has to be some better way to handle static objects than to
\r
229 send them all the time. This affects signs and item objects.
\r
230 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
231 need an additional metadata field for the texts
\r
232 - This is also needed for item container chests
\r
234 Block object server side:
\r
235 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
236 - For all blocks in the buffer, objects are stepped(). This
\r
237 means they are active.
\r
238 - TODO: A global active buffer is needed for the server
\r
239 - TODO: A timestamp to blocks
\r
240 - TODO: All blocks going in and out of the buffer are recorded.
\r
241 - TODO: For outgoing blocks, timestamp is written.
\r
242 - TODO: For incoming blocks, time difference is calculated and
\r
243 objects are stepped according to it.
\r
248 TODO: Mineral and ground material properties
\r
249 - This way mineral ground toughness can be calculated with just
\r
250 some formula, as well as tool strengths
\r
252 TODO: Flowing water to actually contain flow direction information
\r
254 TODO: Remove duplicate lighting implementation from Map (leave
\r
255 VoxelManipulator, which is faster)
\r
257 FEATURE: Map generator version 2
\r
258 - Create a system that allows a huge amount of different "map
\r
259 generator modules/filters"
\r
261 FEATURE: The map could be generated procedually:
\r
262 - This would need the map to be generated in larger pieces
\r
263 - How large? How do they connect to each other?
\r
264 - It has to be split vertically also
\r
265 - Lighting would not have to be necessarily calculated until
\r
266 the blocks are actually needed - it would be quite fast
\r
267 - Something like 64*64*16 MapBlocks?
\r
268 - No, MapSectors. And as much as it is efficient to do,
\r
269 64x64 might be too much.
\r
270 - FIXME: This is currently halfway done and the generator is
\r
272 * Make the stone level with a heightmap
\r
273 * Carve out stuff in the stone
\r
274 * Dump dirt all around, and simulate it falling off steep
\r
276 * Erosion simulation at map generation time
\r
277 - Simulate water flows, which would carve out dirt fast and
\r
278 then turn stone into gravel and sand and relocate it.
\r
279 - How about relocating minerals, too? Coal and gold in
\r
280 downstream sand and gravel would be kind of cool
\r
281 - This would need a better way of handling minerals, mainly
\r
282 to have mineral content as a separate field. the first
\r
283 parameter field is free for this.
\r
284 - Simulate rock falling from cliffs when water has removed
\r
285 enough solid rock from the bottom
\r
287 Doing now (most important at the top):
\r
288 --------------------------------------
\r
292 === Stuff to do before release
\r
293 * Save the new mapgen stuff
\r
294 - map/meta.txt, which should contain only plain text, something like this:
\r
295 seed = 7ff1bafcd7118800
\r
298 * Save chunk metadata on disk
\r
299 * Make server find the spawning place from the real map data, not from
\r
301 - But the changing borders of chunk have to be avoided, because
\r
302 there is time to generate only one chunk.
\r
303 * Make the generator to run in background and not blocking block
\r
304 placement and transfer
\r
305 * only_from_disk might not work anymore - check and fix it.
\r
306 * Check the fixmes in the list above
\r
308 === Stuff to do after release
\r
309 * Set backface culling on, especially for water
\r
310 * Add some kind of erosion and other stuff that now is possible
\r
311 * Make client to fetch stuff asynchronously
\r
312 - Needs method SyncProcessData
\r
313 * Fix the problem with the server constantly saving one or a few
\r
314 blocks? List the first saved block, maybe it explains.
\r
315 - It is probably caused by oscillating water
\r
316 * Water doesn't start flowing after map generation like it should
\r
317 - Are there still problems?
\r
318 * Better water generation (spread it to underwater caverns but don't
\r
319 fill dungeons that don't touch outside air)
\r
320 * When generating a chunk and the neighboring chunk doesn't have mud
\r
321 and stuff yet and the ground is fairly flat, the mud will flow to
\r
322 the other chunk making nasty straight walls when the other chunk
\r
323 is generated. Fix it.
\r
324 * Make a small history check to transformLiquids to detect and log
\r
325 continuous oscillations, in such detail that they can be fixed.
\r
327 ======================================================================
\r
332 Setting this to 1 enables a special camera mode that forces
\r
333 the renderers to think that the camera statically points from
\r
334 the starting place to a static direction.
\r
336 This allows one to move around with the player and see what
\r
337 is actually drawn behind solid things and behind the player.
\r
339 #define FIELD_OF_VIEW_TEST 0
\r
343 #pragma message ("Disabling unit tests")
\r
345 #warning "Disabling unit tests"
\r
347 // Disable unit tests
\r
348 #define ENABLE_TESTS 0
\r
350 // Enable unit tests
\r
351 #define ENABLE_TESTS 1
\r
355 #pragma comment(lib, "Irrlicht.lib")
\r
356 //#pragma comment(lib, "jthread.lib")
\r
357 #pragma comment(lib, "zlibwapi.lib")
\r
358 #pragma comment(lib, "Shell32.lib")
\r
359 // This would get rid of the console window
\r
360 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
363 #include <iostream>
\r
365 #include <jmutexautolock.h>
\r
366 #include <locale.h>
\r
367 #include "common_irrlicht.h"
\r
370 #include "player.h"
\r
373 #include "environment.h"
\r
374 #include "server.h"
\r
375 #include "client.h"
\r
376 #include "serialization.h"
\r
377 #include "constants.h"
\r
378 #include "strfnd.h"
\r
379 #include "porting.h"
\r
380 #include "irrlichtwrapper.h"
\r
381 #include "gettime.h"
\r
382 #include "porting.h"
\r
383 #include "guiPauseMenu.h"
\r
384 #include "guiInventoryMenu.h"
\r
385 #include "guiTextInputMenu.h"
\r
386 #include "materials.h"
\r
387 #include "guiMessageMenu.h"
\r
388 #include "filesys.h"
\r
389 #include "config.h"
\r
390 #include "guiMainMenu.h"
\r
391 #include "mineral.h"
\r
393 IrrlichtWrapper *g_irrlicht;
\r
395 MapDrawControl draw_control;
\r
399 These are loaded from the config file.
\r
402 Settings g_settings;
\r
404 extern void set_default_settings();
\r
410 IrrlichtDevice *g_device = NULL;
\r
411 Client *g_client = NULL;
\r
417 gui::IGUIEnvironment* guienv = NULL;
\r
418 gui::IGUIStaticText *guiroot = NULL;
\r
420 class MainMenuManager : public IMenuManager
\r
423 virtual void createdMenu(GUIModalMenu *menu)
\r
425 for(core::list<GUIModalMenu*>::Iterator
\r
426 i = m_stack.begin();
\r
427 i != m_stack.end(); i++)
\r
429 assert(*i != menu);
\r
432 if(m_stack.size() != 0)
\r
433 (*m_stack.getLast())->setVisible(false);
\r
434 m_stack.push_back(menu);
\r
437 virtual void deletingMenu(GUIModalMenu *menu)
\r
439 // Remove all entries if there are duplicates
\r
440 bool removed_entry;
\r
442 removed_entry = false;
\r
443 for(core::list<GUIModalMenu*>::Iterator
\r
444 i = m_stack.begin();
\r
445 i != m_stack.end(); i++)
\r
450 removed_entry = true;
\r
454 }while(removed_entry);
\r
456 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
457 assert(*i == menu);
\r
458 m_stack.erase(i);*/
\r
460 if(m_stack.size() != 0)
\r
461 (*m_stack.getLast())->setVisible(true);
\r
466 return m_stack.size();
\r
469 core::list<GUIModalMenu*> m_stack;
\r
472 MainMenuManager g_menumgr;
\r
474 bool noMenuActive()
\r
476 return (g_menumgr.menuCount() == 0);
\r
479 bool g_disconnect_requested = false;
\r
481 class MainGameCallback : public IGameCallback
\r
484 virtual void exitToOS()
\r
486 g_device->closeDevice();
\r
489 virtual void disconnect()
\r
491 g_disconnect_requested = true;
\r
495 MainGameCallback g_gamecallback;
\r
497 // Inventory actions from the menu are buffered here before sending
\r
498 Queue<InventoryAction*> inventory_action_queue;
\r
499 // This is a copy of the inventory that the client's environment has
\r
500 Inventory local_inventory;
\r
502 u16 g_selected_item = 0;
\r
509 std::ostream *dout_con_ptr = &dummyout;
\r
510 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
511 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
512 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
513 //std::ostream *dout_con_ptr = &dstream;
\r
514 //std::ostream *derr_con_ptr = &dstream;
\r
517 std::ostream *dout_server_ptr = &dstream;
\r
518 std::ostream *derr_server_ptr = &dstream;
\r
521 std::ostream *dout_client_ptr = &dstream;
\r
522 std::ostream *derr_client_ptr = &dstream;
\r
525 gettime.h implementation
\r
531 Use irrlicht because it is more precise than porting.h's
\r
534 if(g_irrlicht == NULL)
\r
536 return g_irrlicht->getTime();
\r
543 struct TextDestSign : public TextDest
\r
545 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
547 m_blockpos = blockpos;
\r
551 void gotText(std::wstring text)
\r
553 std::string ntext = wide_to_narrow(text);
\r
554 dstream<<"Changing text of a sign object: "
\r
555 <<ntext<<std::endl;
\r
556 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
564 struct TextDestChat : public TextDest
\r
566 TextDestChat(Client *client)
\r
570 void gotText(std::wstring text)
\r
572 m_client->sendChatMessage(text);
\r
573 m_client->addChatMessage(text);
\r
579 class MyEventReceiver : public IEventReceiver
\r
582 // This is the one method that we have to implement
\r
583 virtual bool OnEvent(const SEvent& event)
\r
586 React to nothing here if a menu is active
\r
588 if(noMenuActive() == false)
\r
594 // Remember whether each key is down or up
\r
595 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
597 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
599 if(event.KeyInput.PressedDown)
\r
601 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
607 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
609 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
611 dstream<<DTIME<<"MyEventReceiver: "
\r
612 <<"Launching pause menu"<<std::endl;
\r
613 // It will delete itself by itself
\r
614 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
615 &g_menumgr))->drop();
\r
618 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
620 dstream<<DTIME<<"MyEventReceiver: "
\r
621 <<"Launching inventory"<<std::endl;
\r
622 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
623 &local_inventory, &inventory_action_queue,
\r
624 &g_menumgr))->drop();
\r
627 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
629 TextDest *dest = new TextDestChat(g_client);
\r
631 (new GUITextInputMenu(guienv, guiroot, -1,
\r
637 // Material selection
\r
638 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
640 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
643 g_selected_item = 0;
\r
644 dstream<<DTIME<<"Selected item: "
\r
645 <<g_selected_item<<std::endl;
\r
648 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
649 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
651 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
652 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
654 if(s1 < PLAYER_INVENTORY_SIZE)
\r
655 g_selected_item = s1-1;
\r
656 dstream<<DTIME<<"Selected item: "
\r
657 <<g_selected_item<<std::endl;
\r
660 // Viewing range selection
\r
661 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
663 if(draw_control.range_all)
\r
665 draw_control.range_all = false;
\r
666 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
670 draw_control.range_all = true;
\r
671 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
675 // Print debug stacks
\r
676 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
678 dstream<<"-----------------------------------------"
\r
680 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
681 dstream<<"-----------------------------------------"
\r
683 debug_stacks_print();
\r
688 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
690 if(noMenuActive() == false)
\r
692 left_active = false;
\r
693 middle_active = false;
\r
694 right_active = false;
\r
698 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
699 left_active = event.MouseInput.isLeftPressed();
\r
700 middle_active = event.MouseInput.isMiddlePressed();
\r
701 right_active = event.MouseInput.isRightPressed();
\r
703 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
705 leftclicked = true;
\r
707 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
709 rightclicked = true;
\r
711 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
713 leftreleased = true;
\r
715 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
717 rightreleased = true;
\r
719 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
721 /*dstream<<"event.MouseInput.Wheel="
\r
722 <<event.MouseInput.Wheel<<std::endl;*/
\r
723 if(event.MouseInput.Wheel < 0)
\r
725 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
728 g_selected_item = 0;
\r
730 else if(event.MouseInput.Wheel > 0)
\r
732 if(g_selected_item > 0)
\r
735 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
744 // This is used to check whether a key is being held down
\r
745 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
747 return keyIsDown[keyCode];
\r
752 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
753 keyIsDown[i] = false;
\r
755 leftclicked = false;
\r
756 rightclicked = false;
\r
757 leftreleased = false;
\r
758 rightreleased = false;
\r
760 left_active = false;
\r
761 middle_active = false;
\r
762 right_active = false;
\r
773 bool rightreleased;
\r
776 bool middle_active;
\r
780 // We use this array to store the current state of each key
\r
781 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
784 IrrlichtDevice *m_device;
\r
793 virtual ~InputHandler()
\r
797 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
799 virtual v2s32 getMousePos() = 0;
\r
800 virtual void setMousePos(s32 x, s32 y) = 0;
\r
802 virtual bool getLeftState() = 0;
\r
803 virtual bool getRightState() = 0;
\r
805 virtual bool getLeftClicked() = 0;
\r
806 virtual bool getRightClicked() = 0;
\r
807 virtual void resetLeftClicked() = 0;
\r
808 virtual void resetRightClicked() = 0;
\r
810 virtual bool getLeftReleased() = 0;
\r
811 virtual bool getRightReleased() = 0;
\r
812 virtual void resetLeftReleased() = 0;
\r
813 virtual void resetRightReleased() = 0;
\r
815 virtual void step(float dtime) {};
\r
817 virtual void clear() {};
\r
820 InputHandler *g_input = NULL;
\r
822 class RealInputHandler : public InputHandler
\r
825 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
827 m_receiver(receiver)
\r
830 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
832 return m_receiver->IsKeyDown(keyCode);
\r
834 virtual v2s32 getMousePos()
\r
836 return m_device->getCursorControl()->getPosition();
\r
838 virtual void setMousePos(s32 x, s32 y)
\r
840 m_device->getCursorControl()->setPosition(x, y);
\r
843 virtual bool getLeftState()
\r
845 return m_receiver->left_active;
\r
847 virtual bool getRightState()
\r
849 return m_receiver->right_active;
\r
852 virtual bool getLeftClicked()
\r
854 return m_receiver->leftclicked;
\r
856 virtual bool getRightClicked()
\r
858 return m_receiver->rightclicked;
\r
860 virtual void resetLeftClicked()
\r
862 m_receiver->leftclicked = false;
\r
864 virtual void resetRightClicked()
\r
866 m_receiver->rightclicked = false;
\r
869 virtual bool getLeftReleased()
\r
871 return m_receiver->leftreleased;
\r
873 virtual bool getRightReleased()
\r
875 return m_receiver->rightreleased;
\r
877 virtual void resetLeftReleased()
\r
879 m_receiver->leftreleased = false;
\r
881 virtual void resetRightReleased()
\r
883 m_receiver->rightreleased = false;
\r
888 resetRightClicked();
\r
889 resetLeftClicked();
\r
892 IrrlichtDevice *m_device;
\r
893 MyEventReceiver *m_receiver;
\r
896 class RandomInputHandler : public InputHandler
\r
899 RandomInputHandler()
\r
901 leftclicked = false;
\r
902 rightclicked = false;
\r
903 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
904 keydown[i] = false;
\r
906 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
908 return keydown[keyCode];
\r
910 virtual v2s32 getMousePos()
\r
914 virtual void setMousePos(s32 x, s32 y)
\r
916 mousepos = v2s32(x,y);
\r
919 virtual bool getLeftState()
\r
923 virtual bool getRightState()
\r
928 virtual bool getLeftClicked()
\r
930 return leftclicked;
\r
932 virtual bool getRightClicked()
\r
934 return rightclicked;
\r
936 virtual void resetLeftClicked()
\r
938 leftclicked = false;
\r
940 virtual void resetRightClicked()
\r
942 rightclicked = false;
\r
945 virtual bool getLeftReleased()
\r
949 virtual bool getRightReleased()
\r
953 virtual void resetLeftReleased()
\r
956 virtual void resetRightReleased()
\r
960 virtual void step(float dtime)
\r
963 static float counter1 = 0;
\r
967 counter1 = 0.1*Rand(1,10);
\r
968 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
969 g_selected_material++;
\r
971 g_selected_material = 0;*/
\r
972 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
975 g_selected_item = 0;
\r
979 static float counter1 = 0;
\r
983 counter1 = 0.1*Rand(1, 40);
\r
984 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
988 static float counter1 = 0;
\r
992 counter1 = 0.1*Rand(1, 40);
\r
993 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
997 static float counter1 = 0;
\r
1001 counter1 = 0.1*Rand(1, 40);
\r
1002 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1006 static float counter1 = 0;
\r
1007 counter1 -= dtime;
\r
1008 if(counter1 < 0.0)
\r
1010 counter1 = 0.1*Rand(1, 40);
\r
1011 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1015 static float counter1 = 0;
\r
1016 counter1 -= dtime;
\r
1017 if(counter1 < 0.0)
\r
1019 counter1 = 0.1*Rand(1, 20);
\r
1020 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1024 static float counter1 = 0;
\r
1025 counter1 -= dtime;
\r
1026 if(counter1 < 0.0)
\r
1028 counter1 = 0.1*Rand(1, 30);
\r
1029 leftclicked = true;
\r
1033 static float counter1 = 0;
\r
1034 counter1 -= dtime;
\r
1035 if(counter1 < 0.0)
\r
1037 counter1 = 0.1*Rand(1, 20);
\r
1038 rightclicked = true;
\r
1041 mousepos += mousespeed;
\r
1044 s32 Rand(s32 min, s32 max)
\r
1046 return (myrand()%(max-min+1))+min;
\r
1049 bool keydown[KEY_KEY_CODES_COUNT];
\r
1053 bool rightclicked;
\r
1056 void updateViewingRange(f32 frametime_in, Client *client)
\r
1058 if(draw_control.range_all == true)
\r
1061 static f32 added_frametime = 0;
\r
1062 static s16 added_frames = 0;
\r
1064 added_frametime += frametime_in;
\r
1065 added_frames += 1;
\r
1067 // Actually this counter kind of sucks because frametime is busytime
\r
1068 static f32 counter = 0;
\r
1069 counter -= frametime_in;
\r
1075 /*dstream<<__FUNCTION_NAME
\r
1076 <<": Collected "<<added_frames<<" frames, total of "
\r
1077 <<added_frametime<<"s."<<std::endl;*/
\r
1079 /*dstream<<"draw_control.blocks_drawn="
\r
1080 <<draw_control.blocks_drawn
\r
1081 <<", draw_control.blocks_would_have_drawn="
\r
1082 <<draw_control.blocks_would_have_drawn
\r
1085 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1086 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1088 draw_control.wanted_min_range = range_min;
\r
1089 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1091 float block_draw_ratio = 1.0;
\r
1092 if(draw_control.blocks_would_have_drawn != 0)
\r
1094 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1095 / (float)draw_control.blocks_would_have_drawn;
\r
1098 // Calculate the average frametime in the case that all wanted
\r
1099 // blocks had been drawn
\r
1100 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1102 added_frametime = 0.0;
\r
1105 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1106 float wanted_frametime = 1.0 / wanted_fps;
\r
1108 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1109 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1111 // If needed frametime change is very small, just return
\r
1112 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1114 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1118 float range = draw_control.wanted_range;
\r
1119 float new_range = range;
\r
1121 static s16 range_old = 0;
\r
1122 static f32 frametime_old = 0;
\r
1124 float d_range = range - range_old;
\r
1125 f32 d_frametime = frametime - frametime_old;
\r
1126 // A sane default of 30ms per 50 nodes of range
\r
1127 static f32 time_per_range = 30. / 50;
\r
1130 time_per_range = d_frametime / d_range;
\r
1133 // The minimum allowed calculated frametime-range derivative:
\r
1134 // Practically this sets the maximum speed of changing the range.
\r
1135 // The lower this value, the higher the maximum changing speed.
\r
1136 // A low value here results in wobbly range (0.001)
\r
1137 // A high value here results in slow changing range (0.0025)
\r
1138 // SUGG: This could be dynamically adjusted so that when
\r
1139 // the camera is turning, this is lower
\r
1140 //float min_time_per_range = 0.0015;
\r
1141 float min_time_per_range = 0.0010;
\r
1142 //float min_time_per_range = 0.05 / range;
\r
1143 if(time_per_range < min_time_per_range)
\r
1145 time_per_range = min_time_per_range;
\r
1146 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1150 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1153 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1154 // Dampen the change a bit to kill oscillations
\r
1155 //wanted_range_change *= 0.9;
\r
1156 //wanted_range_change *= 0.75;
\r
1157 wanted_range_change *= 0.5;
\r
1158 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1160 // If needed range change is very small, just return
\r
1161 if(fabs(wanted_range_change) < 0.001)
\r
1163 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1167 new_range += wanted_range_change;
\r
1168 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1170 //float new_range_unclamped = new_range;
\r
1171 if(new_range < range_min)
\r
1172 new_range = range_min;
\r
1173 if(new_range > range_max)
\r
1174 new_range = range_max;
\r
1176 /*if(new_range != new_range_unclamped)
\r
1177 dstream<<", clamped to "<<new_range<<std::endl;
\r
1179 dstream<<std::endl;*/
\r
1181 draw_control.wanted_range = new_range;
\r
1183 range_old = new_range;
\r
1184 frametime_old = frametime;
\r
1187 class GUIQuickInventory : public IEventReceiver
\r
1190 GUIQuickInventory(
\r
1191 gui::IGUIEnvironment* env,
\r
1192 gui::IGUIElement* parent,
\r
1195 Inventory *inventory):
\r
1196 m_itemcount(itemcount),
\r
1197 m_inventory(inventory)
\r
1199 core::rect<s32> imgsize(0,0,48,48);
\r
1200 core::rect<s32> textsize(0,0,48,16);
\r
1201 v2s32 spacing(0, 64);
\r
1202 for(s32 i=0; i<m_itemcount; i++)
\r
1204 m_images.push_back(env->addImage(
\r
1205 imgsize + pos + spacing*i
\r
1207 m_images[i]->setScaleImage(true);
\r
1208 m_texts.push_back(env->addStaticText(
\r
1210 textsize + pos + spacing*i,
\r
1213 m_texts[i]->setBackgroundColor(
\r
1214 video::SColor(128,0,0,0));
\r
1215 m_texts[i]->setTextAlignment(
\r
1216 gui::EGUIA_CENTER,
\r
1217 gui::EGUIA_UPPERLEFT);
\r
1221 ~GUIQuickInventory()
\r
1223 for(u32 i=0; i<m_texts.size(); i++)
\r
1225 m_texts[i]->remove();
\r
1227 for(u32 i=0; i<m_images.size(); i++)
\r
1229 m_images[i]->remove();
\r
1233 virtual bool OnEvent(const SEvent& event)
\r
1238 void setSelection(s32 i)
\r
1247 start = m_selection - m_itemcount / 2;
\r
1249 InventoryList *mainlist = m_inventory->getList("main");
\r
1251 for(s32 i=0; i<m_itemcount; i++)
\r
1253 s32 j = i + start;
\r
1255 if(j > (s32)mainlist->getSize() - 1)
\r
1256 j -= mainlist->getSize();
\r
1258 j += mainlist->getSize();
\r
1260 InventoryItem *item = mainlist->getItem(j);
\r
1264 m_images[i]->setImage(NULL);
\r
1267 if(m_selection == j)
\r
1268 swprintf(t, 10, L"<-");
\r
1270 swprintf(t, 10, L"");
\r
1271 m_texts[i]->setText(t);
\r
1273 // The next ifs will segfault with a NULL pointer
\r
1278 m_images[i]->setImage(item->getImage());
\r
1281 if(m_selection == j)
\r
1282 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1284 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1285 m_texts[i]->setText(t);
\r
1291 core::array<gui::IGUIStaticText*> m_texts;
\r
1292 core::array<gui::IGUIImage*> m_images;
\r
1293 Inventory *m_inventory;
\r
1304 ChatLine(const std::wstring &a_text):
\r
1310 std::wstring text;
\r
1313 // These are defined global so that they're not optimized too much.
\r
1314 // Can't change them to volatile.
\r
1319 std::string tempstring;
\r
1320 std::string tempstring2;
\r
1325 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1326 TimeTaker timer("Testing std::string speed");
\r
1327 const u32 jj = 10000;
\r
1328 for(u32 j=0; j<jj; j++)
\r
1332 const u32 ii = 10;
\r
1333 for(u32 i=0; i<ii; i++){
\r
1334 tempstring2 += "asd";
\r
1336 for(u32 i=0; i<ii+1; i++){
\r
1337 tempstring += "asd";
\r
1338 if(tempstring == tempstring2)
\r
1344 dstream<<"All of the following tests should take around 100ms each."
\r
1348 TimeTaker timer("Testing floating-point conversion speed");
\r
1350 for(u32 i=0; i<4000000; i++){
\r
1357 TimeTaker timer("Testing floating-point vector speed");
\r
1359 tempv3f1 = v3f(1,2,3);
\r
1360 tempv3f2 = v3f(4,5,6);
\r
1361 for(u32 i=0; i<10000000; i++){
\r
1362 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1363 tempv3f2 += v3f(7,8,9);
\r
1368 TimeTaker timer("Testing core::map speed");
\r
1370 core::map<v2s16, f32> map1;
\r
1373 for(s16 y=0; y<ii; y++){
\r
1374 for(s16 x=0; x<ii; x++){
\r
1375 map1.insert(v2s16(x,y), tempf);
\r
1379 for(s16 y=ii-1; y>=0; y--){
\r
1380 for(s16 x=0; x<ii; x++){
\r
1381 tempf = map1[v2s16(x,y)];
\r
1387 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1388 TimeTaker timer("Testing mutex speed");
\r
1401 // Do at least 10ms
\r
1402 while(timer.getTime() < 10);
\r
1404 u32 dtime = timer.stop();
\r
1405 u32 per_ms = n / dtime;
\r
1406 std::cout<<"Done. "<<dtime<<"ms, "
\r
1407 <<per_ms<<"/ms"<<std::endl;
\r
1411 int main(int argc, char *argv[])
\r
1414 Parse command line
\r
1417 // List all allowed options
\r
1418 core::map<std::string, ValueSpec> allowed_options;
\r
1419 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1420 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1421 "Run server directly"));
\r
1422 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1423 "Load configuration from specified file"));
\r
1424 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1425 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1426 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1427 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1428 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1429 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1431 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1433 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1435 Settings cmd_args;
\r
1437 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1439 if(ret == false || cmd_args.getFlag("help"))
\r
1441 dstream<<"Allowed options:"<<std::endl;
\r
1442 for(core::map<std::string, ValueSpec>::Iterator
\r
1443 i = allowed_options.getIterator();
\r
1444 i.atEnd() == false; i++)
\r
1446 dstream<<" --"<<i.getNode()->getKey();
\r
1447 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1452 dstream<<" <value>";
\r
1454 dstream<<std::endl;
\r
1456 if(i.getNode()->getValue().help != NULL)
\r
1458 dstream<<" "<<i.getNode()->getValue().help
\r
1463 return cmd_args.getFlag("help") ? 0 : 1;
\r
1467 Low-level initialization
\r
1470 bool disable_stderr = false;
\r
1472 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1473 disable_stderr = true;
\r
1476 // Initialize debug streams
\r
1477 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1478 // Initialize debug stacks
\r
1479 debug_stacks_init();
\r
1481 DSTACK(__FUNCTION_NAME);
\r
1483 porting::initializePaths();
\r
1484 // Create user data directory
\r
1485 fs::CreateDir(porting::path_userdata);
\r
1487 // C-style stuff initialization
\r
1488 initializeMaterialProperties();
\r
1491 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1493 // Print startup message
\r
1494 dstream<<DTIME<<"minetest-c55"
\r
1495 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1496 <<", "<<BUILD_INFO
\r
1500 Basic initialization
\r
1503 // Initialize default settings
\r
1504 set_default_settings();
\r
1506 // Set locale. This is for forcing '.' as the decimal point.
\r
1507 std::locale::global(std::locale("C"));
\r
1508 // This enables printing all characters in bitmap font
\r
1509 setlocale(LC_CTYPE, "en_US");
\r
1511 // Initialize sockets
\r
1513 atexit(sockets_cleanup);
\r
1523 // Path of configuration file in use
\r
1524 std::string configpath = "";
\r
1526 if(cmd_args.exists("config"))
\r
1528 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1531 dstream<<"Could not read configuration from \""
\r
1532 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1535 configpath = cmd_args.get("config");
\r
1539 core::array<std::string> filenames;
\r
1540 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1541 #ifdef RUN_IN_PLACE
\r
1542 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1545 for(u32 i=0; i<filenames.size(); i++)
\r
1547 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1550 configpath = filenames[i];
\r
1556 // Initialize random seed
\r
1561 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1563 These are needed for unit tests at least.
\r
1566 IIrrlichtWrapper irrlicht_dummy;
\r
1568 init_mapnode(&irrlicht_dummy);
\r
1573 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1574 || cmd_args.getFlag("enable-unittests") == true)
\r
1585 if(cmd_args.exists("port"))
\r
1586 port = cmd_args.getU16("port");
\r
1587 else if(cmd_args.exists("port"))
\r
1588 port = g_settings.getU16("port");
\r
1591 std::string map_dir = porting::path_userdata+"/map";
\r
1592 if(cmd_args.exists("map-dir"))
\r
1593 map_dir = cmd_args.get("map-dir");
\r
1594 else if(g_settings.exists("map-dir"))
\r
1595 map_dir = g_settings.get("map-dir");
\r
1597 // Run dedicated server if asked to
\r
1598 if(cmd_args.getFlag("server"))
\r
1600 DSTACK("Dedicated server branch");
\r
1603 Server server(map_dir.c_str());
\r
1604 server.start(port);
\r
1607 dedicated_server_loop(server);
\r
1616 // Address to connect to
\r
1617 std::string address = "";
\r
1619 if(cmd_args.exists("address"))
\r
1621 address = cmd_args.get("address");
\r
1625 address = g_settings.get("address");
\r
1628 std::string playername = g_settings.get("name");
\r
1631 Resolution selection
\r
1634 bool fullscreen = false;
\r
1635 u16 screenW = g_settings.getU16("screenW");
\r
1636 u16 screenH = g_settings.getU16("screenH");
\r
1640 MyEventReceiver receiver;
\r
1642 video::E_DRIVER_TYPE driverType;
\r
1645 //driverType = video::EDT_DIRECT3D9;
\r
1646 driverType = video::EDT_OPENGL;
\r
1648 driverType = video::EDT_OPENGL;
\r
1649 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1652 // create device and exit if creation failed
\r
1654 IrrlichtDevice *device;
\r
1655 device = createDevice(driverType,
\r
1656 core::dimension2d<u32>(screenW, screenH),
\r
1657 16, fullscreen, false, false, &receiver);
\r
1660 return 1; // could not create selected driver.
\r
1662 g_device = device;
\r
1663 g_irrlicht = new IrrlichtWrapper(device);
\r
1666 Speed tests (done after irrlicht is loaded to get timer)
\r
1668 if(cmd_args.getFlag("speedtests"))
\r
1670 dstream<<"Running speed tests"<<std::endl;
\r
1675 device->setResizable(true);
\r
1677 bool random_input = g_settings.getBool("random_input")
\r
1678 || cmd_args.getFlag("random-input");
\r
1680 g_input = new RandomInputHandler();
\r
1682 g_input = new RealInputHandler(device, &receiver);
\r
1685 Continue initialization
\r
1688 video::IVideoDriver* driver = device->getVideoDriver();
\r
1691 This changes the minimum allowed number of vertices in a VBO
\r
1693 //driver->setMinHardwareBufferVertexCount(50);
\r
1695 scene::ISceneManager* smgr = device->getSceneManager();
\r
1697 guienv = device->getGUIEnvironment();
\r
1698 gui::IGUISkin* skin = guienv->getSkin();
\r
1699 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1701 skin->setFont(font);
\r
1703 dstream<<"WARNING: Font file was not found."
\r
1704 " Using default font."<<std::endl;
\r
1705 // If font was not found, this will get us one
\r
1706 font = skin->getFont();
\r
1709 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1710 dstream<<"text_height="<<text_height<<std::endl;
\r
1712 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1713 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1714 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1715 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1716 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1717 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1720 Preload some textures and stuff
\r
1723 init_content_inventory_texture_paths();
\r
1724 init_mapnode(g_irrlicht);
\r
1725 init_mineral(g_irrlicht);
\r
1732 We need some kind of a root node to be able to add
\r
1733 custom gui elements directly on the screen.
\r
1734 Otherwise they won't be automatically drawn.
\r
1736 guiroot = guienv->addStaticText(L"",
\r
1737 core::rect<s32>(0, 0, 10000, 10000));
\r
1739 // First line of debug text
\r
1740 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1742 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1744 // Second line of debug text
\r
1745 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1747 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1750 // At the middle of the screen
\r
1751 // Object infos are shown in this
\r
1752 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1754 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1758 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1760 core::rect<s32>(0,0,0,0),
\r
1762 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1763 core::list<ChatLine> chat_lines;
\r
1766 If an error occurs, this is set to something and the
\r
1767 menu-game loop is restarted. It is then displayed before
\r
1770 std::wstring error_message = L"";
\r
1775 while(g_device->run())
\r
1778 // This is used for catching disconnects
\r
1783 Out-of-game menu loop.
\r
1785 Loop quits when menu returns proper parameters.
\r
1789 // Cursor can be non-visible when coming from the game
\r
1790 device->getCursorControl()->setVisible(true);
\r
1791 // Some stuff are left to scene manager when coming from the game
\r
1792 // (map at least?)
\r
1794 // Reset or hide the debug gui texts
\r
1795 guitext->setText(L"Minetest-c55");
\r
1796 guitext2->setVisible(false);
\r
1797 guitext_info->setVisible(false);
\r
1798 guitext_chat->setVisible(false);
\r
1800 // Initialize menu data
\r
1801 MainMenuData menudata;
\r
1802 menudata.address = narrow_to_wide(address);
\r
1803 menudata.name = narrow_to_wide(playername);
\r
1804 menudata.port = narrow_to_wide(itos(port));
\r
1805 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1807 GUIMainMenu *menu =
\r
1808 new GUIMainMenu(guienv, guiroot, -1,
\r
1809 &g_menumgr, &menudata, &g_gamecallback);
\r
1810 menu->allowFocusRemoval(true);
\r
1812 if(error_message != L"")
\r
1814 GUIMessageMenu *menu2 =
\r
1815 new GUIMessageMenu(guienv, guiroot, -1,
\r
1816 &g_menumgr, error_message.c_str());
\r
1818 error_message = L"";
\r
1821 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1823 dstream<<"Created main menu"<<std::endl;
\r
1825 while(g_device->run())
\r
1827 // Run global IrrlichtWrapper's main thread processing stuff
\r
1828 g_irrlicht->Run();
\r
1830 if(menu->getStatus() == true)
\r
1833 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1834 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1835 guienv->drawAll();
\r
1836 driver->endScene();
\r
1839 // Break out of menu-game loop to shut down cleanly
\r
1840 if(g_device->run() == false)
\r
1843 dstream<<"Dropping main menu"<<std::endl;
\r
1847 // Delete map if requested
\r
1848 if(menudata.delete_map)
\r
1850 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1852 error_message = L"Delete failed";
\r
1856 playername = wide_to_narrow(menudata.name);
\r
1857 address = wide_to_narrow(menudata.address);
\r
1858 port = stoi(wide_to_narrow(menudata.port));
\r
1859 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1861 // Check for valid parameters, restart menu if invalid.
\r
1862 if(playername == "")
\r
1864 error_message = L"Name required.";
\r
1869 g_settings.set("name", playername);
\r
1870 g_settings.set("address", address);
\r
1871 g_settings.set("port", itos(port));
\r
1872 // Update configuration file
\r
1873 if(configpath != "")
\r
1874 g_settings.updateConfigFile(configpath.c_str());
\r
1876 // Continue to game
\r
1880 // Break out of menu-game loop to shut down cleanly
\r
1881 if(g_device->run() == false)
\r
1885 Make a scope here so that the client and the server and other
\r
1886 stuff gets removed when disconnected or the irrlicht device
\r
1891 // This is set to true at the end of the scope
\r
1892 g_irrlicht->Shutdown(false);
\r
1895 Draw "Loading" screen
\r
1897 const wchar_t *text = L"Loading and connecting...";
\r
1898 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1899 core::vector2d<s32> textsize(300, text_height);
\r
1900 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1902 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1903 text, textrect, false, false);
\r
1904 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1906 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1907 guienv->drawAll();
\r
1908 driver->endScene();
\r
1910 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1914 SharedPtr will delete it when it goes out of scope.
\r
1916 SharedPtr<Server> server;
\r
1917 if(address == ""){
\r
1918 server = new Server(map_dir);
\r
1919 server->start(port);
\r
1926 Client client(device, playername.c_str(), draw_control);
\r
1928 g_client = &client;
\r
1930 Address connect_address(0,0,0,0, port);
\r
1933 connect_address.Resolve("localhost");
\r
1935 connect_address.Resolve(address.c_str());
\r
1937 catch(ResolveError &e)
\r
1939 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1941 error_message = L"Couldn't resolve address";
\r
1942 gui_loadingtext->remove();
\r
1946 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1947 client.connect(connect_address);
\r
1950 while(client.connectedAndInitialized() == false)
\r
1953 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1954 guienv->drawAll();
\r
1955 driver->endScene();
\r
1957 // Update client and server
\r
1961 if(server != NULL)
\r
1962 server->step(0.1);
\r
1968 catch(con::PeerNotFoundException &e)
\r
1970 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1972 error_message = L"Connection timed out.";
\r
1973 gui_loadingtext->remove();
\r
1980 /*scene::ISceneNode* skybox;
\r
1981 skybox = smgr->addSkyBoxSceneNode(
\r
1982 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1983 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1984 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1985 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1986 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1987 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1990 Create the camera node
\r
1993 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1994 0, // Camera parent
\r
1995 v3f(BS*100, BS*2, BS*100), // Look from
\r
1996 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2000 if(camera == NULL)
\r
2003 video::SColor skycolor = video::SColor(255,90,140,200);
\r
2005 camera->setFOV(FOV_ANGLE);
\r
2007 // Just so big a value that everything rendered is visible
\r
2008 camera->setFarValue(100000*BS);
\r
2010 f32 camera_yaw = 0; // "right/left"
\r
2011 f32 camera_pitch = 0; // "up/down"
\r
2017 gui_loadingtext->remove();
\r
2020 Add some gui stuff
\r
2023 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2024 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2026 // Test the text input system
\r
2027 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2029 /*GUIMessageMenu *menu =
\r
2030 new GUIMessageMenu(guienv, guiroot, -1,
\r
2035 // Launch pause menu
\r
2036 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2037 &g_menumgr))->drop();
\r
2040 guitext2->setVisible(true);
\r
2041 guitext_info->setVisible(true);
\r
2042 guitext_chat->setVisible(true);
\r
2045 Some statistics are collected in these
\r
2048 u32 beginscenetime = 0;
\r
2049 u32 scenetime = 0;
\r
2050 u32 endscenetime = 0;
\r
2053 //throw con::PeerNotFoundException("lol");
\r
2059 bool first_loop_after_window_activation = true;
\r
2061 // Time is in milliseconds
\r
2062 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2063 // NOTE: So we have to use getTime() and call run()s between them
\r
2064 u32 lasttime = device->getTimer()->getTime();
\r
2066 while(device->run())
\r
2068 if(g_disconnect_requested)
\r
2070 g_disconnect_requested = false;
\r
2075 Run global IrrlichtWrapper's main thread processing stuff
\r
2077 g_irrlicht->Run();
\r
2080 Random calculations
\r
2082 v2u32 screensize = driver->getScreenSize();
\r
2083 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2085 // Hilight boxes collected during the loop and displayed
\r
2086 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2089 std::wstring infotext;
\r
2091 //TimeTaker //timer1("//timer1");
\r
2093 // Time of frame without fps limit
\r
2097 // not using getRealTime is necessary for wine
\r
2098 u32 time = device->getTimer()->getTime();
\r
2099 if(time > lasttime)
\r
2100 busytime_u32 = time - lasttime;
\r
2103 busytime = busytime_u32 / 1000.0;
\r
2106 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2108 // Absolutelu necessary for wine!
\r
2115 updateViewingRange(busytime, &client);
\r
2122 float fps_max = g_settings.getFloat("fps_max");
\r
2123 u32 frametime_min = 1000./fps_max;
\r
2125 if(busytime_u32 < frametime_min)
\r
2127 u32 sleeptime = frametime_min - busytime_u32;
\r
2128 device->sleep(sleeptime);
\r
2132 // Absolutelu necessary for wine!
\r
2136 Time difference calculation
\r
2138 f32 dtime; // in seconds
\r
2140 u32 time = device->getTimer()->getTime();
\r
2141 if(time > lasttime)
\r
2142 dtime = (time - lasttime) / 1000.0;
\r
2148 Time average and jitter calculation
\r
2151 static f32 dtime_avg1 = 0.0;
\r
2152 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2153 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2155 static f32 dtime_jitter1_max_sample = 0.0;
\r
2156 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2158 static f32 jitter1_max = 0.0;
\r
2159 static f32 counter = 0.0;
\r
2160 if(dtime_jitter1 > jitter1_max)
\r
2161 jitter1_max = dtime_jitter1;
\r
2166 dtime_jitter1_max_sample = jitter1_max;
\r
2167 dtime_jitter1_max_fraction
\r
2168 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2169 jitter1_max = 0.0;
\r
2174 Busytime average and jitter calculation
\r
2177 static f32 busytime_avg1 = 0.0;
\r
2178 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2179 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2181 static f32 busytime_jitter1_max_sample = 0.0;
\r
2182 static f32 busytime_jitter1_min_sample = 0.0;
\r
2184 static f32 jitter1_max = 0.0;
\r
2185 static f32 jitter1_min = 0.0;
\r
2186 static f32 counter = 0.0;
\r
2187 if(busytime_jitter1 > jitter1_max)
\r
2188 jitter1_max = busytime_jitter1;
\r
2189 if(busytime_jitter1 < jitter1_min)
\r
2190 jitter1_min = busytime_jitter1;
\r
2192 if(counter > 0.0){
\r
2194 busytime_jitter1_max_sample = jitter1_max;
\r
2195 busytime_jitter1_min_sample = jitter1_min;
\r
2196 jitter1_max = 0.0;
\r
2197 jitter1_min = 0.0;
\r
2202 Debug info for client
\r
2205 static float counter = 0.0;
\r
2210 client.printDebugInfo(std::cout);
\r
2215 Input handler step()
\r
2217 g_input->step(dtime);
\r
2220 Player speed control
\r
2229 bool a_superspeed,
\r
2232 PlayerControl control(
\r
2233 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2234 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2235 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2236 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2237 g_input->isKeyDown(irr::KEY_SPACE),
\r
2238 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2242 client.setPlayerControl(control);
\r
2246 Process environment
\r
2250 //TimeTaker timer("client.step(dtime)");
\r
2251 client.step(dtime);
\r
2252 //client.step(dtime_avg1);
\r
2255 if(server != NULL)
\r
2257 //TimeTaker timer("server->step(dtime)");
\r
2258 server->step(dtime);
\r
2261 v3f player_position = client.getPlayerPosition();
\r
2263 //TimeTaker //timer2("//timer2");
\r
2266 Mouse and camera control
\r
2269 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2272 device->getCursorControl()->setVisible(false);
\r
2274 if(first_loop_after_window_activation){
\r
2275 //std::cout<<"window active, first loop"<<std::endl;
\r
2276 first_loop_after_window_activation = false;
\r
2279 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2280 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2281 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2282 camera_yaw -= dx*0.2;
\r
2283 camera_pitch += dy*0.2;
\r
2284 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2285 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2287 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2290 device->getCursorControl()->setVisible(true);
\r
2292 //std::cout<<"window inactive"<<std::endl;
\r
2293 first_loop_after_window_activation = true;
\r
2296 camera_yaw = wrapDegrees(camera_yaw);
\r
2297 camera_pitch = wrapDegrees(camera_pitch);
\r
2299 v3f camera_direction = v3f(0,0,1);
\r
2300 camera_direction.rotateYZBy(camera_pitch);
\r
2301 camera_direction.rotateXZBy(camera_yaw);
\r
2303 // This is at the height of the eyes of the current figure
\r
2304 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2305 // This is more like in minecraft
\r
2306 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2308 camera->setPosition(camera_position);
\r
2309 // *100.0 helps in large map coordinates
\r
2310 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2312 if(FIELD_OF_VIEW_TEST){
\r
2313 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2314 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2317 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2318 //TimeTaker timer("client.updateCamera");
\r
2319 client.updateCamera(camera_position, camera_direction);
\r
2323 //TimeTaker //timer3("//timer3");
\r
2326 Calculate what block is the crosshair pointing to
\r
2329 //u32 t1 = device->getTimer()->getRealTime();
\r
2331 //f32 d = 4; // max. distance
\r
2332 f32 d = 4; // max. distance
\r
2333 core::line3d<f32> shootline(camera_position,
\r
2334 camera_position + camera_direction * BS * (d+1));
\r
2336 MapBlockObject *selected_object = client.getSelectedObject
\r
2337 (d*BS, camera_position, shootline);
\r
2340 If it's pointing to a MapBlockObject
\r
2343 if(selected_object != NULL)
\r
2345 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2347 core::aabbox3d<f32> box_on_map
\r
2348 = selected_object->getSelectionBoxOnMap();
\r
2350 hilightboxes.push_back(box_on_map);
\r
2352 infotext = narrow_to_wide(selected_object->infoText());
\r
2354 if(g_input->getLeftClicked())
\r
2356 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2357 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2358 selected_object->getId(), g_selected_item);
\r
2360 else if(g_input->getRightClicked())
\r
2362 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2364 Check if we want to modify the object ourselves
\r
2366 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2368 dstream<<"Sign object right-clicked"<<std::endl;
\r
2370 if(random_input == false)
\r
2372 // Get a new text for it
\r
2374 TextDest *dest = new TextDestSign(
\r
2375 selected_object->getBlock()->getPos(),
\r
2376 selected_object->getId(),
\r
2379 SignObject *sign_object = (SignObject*)selected_object;
\r
2381 std::wstring wtext =
\r
2382 narrow_to_wide(sign_object->getText());
\r
2384 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2390 Otherwise pass the event to the server as-is
\r
2394 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2395 selected_object->getId(), g_selected_item);
\r
2399 else // selected_object == NULL
\r
2403 Find out which node we are pointing at
\r
2406 bool nodefound = false;
\r
2408 v3s16 neighbourpos;
\r
2409 core::aabbox3d<f32> nodehilightbox;
\r
2410 f32 mindistance = BS * 1001;
\r
2412 v3s16 pos_i = floatToInt(player_position);
\r
2414 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2418 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2419 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2420 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2421 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2422 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2423 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2425 for(s16 y = ystart; y <= yend; y++)
\r
2426 for(s16 z = zstart; z <= zend; z++)
\r
2427 for(s16 x = xstart; x <= xend; x++)
\r
2432 n = client.getNode(v3s16(x,y,z));
\r
2433 if(content_pointable(n.d) == false)
\r
2436 catch(InvalidPositionException &e)
\r
2442 v3f npf = intToFloat(np);
\r
2447 v3s16(0,0,1), // back
\r
2448 v3s16(0,1,0), // top
\r
2449 v3s16(1,0,0), // right
\r
2450 v3s16(0,0,-1), // front
\r
2451 v3s16(0,-1,0), // bottom
\r
2452 v3s16(-1,0,0), // left
\r
2458 if(n.d == CONTENT_TORCH)
\r
2460 v3s16 dir = unpackDir(n.dir);
\r
2461 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2462 dir_f *= BS/2 - BS/6 - BS/20;
\r
2463 v3f cpf = npf + dir_f;
\r
2464 f32 distance = (cpf - camera_position).getLength();
\r
2466 core::aabbox3d<f32> box;
\r
2469 if(dir == v3s16(0,-1,0))
\r
2471 box = core::aabbox3d<f32>(
\r
2472 npf - v3f(BS/6, BS/2, BS/6),
\r
2473 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2477 else if(dir == v3s16(0,1,0))
\r
2479 box = core::aabbox3d<f32>(
\r
2480 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2481 npf + v3f(BS/6, BS/2, BS/6)
\r
2487 box = core::aabbox3d<f32>(
\r
2488 cpf - v3f(BS/6, BS/3, BS/6),
\r
2489 cpf + v3f(BS/6, BS/3, BS/6)
\r
2493 if(distance < mindistance)
\r
2495 if(box.intersectsWithLine(shootline))
\r
2499 neighbourpos = np;
\r
2500 mindistance = distance;
\r
2501 nodehilightbox = box;
\r
2510 for(u16 i=0; i<6; i++)
\r
2512 v3f dir_f = v3f(dirs[i].X,
\r
2513 dirs[i].Y, dirs[i].Z);
\r
2514 v3f centerpoint = npf + dir_f * BS/2;
\r
2516 (centerpoint - camera_position).getLength();
\r
2518 if(distance < mindistance)
\r
2520 core::CMatrix4<f32> m;
\r
2521 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2523 // This is the back face
\r
2524 v3f corners[2] = {
\r
2525 v3f(BS/2, BS/2, BS/2),
\r
2526 v3f(-BS/2, -BS/2, BS/2+d)
\r
2529 for(u16 j=0; j<2; j++)
\r
2531 m.rotateVect(corners[j]);
\r
2532 corners[j] += npf;
\r
2535 core::aabbox3d<f32> facebox(corners[0]);
\r
2536 facebox.addInternalPoint(corners[1]);
\r
2538 if(facebox.intersectsWithLine(shootline))
\r
2542 neighbourpos = np + dirs[i];
\r
2543 mindistance = distance;
\r
2545 //nodehilightbox = facebox;
\r
2547 const float d = 0.502;
\r
2548 core::aabbox3d<f32> nodebox
\r
2549 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2550 v3f nodepos_f = intToFloat(nodepos);
\r
2551 nodebox.MinEdge += nodepos_f;
\r
2552 nodebox.MaxEdge += nodepos_f;
\r
2553 nodehilightbox = nodebox;
\r
2555 } // if distance < mindistance
\r
2557 } // regular block
\r
2560 static float nodig_delay_counter = 0.0;
\r
2564 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2566 static float dig_time = 0.0;
\r
2567 static u16 dig_index = 0;
\r
2569 // Visualize selection
\r
2571 hilightboxes.push_back(nodehilightbox);
\r
2575 if(g_input->getLeftReleased())
\r
2577 client.clearTempMod(nodepos);
\r
2581 if(nodig_delay_counter > 0.0)
\r
2583 nodig_delay_counter -= dtime;
\r
2587 if(nodepos != nodepos_old)
\r
2589 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2590 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2592 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2594 client.clearTempMod(nodepos_old);
\r
2599 if(g_input->getLeftClicked() ||
\r
2600 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2602 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2603 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2605 if(g_input->getLeftClicked())
\r
2607 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2609 if(g_input->getLeftState())
\r
2611 MapNode n = client.getNode(nodepos);
\r
2613 // Get tool name. Default is "" = bare hands
\r
2614 std::string toolname = "";
\r
2615 InventoryList *mlist = local_inventory.getList("main");
\r
2618 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2619 if(item && (std::string)item->getName() == "ToolItem")
\r
2621 ToolItem *titem = (ToolItem*)item;
\r
2622 toolname = titem->getToolName();
\r
2626 // Get digging properties for material and tool
\r
2627 u8 material = n.d;
\r
2628 DiggingProperties prop =
\r
2629 getDiggingProperties(material, toolname);
\r
2631 float dig_time_complete = 0.0;
\r
2633 if(prop.diggable == false)
\r
2635 /*dstream<<"Material "<<(int)material
\r
2636 <<" not diggable with \""
\r
2637 <<toolname<<"\""<<std::endl;*/
\r
2638 // I guess nobody will wait for this long
\r
2639 dig_time_complete = 10000000.0;
\r
2643 dig_time_complete = prop.time;
\r
2646 if(dig_time_complete >= 0.001)
\r
2648 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2649 * dig_time/dig_time_complete);
\r
2651 // This is for torches
\r
2654 dig_index = CRACK_ANIMATION_LENGTH;
\r
2657 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2659 //TimeTaker timer("client.setTempMod");
\r
2660 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2661 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2665 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2666 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2667 client.clearTempMod(nodepos);
\r
2668 client.removeNode(nodepos);
\r
2672 nodig_delay_counter = dig_time_complete
\r
2673 / (float)CRACK_ANIMATION_LENGTH;
\r
2675 // We don't want a corresponding delay to
\r
2676 // very time consuming nodes
\r
2677 if(nodig_delay_counter > 0.5)
\r
2679 nodig_delay_counter = 0.5;
\r
2681 // We want a slight delay to very little
\r
2682 // time consuming nodes
\r
2683 float mindelay = 0.15;
\r
2684 if(nodig_delay_counter < mindelay)
\r
2686 nodig_delay_counter = mindelay;
\r
2690 dig_time += dtime;
\r
2694 if(g_input->getRightClicked())
\r
2696 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2697 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2700 nodepos_old = nodepos;
\r
2705 } // selected_object == NULL
\r
2707 g_input->resetLeftClicked();
\r
2708 g_input->resetRightClicked();
\r
2710 if(g_input->getLeftReleased())
\r
2712 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2714 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2716 if(g_input->getRightReleased())
\r
2718 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2722 g_input->resetLeftReleased();
\r
2723 g_input->resetRightReleased();
\r
2726 Calculate stuff for drawing
\r
2729 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2731 u32 daynight_ratio = client.getDayNightRatio();
\r
2732 /*video::SColor bgcolor = video::SColor(
\r
2734 skycolor.getRed() * daynight_ratio / 1000,
\r
2735 skycolor.getGreen() * daynight_ratio / 1000,
\r
2736 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2738 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2739 video::SColor bgcolor = video::SColor(
\r
2741 skycolor.getRed() * l / 255,
\r
2742 skycolor.getGreen() * l / 255,
\r
2743 skycolor.getBlue() * l / 255);
\r
2749 if(g_settings.getBool("enable_fog") == true)
\r
2751 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2752 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2753 if(draw_control.range_all)
\r
2754 range = 100000*BS;
\r
2758 video::EFT_FOG_LINEAR,
\r
2762 false, // pixel fog
\r
2763 false // range fog
\r
2769 Update gui stuff (0ms)
\r
2772 //TimeTaker guiupdatetimer("Gui updating");
\r
2775 wchar_t temptext[150];
\r
2777 static float drawtime_avg = 0;
\r
2778 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2779 static float beginscenetime_avg = 0;
\r
2780 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2781 static float scenetime_avg = 0;
\r
2782 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2783 static float endscenetime_avg = 0;
\r
2784 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2786 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2788 L", R: range_all=%i"
\r
2790 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2792 draw_control.range_all,
\r
2794 beginscenetime_avg,
\r
2799 guitext->setText(temptext);
\r
2803 wchar_t temptext[150];
\r
2804 swprintf(temptext, 150,
\r
2805 L"(% .1f, % .1f, % .1f)"
\r
2806 L" (% .3f < btime_jitter < % .3f"
\r
2807 L", dtime_jitter = % .1f %%"
\r
2808 L", v_range = %.1f)",
\r
2809 player_position.X/BS,
\r
2810 player_position.Y/BS,
\r
2811 player_position.Z/BS,
\r
2812 busytime_jitter1_min_sample,
\r
2813 busytime_jitter1_max_sample,
\r
2814 dtime_jitter1_max_fraction * 100.0,
\r
2815 draw_control.wanted_range
\r
2818 guitext2->setText(temptext);
\r
2822 guitext_info->setText(infotext.c_str());
\r
2826 Get chat messages from client
\r
2829 // Get new messages
\r
2830 std::wstring message;
\r
2831 while(client.getChatMessage(message))
\r
2833 chat_lines.push_back(ChatLine(message));
\r
2834 /*if(chat_lines.size() > 6)
\r
2836 core::list<ChatLine>::Iterator
\r
2837 i = chat_lines.begin();
\r
2838 chat_lines.erase(i);
\r
2841 // Append them to form the whole static text and throw
\r
2842 // it to the gui element
\r
2843 std::wstring whole;
\r
2844 // This will correspond to the line number counted from
\r
2845 // top to bottom, from size-1 to 0
\r
2846 s16 line_number = chat_lines.size();
\r
2847 // Count of messages to be removed from the top
\r
2848 u16 to_be_removed_count = 0;
\r
2849 for(core::list<ChatLine>::Iterator
\r
2850 i = chat_lines.begin();
\r
2851 i != chat_lines.end(); i++)
\r
2853 // After this, line number is valid for this loop
\r
2856 (*i).age += dtime;
\r
2858 This results in a maximum age of 60*6 to the
\r
2859 lowermost line and a maximum of 6 lines
\r
2861 float allowed_age = (6-line_number) * 60.0;
\r
2863 if((*i).age > allowed_age)
\r
2865 to_be_removed_count++;
\r
2868 whole += (*i).text + L'\n';
\r
2870 for(u16 i=0; i<to_be_removed_count; i++)
\r
2872 core::list<ChatLine>::Iterator
\r
2873 it = chat_lines.begin();
\r
2874 chat_lines.erase(it);
\r
2876 guitext_chat->setText(whole.c_str());
\r
2877 // Update gui element size and position
\r
2878 core::rect<s32> rect(
\r
2880 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2881 screensize.X - 10,
\r
2884 guitext_chat->setRelativePosition(rect);
\r
2886 if(chat_lines.size() == 0)
\r
2887 guitext_chat->setVisible(false);
\r
2889 guitext_chat->setVisible(true);
\r
2896 static u16 old_selected_item = 65535;
\r
2897 if(client.getLocalInventoryUpdated()
\r
2898 || g_selected_item != old_selected_item)
\r
2900 old_selected_item = g_selected_item;
\r
2901 //std::cout<<"Updating local inventory"<<std::endl;
\r
2902 client.getLocalInventory(local_inventory);
\r
2903 quick_inventory->setSelection(g_selected_item);
\r
2904 quick_inventory->update();
\r
2908 Send actions returned by the inventory menu
\r
2910 while(inventory_action_queue.size() != 0)
\r
2912 InventoryAction *a = inventory_action_queue.pop_front();
\r
2914 client.sendInventoryAction(a);
\r
2923 TimeTaker drawtimer("Drawing");
\r
2927 TimeTaker timer("beginScene");
\r
2928 driver->beginScene(true, true, bgcolor);
\r
2929 //driver->beginScene(false, true, bgcolor);
\r
2930 beginscenetime = timer.stop(true);
\r
2935 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2938 TimeTaker timer("smgr");
\r
2940 scenetime = timer.stop(true);
\r
2944 //TimeTaker timer9("auxiliary drawings");
\r
2948 //TimeTaker //timer10("//timer10");
\r
2950 video::SMaterial m;
\r
2951 //m.Thickness = 10;
\r
2953 m.Lighting = false;
\r
2954 driver->setMaterial(m);
\r
2956 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2958 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2959 i != hilightboxes.end(); i++)
\r
2961 /*std::cout<<"hilightbox min="
\r
2962 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2964 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2966 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2972 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2973 displaycenter + core::vector2d<s32>(10,0),
\r
2974 video::SColor(255,255,255,255));
\r
2975 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2976 displaycenter + core::vector2d<s32>(0,10),
\r
2977 video::SColor(255,255,255,255));
\r
2982 //TimeTaker //timer11("//timer11");
\r
2988 guienv->drawAll();
\r
2992 TimeTaker timer("endScene");
\r
2993 driver->endScene();
\r
2994 endscenetime = timer.stop(true);
\r
2997 drawtime = drawtimer.stop(true);
\r
3003 static s16 lastFPS = 0;
\r
3004 //u16 fps = driver->getFPS();
\r
3005 u16 fps = (1.0/dtime_avg1);
\r
3007 if (lastFPS != fps)
\r
3009 core::stringw str = L"Minetest [";
\r
3010 str += driver->getName();
\r
3014 device->setWindowCaption(str.c_str());
\r
3020 device->yield();*/
\r
3023 delete quick_inventory;
\r
3026 Disable texture fetches and other stuff that is queued
\r
3027 to be processed by the main loop.
\r
3029 This has to be done before client goes out of scope.
\r
3031 g_irrlicht->Shutdown(true);
\r
3033 } // client and server are deleted at this point
\r
3036 catch(con::PeerNotFoundException &e)
\r
3038 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3039 error_message = L"Connection timed out.";
\r
3042 } // Menu-game loop
\r
3047 In the end, delete the Irrlicht device.
\r
3052 Update configuration file
\r
3054 /*if(configpath != "")
\r
3056 g_settings.updateConfigFile(configpath.c_str());
\r
3059 END_DEBUG_EXCEPTION_HANDLER
\r
3061 debugstreams_deinit();
\r