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
100 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
106 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
108 - The player could go faster by a crafting a boat, or riding an animal
\r
110 - Random NPC traders. what else?
\r
115 Build system / running:
\r
116 -----------------------
\r
118 FIXME: Some network errors on Windows that cause local game to not work
\r
119 - See siggjen's emails.
\r
120 - Is this the famous "windows 7 problem"?
\r
121 - Apparently there might be other errors too
\r
123 Networking and serialization:
\r
124 -----------------------------
\r
126 TODO: Get rid of GotSplitPacketException
\r
131 TODO: Add gui option to remove map
\r
133 TODO: Configuration menu, at least for keys
\r
138 TODO: Optimize day/night mesh updating somehow
\r
139 - create copies of all textures for all lighting values and only
\r
140 change texture for material?
\r
141 - Umm... the collecting of the faces is the slow part
\r
142 -> what about just changing the color values of the existing
\r
143 meshbuffers? It should go quite fast.
\r
144 - This is not easy; There'd need to be a buffer somewhere
\r
145 that would contain the night and day lighting values.
\r
146 - Actually if FastFaces would be stored, they could
\r
149 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
151 - That is >500 vertices
\r
152 - This is not easy; all the MapBlocks close to the player would
\r
153 still need to be drawn separately and combining the blocks
\r
154 would have to happen in a background thread
\r
156 TODO: Make fetching sector's blocks more efficient when rendering
\r
157 sectors that have very large amounts of blocks (on client)
\r
158 - Is this necessary at all?
\r
160 TODO: Flowing water animation
\r
162 NOTE(FIXED): A lock condition is possible:
\r
163 1) MapBlock::updateMesh() is called from client asynchronously:
\r
164 - AsyncProcessData() -> Map::updateMeshes()
\r
165 2) Asynchronous locks m_temp_mods_mutex
\r
166 3) MapBlock::updateMesh() is called from client synchronously:
\r
167 - Client::step() -> Environment::step()
\r
168 4) Synchronous starts waiting for m_temp_mods_mutex
\r
169 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
174 TODO: Make the video backend selectable
\r
179 TODO: Untie client network operations from framerate
\r
180 - Needs some input queues or something
\r
182 TODO: Make morning and evening transition more smooth and maybe shorter
\r
184 TODO: Don't update all meshes always on single node changes, but
\r
185 check which ones should be updated
\r
186 - implement Map::updateNodeMeshes()
\r
191 TODO: When player dies, throw items on map
\r
193 TODO: Make an option to the server to disable building and digging near
\r
194 the starting position
\r
196 TODO: Save players with inventories to disk
\r
197 TODO: Players to be saved as text in map/players/<name>
\r
199 TODO: Copy the text of the last picked sign to inventory in creative
\r
202 TODO: Check what goes wrong with caching map to disk (Kray)
\r
205 TODO: When server sees that client is removing an inexistent block to
\r
206 an existent position, resend the MapBlock.
\r
208 FIXME: Server went into some infinite PeerNotFoundException loop
\r
213 TODO: Better handling of objects and mobs
\r
215 - There has to be some way to do it with less messy code
\r
216 - Make separate classes for client and server
\r
217 - Client should not discriminate between blocks, server should
\r
218 - Make other players utilize the same framework
\r
219 - This is also needed for objects that don't get sent to client
\r
220 but are used for triggers etc
\r
222 TODO: There has to be some better way to handle static objects than to
\r
223 send them all the time. This affects signs and item objects.
\r
224 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
225 need an additional metadata field for the texts
\r
226 - This is also needed for item container chests
\r
228 Block object server side:
\r
229 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
230 - For all blocks in the buffer, objects are stepped(). This
\r
231 means they are active.
\r
232 - TODO: A global active buffer is needed for the server
\r
233 - TODO: A timestamp to blocks
\r
234 - TODO: All blocks going in and out of the buffer are recorded.
\r
235 - TODO: For outgoing blocks, timestamp is written.
\r
236 - TODO: For incoming blocks, time difference is calculated and
\r
237 objects are stepped according to it.
\r
242 TODO: Mineral and ground material properties
\r
243 - This way mineral ground toughness can be calculated with just
\r
244 some formula, as well as tool strengths
\r
246 TODO: Flowing water to actually contain flow direction information
\r
248 TODO: Remove duplicate lighting implementation from Map (leave
\r
249 VoxelManipulator, which is faster)
\r
251 FEATURE: Create a system that allows a huge amount of different "map
\r
252 generator modules/filters"
\r
254 FEATURE: Erosion simulation at map generation time
\r
255 - Simulate water flows, which would carve out dirt fast and
\r
256 then turn stone into gravel and sand and relocate it.
\r
257 - How about relocating minerals, too? Coal and gold in
\r
258 downstream sand and gravel would be kind of cool
\r
259 - This would need a better way of handling minerals, mainly
\r
260 to have mineral content as a separate field. the first
\r
261 parameter field is free for this.
\r
262 - Simulate rock falling from cliffs when water has removed
\r
263 enough solid rock from the bottom
\r
265 Doing now (most important at the top):
\r
266 --------------------------------------
\r
270 === Stuff to do before release
\r
271 * Save the new mapgen stuff
\r
272 - map/meta.txt, which should contain only plain text, something like this:
\r
273 seed = 7ff1bafcd7118800
\r
275 - map/chunks.dat: chunk positions and flags in binary format
\r
276 * Make server find the spawning place from the real map data, not from
\r
278 - But the changing borders of chunk have to be avoided, because
\r
279 there is time to generate only one chunk.
\r
280 * Make the generator to run in background and not blocking block
\r
281 placement and transfer
\r
282 * only_from_disk might not work anymore - check and fix it.
\r
283 * Check the fixmes in the list above
\r
285 === Making it more portable
\r
286 * MinGW: Switch away from swprintf; mingw has a bad version of it.
\r
287 Use snprintf + narrow_to_wide or (w)ostringstream
\r
288 * Some MSVC: std::sto* are defined without a namespace and collide
\r
289 with the ones in utility.h
\r
291 === Stuff to do after release
\r
292 * Make an "environment metafile" to store at least time of day
\r
293 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
294 - Or maybe move content_features to material.{h,cpp}?
\r
295 * Add some kind of erosion and other stuff that now is possible
\r
296 * Make client to fetch stuff asynchronously
\r
297 - Needs method SyncProcessData
\r
298 * Fix the problem with the server constantly saving one or a few
\r
299 blocks? List the first saved block, maybe it explains.
\r
300 - It is probably caused by oscillating water
\r
301 * Water doesn't start flowing after map generation like it should
\r
302 - Are there still problems?
\r
303 * Better water generation (spread it to underwater caverns but don't
\r
304 fill dungeons that don't touch big water masses)
\r
305 * When generating a chunk and the neighboring chunk doesn't have mud
\r
306 and stuff yet and the ground is fairly flat, the mud will flow to
\r
307 the other chunk making nasty straight walls when the other chunk
\r
308 is generated. Fix it.
\r
309 * Make a small history check to transformLiquids to detect and log
\r
310 continuous oscillations, in such detail that they can be fixed.
\r
311 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
313 ======================================================================
\r
318 Setting this to 1 enables a special camera mode that forces
\r
319 the renderers to think that the camera statically points from
\r
320 the starting place to a static direction.
\r
322 This allows one to move around with the player and see what
\r
323 is actually drawn behind solid things and behind the player.
\r
325 #define FIELD_OF_VIEW_TEST 0
\r
329 #pragma message ("Disabling unit tests")
\r
331 #warning "Disabling unit tests"
\r
333 // Disable unit tests
\r
334 #define ENABLE_TESTS 0
\r
336 // Enable unit tests
\r
337 #define ENABLE_TESTS 1
\r
341 #pragma comment(lib, "Irrlicht.lib")
\r
342 //#pragma comment(lib, "jthread.lib")
\r
343 #pragma comment(lib, "zlibwapi.lib")
\r
344 #pragma comment(lib, "Shell32.lib")
\r
345 // This would get rid of the console window
\r
346 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
349 #include <iostream>
\r
351 #include <jmutexautolock.h>
\r
352 #include <locale.h>
\r
353 #include "common_irrlicht.h"
\r
356 #include "player.h"
\r
359 #include "environment.h"
\r
360 #include "server.h"
\r
361 #include "client.h"
\r
362 #include "serialization.h"
\r
363 #include "constants.h"
\r
364 #include "strfnd.h"
\r
365 #include "porting.h"
\r
366 #include "irrlichtwrapper.h"
\r
367 #include "gettime.h"
\r
368 #include "porting.h"
\r
369 #include "guiPauseMenu.h"
\r
370 #include "guiInventoryMenu.h"
\r
371 #include "guiTextInputMenu.h"
\r
372 #include "materials.h"
\r
373 #include "guiMessageMenu.h"
\r
374 #include "filesys.h"
\r
375 #include "config.h"
\r
376 #include "guiMainMenu.h"
\r
377 #include "mineral.h"
\r
381 // TODO: Remove this
\r
382 IrrlichtWrapper *g_irrlicht = NULL;
\r
384 // This makes textures
\r
385 ITextureSource *g_texturesource = NULL;
\r
387 MapDrawControl draw_control;
\r
391 These are loaded from the config file.
\r
394 Settings g_settings;
\r
396 extern void set_default_settings();
\r
402 IrrlichtDevice *g_device = NULL;
\r
403 Client *g_client = NULL;
\r
409 gui::IGUIEnvironment* guienv = NULL;
\r
410 gui::IGUIStaticText *guiroot = NULL;
\r
412 class MainMenuManager : public IMenuManager
\r
415 virtual void createdMenu(GUIModalMenu *menu)
\r
417 for(core::list<GUIModalMenu*>::Iterator
\r
418 i = m_stack.begin();
\r
419 i != m_stack.end(); i++)
\r
421 assert(*i != menu);
\r
424 if(m_stack.size() != 0)
\r
425 (*m_stack.getLast())->setVisible(false);
\r
426 m_stack.push_back(menu);
\r
429 virtual void deletingMenu(GUIModalMenu *menu)
\r
431 // Remove all entries if there are duplicates
\r
432 bool removed_entry;
\r
434 removed_entry = false;
\r
435 for(core::list<GUIModalMenu*>::Iterator
\r
436 i = m_stack.begin();
\r
437 i != m_stack.end(); i++)
\r
442 removed_entry = true;
\r
446 }while(removed_entry);
\r
448 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
449 assert(*i == menu);
\r
450 m_stack.erase(i);*/
\r
452 if(m_stack.size() != 0)
\r
453 (*m_stack.getLast())->setVisible(true);
\r
458 return m_stack.size();
\r
461 core::list<GUIModalMenu*> m_stack;
\r
464 MainMenuManager g_menumgr;
\r
466 bool noMenuActive()
\r
468 return (g_menumgr.menuCount() == 0);
\r
471 bool g_disconnect_requested = false;
\r
473 class MainGameCallback : public IGameCallback
\r
476 virtual void exitToOS()
\r
478 g_device->closeDevice();
\r
481 virtual void disconnect()
\r
483 g_disconnect_requested = true;
\r
487 MainGameCallback g_gamecallback;
\r
489 // Inventory actions from the menu are buffered here before sending
\r
490 Queue<InventoryAction*> inventory_action_queue;
\r
491 // This is a copy of the inventory that the client's environment has
\r
492 Inventory local_inventory;
\r
494 u16 g_selected_item = 0;
\r
501 std::ostream *dout_con_ptr = &dummyout;
\r
502 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
503 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
504 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
505 //std::ostream *dout_con_ptr = &dstream;
\r
506 //std::ostream *derr_con_ptr = &dstream;
\r
509 std::ostream *dout_server_ptr = &dstream;
\r
510 std::ostream *derr_server_ptr = &dstream;
\r
513 std::ostream *dout_client_ptr = &dstream;
\r
514 std::ostream *derr_client_ptr = &dstream;
\r
517 gettime.h implementation
\r
523 Use irrlicht because it is more precise than porting.h's
\r
526 if(g_irrlicht == NULL)
\r
528 return g_irrlicht->getTime();
\r
535 struct TextDestSign : public TextDest
\r
537 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
539 m_blockpos = blockpos;
\r
543 void gotText(std::wstring text)
\r
545 std::string ntext = wide_to_narrow(text);
\r
546 dstream<<"Changing text of a sign object: "
\r
547 <<ntext<<std::endl;
\r
548 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
556 struct TextDestChat : public TextDest
\r
558 TextDestChat(Client *client)
\r
562 void gotText(std::wstring text)
\r
564 m_client->sendChatMessage(text);
\r
565 m_client->addChatMessage(text);
\r
571 class MyEventReceiver : public IEventReceiver
\r
574 // This is the one method that we have to implement
\r
575 virtual bool OnEvent(const SEvent& event)
\r
578 React to nothing here if a menu is active
\r
580 if(noMenuActive() == false)
\r
586 // Remember whether each key is down or up
\r
587 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
589 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
591 if(event.KeyInput.PressedDown)
\r
593 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
599 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
601 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
603 dstream<<DTIME<<"MyEventReceiver: "
\r
604 <<"Launching pause menu"<<std::endl;
\r
605 // It will delete itself by itself
\r
606 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
607 &g_menumgr))->drop();
\r
610 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
612 dstream<<DTIME<<"MyEventReceiver: "
\r
613 <<"Launching inventory"<<std::endl;
\r
614 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
615 &local_inventory, &inventory_action_queue,
\r
616 &g_menumgr))->drop();
\r
619 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
621 TextDest *dest = new TextDestChat(g_client);
\r
623 (new GUITextInputMenu(guienv, guiroot, -1,
\r
629 // Material selection
\r
630 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
632 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
635 g_selected_item = 0;
\r
636 dstream<<DTIME<<"Selected item: "
\r
637 <<g_selected_item<<std::endl;
\r
640 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
641 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
643 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
644 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
646 if(s1 < PLAYER_INVENTORY_SIZE)
\r
647 g_selected_item = s1-1;
\r
648 dstream<<DTIME<<"Selected item: "
\r
649 <<g_selected_item<<std::endl;
\r
652 // Viewing range selection
\r
653 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
655 if(draw_control.range_all)
\r
657 draw_control.range_all = false;
\r
658 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
662 draw_control.range_all = true;
\r
663 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
667 // Print debug stacks
\r
668 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
670 dstream<<"-----------------------------------------"
\r
672 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
673 dstream<<"-----------------------------------------"
\r
675 debug_stacks_print();
\r
680 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
682 if(noMenuActive() == false)
\r
684 left_active = false;
\r
685 middle_active = false;
\r
686 right_active = false;
\r
690 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
691 left_active = event.MouseInput.isLeftPressed();
\r
692 middle_active = event.MouseInput.isMiddlePressed();
\r
693 right_active = event.MouseInput.isRightPressed();
\r
695 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
697 leftclicked = true;
\r
699 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
701 rightclicked = true;
\r
703 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
705 leftreleased = true;
\r
707 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
709 rightreleased = true;
\r
711 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
713 /*dstream<<"event.MouseInput.Wheel="
\r
714 <<event.MouseInput.Wheel<<std::endl;*/
\r
715 if(event.MouseInput.Wheel < 0)
\r
717 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
720 g_selected_item = 0;
\r
722 else if(event.MouseInput.Wheel > 0)
\r
724 if(g_selected_item > 0)
\r
727 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
736 // This is used to check whether a key is being held down
\r
737 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
739 return keyIsDown[keyCode];
\r
744 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
745 keyIsDown[i] = false;
\r
747 leftclicked = false;
\r
748 rightclicked = false;
\r
749 leftreleased = false;
\r
750 rightreleased = false;
\r
752 left_active = false;
\r
753 middle_active = false;
\r
754 right_active = false;
\r
765 bool rightreleased;
\r
768 bool middle_active;
\r
772 // We use this array to store the current state of each key
\r
773 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
776 IrrlichtDevice *m_device;
\r
785 virtual ~InputHandler()
\r
789 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
791 virtual v2s32 getMousePos() = 0;
\r
792 virtual void setMousePos(s32 x, s32 y) = 0;
\r
794 virtual bool getLeftState() = 0;
\r
795 virtual bool getRightState() = 0;
\r
797 virtual bool getLeftClicked() = 0;
\r
798 virtual bool getRightClicked() = 0;
\r
799 virtual void resetLeftClicked() = 0;
\r
800 virtual void resetRightClicked() = 0;
\r
802 virtual bool getLeftReleased() = 0;
\r
803 virtual bool getRightReleased() = 0;
\r
804 virtual void resetLeftReleased() = 0;
\r
805 virtual void resetRightReleased() = 0;
\r
807 virtual void step(float dtime) {};
\r
809 virtual void clear() {};
\r
812 InputHandler *g_input = NULL;
\r
814 class RealInputHandler : public InputHandler
\r
817 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
819 m_receiver(receiver)
\r
822 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
824 return m_receiver->IsKeyDown(keyCode);
\r
826 virtual v2s32 getMousePos()
\r
828 return m_device->getCursorControl()->getPosition();
\r
830 virtual void setMousePos(s32 x, s32 y)
\r
832 m_device->getCursorControl()->setPosition(x, y);
\r
835 virtual bool getLeftState()
\r
837 return m_receiver->left_active;
\r
839 virtual bool getRightState()
\r
841 return m_receiver->right_active;
\r
844 virtual bool getLeftClicked()
\r
846 return m_receiver->leftclicked;
\r
848 virtual bool getRightClicked()
\r
850 return m_receiver->rightclicked;
\r
852 virtual void resetLeftClicked()
\r
854 m_receiver->leftclicked = false;
\r
856 virtual void resetRightClicked()
\r
858 m_receiver->rightclicked = false;
\r
861 virtual bool getLeftReleased()
\r
863 return m_receiver->leftreleased;
\r
865 virtual bool getRightReleased()
\r
867 return m_receiver->rightreleased;
\r
869 virtual void resetLeftReleased()
\r
871 m_receiver->leftreleased = false;
\r
873 virtual void resetRightReleased()
\r
875 m_receiver->rightreleased = false;
\r
880 resetRightClicked();
\r
881 resetLeftClicked();
\r
884 IrrlichtDevice *m_device;
\r
885 MyEventReceiver *m_receiver;
\r
888 class RandomInputHandler : public InputHandler
\r
891 RandomInputHandler()
\r
893 leftclicked = false;
\r
894 rightclicked = false;
\r
895 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
896 keydown[i] = false;
\r
898 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
900 return keydown[keyCode];
\r
902 virtual v2s32 getMousePos()
\r
906 virtual void setMousePos(s32 x, s32 y)
\r
908 mousepos = v2s32(x,y);
\r
911 virtual bool getLeftState()
\r
915 virtual bool getRightState()
\r
920 virtual bool getLeftClicked()
\r
922 return leftclicked;
\r
924 virtual bool getRightClicked()
\r
926 return rightclicked;
\r
928 virtual void resetLeftClicked()
\r
930 leftclicked = false;
\r
932 virtual void resetRightClicked()
\r
934 rightclicked = false;
\r
937 virtual bool getLeftReleased()
\r
941 virtual bool getRightReleased()
\r
945 virtual void resetLeftReleased()
\r
948 virtual void resetRightReleased()
\r
952 virtual void step(float dtime)
\r
955 static float counter1 = 0;
\r
959 counter1 = 0.1*Rand(1,10);
\r
960 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
961 g_selected_material++;
\r
963 g_selected_material = 0;*/
\r
964 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
967 g_selected_item = 0;
\r
971 static float counter1 = 0;
\r
975 counter1 = 0.1*Rand(1, 40);
\r
976 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
980 static float counter1 = 0;
\r
984 counter1 = 0.1*Rand(1, 40);
\r
985 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
989 static float counter1 = 0;
\r
993 counter1 = 0.1*Rand(1, 40);
\r
994 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
998 static float counter1 = 0;
\r
1000 if(counter1 < 0.0)
\r
1002 counter1 = 0.1*Rand(1, 40);
\r
1003 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1007 static float counter1 = 0;
\r
1008 counter1 -= dtime;
\r
1009 if(counter1 < 0.0)
\r
1011 counter1 = 0.1*Rand(1, 20);
\r
1012 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1016 static float counter1 = 0;
\r
1017 counter1 -= dtime;
\r
1018 if(counter1 < 0.0)
\r
1020 counter1 = 0.1*Rand(1, 30);
\r
1021 leftclicked = true;
\r
1025 static float counter1 = 0;
\r
1026 counter1 -= dtime;
\r
1027 if(counter1 < 0.0)
\r
1029 counter1 = 0.1*Rand(1, 20);
\r
1030 rightclicked = true;
\r
1033 mousepos += mousespeed;
\r
1036 s32 Rand(s32 min, s32 max)
\r
1038 return (myrand()%(max-min+1))+min;
\r
1041 bool keydown[KEY_KEY_CODES_COUNT];
\r
1045 bool rightclicked;
\r
1048 void updateViewingRange(f32 frametime_in, Client *client)
\r
1050 if(draw_control.range_all == true)
\r
1053 static f32 added_frametime = 0;
\r
1054 static s16 added_frames = 0;
\r
1056 added_frametime += frametime_in;
\r
1057 added_frames += 1;
\r
1059 // Actually this counter kind of sucks because frametime is busytime
\r
1060 static f32 counter = 0;
\r
1061 counter -= frametime_in;
\r
1067 /*dstream<<__FUNCTION_NAME
\r
1068 <<": Collected "<<added_frames<<" frames, total of "
\r
1069 <<added_frametime<<"s."<<std::endl;*/
\r
1071 /*dstream<<"draw_control.blocks_drawn="
\r
1072 <<draw_control.blocks_drawn
\r
1073 <<", draw_control.blocks_would_have_drawn="
\r
1074 <<draw_control.blocks_would_have_drawn
\r
1077 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1078 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1080 draw_control.wanted_min_range = range_min;
\r
1081 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1083 float block_draw_ratio = 1.0;
\r
1084 if(draw_control.blocks_would_have_drawn != 0)
\r
1086 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1087 / (float)draw_control.blocks_would_have_drawn;
\r
1090 // Calculate the average frametime in the case that all wanted
\r
1091 // blocks had been drawn
\r
1092 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1094 added_frametime = 0.0;
\r
1097 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1098 float wanted_frametime = 1.0 / wanted_fps;
\r
1100 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1101 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1103 // If needed frametime change is very small, just return
\r
1104 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1106 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1110 float range = draw_control.wanted_range;
\r
1111 float new_range = range;
\r
1113 static s16 range_old = 0;
\r
1114 static f32 frametime_old = 0;
\r
1116 float d_range = range - range_old;
\r
1117 f32 d_frametime = frametime - frametime_old;
\r
1118 // A sane default of 30ms per 50 nodes of range
\r
1119 static f32 time_per_range = 30. / 50;
\r
1122 time_per_range = d_frametime / d_range;
\r
1125 // The minimum allowed calculated frametime-range derivative:
\r
1126 // Practically this sets the maximum speed of changing the range.
\r
1127 // The lower this value, the higher the maximum changing speed.
\r
1128 // A low value here results in wobbly range (0.001)
\r
1129 // A high value here results in slow changing range (0.0025)
\r
1130 // SUGG: This could be dynamically adjusted so that when
\r
1131 // the camera is turning, this is lower
\r
1132 //float min_time_per_range = 0.0015;
\r
1133 float min_time_per_range = 0.0010;
\r
1134 //float min_time_per_range = 0.05 / range;
\r
1135 if(time_per_range < min_time_per_range)
\r
1137 time_per_range = min_time_per_range;
\r
1138 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1142 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1145 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1146 // Dampen the change a bit to kill oscillations
\r
1147 //wanted_range_change *= 0.9;
\r
1148 //wanted_range_change *= 0.75;
\r
1149 wanted_range_change *= 0.5;
\r
1150 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1152 // If needed range change is very small, just return
\r
1153 if(fabs(wanted_range_change) < 0.001)
\r
1155 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1159 new_range += wanted_range_change;
\r
1160 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1162 //float new_range_unclamped = new_range;
\r
1163 if(new_range < range_min)
\r
1164 new_range = range_min;
\r
1165 if(new_range > range_max)
\r
1166 new_range = range_max;
\r
1168 /*if(new_range != new_range_unclamped)
\r
1169 dstream<<", clamped to "<<new_range<<std::endl;
\r
1171 dstream<<std::endl;*/
\r
1173 draw_control.wanted_range = new_range;
\r
1175 range_old = new_range;
\r
1176 frametime_old = frametime;
\r
1179 class GUIQuickInventory : public IEventReceiver
\r
1182 GUIQuickInventory(
\r
1183 gui::IGUIEnvironment* env,
\r
1184 gui::IGUIElement* parent,
\r
1187 Inventory *inventory):
\r
1188 m_itemcount(itemcount),
\r
1189 m_inventory(inventory)
\r
1191 core::rect<s32> imgsize(0,0,48,48);
\r
1192 core::rect<s32> textsize(0,0,48,16);
\r
1193 v2s32 spacing(0, 64);
\r
1194 for(s32 i=0; i<m_itemcount; i++)
\r
1196 m_images.push_back(env->addImage(
\r
1197 imgsize + pos + spacing*i
\r
1199 m_images[i]->setScaleImage(true);
\r
1200 m_texts.push_back(env->addStaticText(
\r
1202 textsize + pos + spacing*i,
\r
1205 m_texts[i]->setBackgroundColor(
\r
1206 video::SColor(128,0,0,0));
\r
1207 m_texts[i]->setTextAlignment(
\r
1208 gui::EGUIA_CENTER,
\r
1209 gui::EGUIA_UPPERLEFT);
\r
1213 ~GUIQuickInventory()
\r
1215 for(u32 i=0; i<m_texts.size(); i++)
\r
1217 m_texts[i]->remove();
\r
1219 for(u32 i=0; i<m_images.size(); i++)
\r
1221 m_images[i]->remove();
\r
1225 virtual bool OnEvent(const SEvent& event)
\r
1230 void setSelection(s32 i)
\r
1239 start = m_selection - m_itemcount / 2;
\r
1241 InventoryList *mainlist = m_inventory->getList("main");
\r
1243 for(s32 i=0; i<m_itemcount; i++)
\r
1245 s32 j = i + start;
\r
1247 if(j > (s32)mainlist->getSize() - 1)
\r
1248 j -= mainlist->getSize();
\r
1250 j += mainlist->getSize();
\r
1252 InventoryItem *item = mainlist->getItem(j);
\r
1256 m_images[i]->setImage(NULL);
\r
1259 if(m_selection == j)
\r
1260 swprintf(t, 10, L"<-");
\r
1262 swprintf(t, 10, L"");
\r
1263 m_texts[i]->setText(t);
\r
1265 // The next ifs will segfault with a NULL pointer
\r
1270 m_images[i]->setImage(item->getImage());
\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
2015 camera->setFOV(FOV_ANGLE);
\r
2017 // Just so big a value that everything rendered is visible
\r
2018 camera->setFarValue(100000*BS);
\r
2020 f32 camera_yaw = 0; // "right/left"
\r
2021 f32 camera_pitch = 0; // "up/down"
\r
2027 gui_loadingtext->remove();
\r
2030 Add some gui stuff
\r
2033 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2034 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2036 // Test the text input system
\r
2037 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2039 /*GUIMessageMenu *menu =
\r
2040 new GUIMessageMenu(guienv, guiroot, -1,
\r
2045 // Launch pause menu
\r
2046 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2047 &g_menumgr))->drop();
\r
2050 guitext2->setVisible(true);
\r
2051 guitext_info->setVisible(true);
\r
2052 guitext_chat->setVisible(true);
\r
2055 Some statistics are collected in these
\r
2058 u32 beginscenetime = 0;
\r
2059 u32 scenetime = 0;
\r
2060 u32 endscenetime = 0;
\r
2063 //throw con::PeerNotFoundException("lol");
\r
2065 core::list<float> frametime_log;
\r
2071 bool first_loop_after_window_activation = true;
\r
2073 // Time is in milliseconds
\r
2074 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2075 // NOTE: So we have to use getTime() and call run()s between them
\r
2076 u32 lasttime = device->getTimer()->getTime();
\r
2078 while(device->run())
\r
2080 if(g_disconnect_requested)
\r
2082 g_disconnect_requested = false;
\r
2087 Run global IrrlichtWrapper's main thread processing stuff
\r
2089 g_irrlicht->Run();
\r
2092 Process TextureSource's queue
\r
2094 texturesource->processQueue();
\r
2097 Random calculations
\r
2099 v2u32 screensize = driver->getScreenSize();
\r
2100 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2102 // Hilight boxes collected during the loop and displayed
\r
2103 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2106 std::wstring infotext;
\r
2108 //TimeTaker //timer1("//timer1");
\r
2110 // Time of frame without fps limit
\r
2114 // not using getRealTime is necessary for wine
\r
2115 u32 time = device->getTimer()->getTime();
\r
2116 if(time > lasttime)
\r
2117 busytime_u32 = time - lasttime;
\r
2120 busytime = busytime_u32 / 1000.0;
\r
2123 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2125 // Absolutelu necessary for wine!
\r
2132 updateViewingRange(busytime, &client);
\r
2139 float fps_max = g_settings.getFloat("fps_max");
\r
2140 u32 frametime_min = 1000./fps_max;
\r
2142 if(busytime_u32 < frametime_min)
\r
2144 u32 sleeptime = frametime_min - busytime_u32;
\r
2145 device->sleep(sleeptime);
\r
2149 // Absolutelu necessary for wine!
\r
2153 Time difference calculation
\r
2155 f32 dtime; // in seconds
\r
2157 u32 time = device->getTimer()->getTime();
\r
2158 if(time > lasttime)
\r
2159 dtime = (time - lasttime) / 1000.0;
\r
2165 Log frametime for visualization
\r
2167 frametime_log.push_back(dtime);
\r
2168 if(frametime_log.size() > 100)
\r
2170 core::list<float>::Iterator i = frametime_log.begin();
\r
2171 frametime_log.erase(i);
\r
2175 Visualize frametime in terminal
\r
2177 /*for(u32 i=0; i<dtime*400; i++)
\r
2179 std::cout<<std::endl;*/
\r
2182 Time average and jitter calculation
\r
2185 static f32 dtime_avg1 = 0.0;
\r
2186 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2187 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2189 static f32 dtime_jitter1_max_sample = 0.0;
\r
2190 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2192 static f32 jitter1_max = 0.0;
\r
2193 static f32 counter = 0.0;
\r
2194 if(dtime_jitter1 > jitter1_max)
\r
2195 jitter1_max = dtime_jitter1;
\r
2200 dtime_jitter1_max_sample = jitter1_max;
\r
2201 dtime_jitter1_max_fraction
\r
2202 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2203 jitter1_max = 0.0;
\r
2208 Busytime average and jitter calculation
\r
2211 static f32 busytime_avg1 = 0.0;
\r
2212 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2213 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2215 static f32 busytime_jitter1_max_sample = 0.0;
\r
2216 static f32 busytime_jitter1_min_sample = 0.0;
\r
2218 static f32 jitter1_max = 0.0;
\r
2219 static f32 jitter1_min = 0.0;
\r
2220 static f32 counter = 0.0;
\r
2221 if(busytime_jitter1 > jitter1_max)
\r
2222 jitter1_max = busytime_jitter1;
\r
2223 if(busytime_jitter1 < jitter1_min)
\r
2224 jitter1_min = busytime_jitter1;
\r
2226 if(counter > 0.0){
\r
2228 busytime_jitter1_max_sample = jitter1_max;
\r
2229 busytime_jitter1_min_sample = jitter1_min;
\r
2230 jitter1_max = 0.0;
\r
2231 jitter1_min = 0.0;
\r
2236 Debug info for client
\r
2239 static float counter = 0.0;
\r
2244 client.printDebugInfo(std::cout);
\r
2249 Input handler step()
\r
2251 g_input->step(dtime);
\r
2254 Player speed control
\r
2263 bool a_superspeed,
\r
2266 PlayerControl control(
\r
2267 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2268 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2269 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2270 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2271 g_input->isKeyDown(irr::KEY_SPACE),
\r
2272 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2276 client.setPlayerControl(control);
\r
2280 Process environment
\r
2284 //TimeTaker timer("client.step(dtime)");
\r
2285 client.step(dtime);
\r
2286 //client.step(dtime_avg1);
\r
2289 if(server != NULL)
\r
2291 //TimeTaker timer("server->step(dtime)");
\r
2292 server->step(dtime);
\r
2295 v3f player_position = client.getPlayerPosition();
\r
2297 //TimeTaker //timer2("//timer2");
\r
2300 Mouse and camera control
\r
2303 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2306 device->getCursorControl()->setVisible(false);
\r
2308 if(first_loop_after_window_activation){
\r
2309 //std::cout<<"window active, first loop"<<std::endl;
\r
2310 first_loop_after_window_activation = false;
\r
2313 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2314 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2315 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2316 camera_yaw -= dx*0.2;
\r
2317 camera_pitch += dy*0.2;
\r
2318 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2319 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2321 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2324 device->getCursorControl()->setVisible(true);
\r
2326 //std::cout<<"window inactive"<<std::endl;
\r
2327 first_loop_after_window_activation = true;
\r
2330 camera_yaw = wrapDegrees(camera_yaw);
\r
2331 camera_pitch = wrapDegrees(camera_pitch);
\r
2333 v3f camera_direction = v3f(0,0,1);
\r
2334 camera_direction.rotateYZBy(camera_pitch);
\r
2335 camera_direction.rotateXZBy(camera_yaw);
\r
2337 // This is at the height of the eyes of the current figure
\r
2338 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2339 // This is more like in minecraft
\r
2340 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2342 camera->setPosition(camera_position);
\r
2343 // *100.0 helps in large map coordinates
\r
2344 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2346 if(FIELD_OF_VIEW_TEST){
\r
2347 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2348 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2351 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2352 //TimeTaker timer("client.updateCamera");
\r
2353 client.updateCamera(camera_position, camera_direction);
\r
2357 //TimeTaker //timer3("//timer3");
\r
2360 Calculate what block is the crosshair pointing to
\r
2363 //u32 t1 = device->getTimer()->getRealTime();
\r
2365 //f32 d = 4; // max. distance
\r
2366 f32 d = 4; // max. distance
\r
2367 core::line3d<f32> shootline(camera_position,
\r
2368 camera_position + camera_direction * BS * (d+1));
\r
2370 MapBlockObject *selected_object = client.getSelectedObject
\r
2371 (d*BS, camera_position, shootline);
\r
2374 If it's pointing to a MapBlockObject
\r
2377 if(selected_object != NULL)
\r
2379 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2381 core::aabbox3d<f32> box_on_map
\r
2382 = selected_object->getSelectionBoxOnMap();
\r
2384 hilightboxes.push_back(box_on_map);
\r
2386 infotext = narrow_to_wide(selected_object->infoText());
\r
2388 if(g_input->getLeftClicked())
\r
2390 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2391 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2392 selected_object->getId(), g_selected_item);
\r
2394 else if(g_input->getRightClicked())
\r
2396 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2398 Check if we want to modify the object ourselves
\r
2400 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2402 dstream<<"Sign object right-clicked"<<std::endl;
\r
2404 if(random_input == false)
\r
2406 // Get a new text for it
\r
2408 TextDest *dest = new TextDestSign(
\r
2409 selected_object->getBlock()->getPos(),
\r
2410 selected_object->getId(),
\r
2413 SignObject *sign_object = (SignObject*)selected_object;
\r
2415 std::wstring wtext =
\r
2416 narrow_to_wide(sign_object->getText());
\r
2418 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2424 Otherwise pass the event to the server as-is
\r
2428 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2429 selected_object->getId(), g_selected_item);
\r
2433 else // selected_object == NULL
\r
2437 Find out which node we are pointing at
\r
2440 bool nodefound = false;
\r
2442 v3s16 neighbourpos;
\r
2443 core::aabbox3d<f32> nodehilightbox;
\r
2444 f32 mindistance = BS * 1001;
\r
2446 v3s16 pos_i = floatToInt(player_position);
\r
2448 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2452 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2453 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2454 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2455 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2456 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2457 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2459 for(s16 y = ystart; y <= yend; y++)
\r
2460 for(s16 z = zstart; z <= zend; z++)
\r
2461 for(s16 x = xstart; x <= xend; x++)
\r
2466 n = client.getNode(v3s16(x,y,z));
\r
2467 if(content_pointable(n.d) == false)
\r
2470 catch(InvalidPositionException &e)
\r
2476 v3f npf = intToFloat(np);
\r
2481 v3s16(0,0,1), // back
\r
2482 v3s16(0,1,0), // top
\r
2483 v3s16(1,0,0), // right
\r
2484 v3s16(0,0,-1), // front
\r
2485 v3s16(0,-1,0), // bottom
\r
2486 v3s16(-1,0,0), // left
\r
2492 if(n.d == CONTENT_TORCH)
\r
2494 v3s16 dir = unpackDir(n.dir);
\r
2495 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2496 dir_f *= BS/2 - BS/6 - BS/20;
\r
2497 v3f cpf = npf + dir_f;
\r
2498 f32 distance = (cpf - camera_position).getLength();
\r
2500 core::aabbox3d<f32> box;
\r
2503 if(dir == v3s16(0,-1,0))
\r
2505 box = core::aabbox3d<f32>(
\r
2506 npf - v3f(BS/6, BS/2, BS/6),
\r
2507 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2511 else if(dir == v3s16(0,1,0))
\r
2513 box = core::aabbox3d<f32>(
\r
2514 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2515 npf + v3f(BS/6, BS/2, BS/6)
\r
2521 box = core::aabbox3d<f32>(
\r
2522 cpf - v3f(BS/6, BS/3, BS/6),
\r
2523 cpf + v3f(BS/6, BS/3, BS/6)
\r
2527 if(distance < mindistance)
\r
2529 if(box.intersectsWithLine(shootline))
\r
2533 neighbourpos = np;
\r
2534 mindistance = distance;
\r
2535 nodehilightbox = box;
\r
2544 for(u16 i=0; i<6; i++)
\r
2546 v3f dir_f = v3f(dirs[i].X,
\r
2547 dirs[i].Y, dirs[i].Z);
\r
2548 v3f centerpoint = npf + dir_f * BS/2;
\r
2550 (centerpoint - camera_position).getLength();
\r
2552 if(distance < mindistance)
\r
2554 core::CMatrix4<f32> m;
\r
2555 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2557 // This is the back face
\r
2558 v3f corners[2] = {
\r
2559 v3f(BS/2, BS/2, BS/2),
\r
2560 v3f(-BS/2, -BS/2, BS/2+d)
\r
2563 for(u16 j=0; j<2; j++)
\r
2565 m.rotateVect(corners[j]);
\r
2566 corners[j] += npf;
\r
2569 core::aabbox3d<f32> facebox(corners[0]);
\r
2570 facebox.addInternalPoint(corners[1]);
\r
2572 if(facebox.intersectsWithLine(shootline))
\r
2576 neighbourpos = np + dirs[i];
\r
2577 mindistance = distance;
\r
2579 //nodehilightbox = facebox;
\r
2581 const float d = 0.502;
\r
2582 core::aabbox3d<f32> nodebox
\r
2583 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2584 v3f nodepos_f = intToFloat(nodepos);
\r
2585 nodebox.MinEdge += nodepos_f;
\r
2586 nodebox.MaxEdge += nodepos_f;
\r
2587 nodehilightbox = nodebox;
\r
2589 } // if distance < mindistance
\r
2591 } // regular block
\r
2594 static float nodig_delay_counter = 0.0;
\r
2598 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2600 static float dig_time = 0.0;
\r
2601 static u16 dig_index = 0;
\r
2603 // Visualize selection
\r
2605 hilightboxes.push_back(nodehilightbox);
\r
2609 if(g_input->getLeftReleased())
\r
2611 client.clearTempMod(nodepos);
\r
2615 if(nodig_delay_counter > 0.0)
\r
2617 nodig_delay_counter -= dtime;
\r
2621 if(nodepos != nodepos_old)
\r
2623 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2624 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2626 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2628 client.clearTempMod(nodepos_old);
\r
2633 if(g_input->getLeftClicked() ||
\r
2634 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2636 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2637 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2639 if(g_input->getLeftClicked())
\r
2641 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2643 if(g_input->getLeftState())
\r
2645 MapNode n = client.getNode(nodepos);
\r
2647 // Get tool name. Default is "" = bare hands
\r
2648 std::string toolname = "";
\r
2649 InventoryList *mlist = local_inventory.getList("main");
\r
2652 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2653 if(item && (std::string)item->getName() == "ToolItem")
\r
2655 ToolItem *titem = (ToolItem*)item;
\r
2656 toolname = titem->getToolName();
\r
2660 // Get digging properties for material and tool
\r
2661 u8 material = n.d;
\r
2662 DiggingProperties prop =
\r
2663 getDiggingProperties(material, toolname);
\r
2665 float dig_time_complete = 0.0;
\r
2667 if(prop.diggable == false)
\r
2669 /*dstream<<"Material "<<(int)material
\r
2670 <<" not diggable with \""
\r
2671 <<toolname<<"\""<<std::endl;*/
\r
2672 // I guess nobody will wait for this long
\r
2673 dig_time_complete = 10000000.0;
\r
2677 dig_time_complete = prop.time;
\r
2680 if(dig_time_complete >= 0.001)
\r
2682 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2683 * dig_time/dig_time_complete);
\r
2685 // This is for torches
\r
2688 dig_index = CRACK_ANIMATION_LENGTH;
\r
2691 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2693 //TimeTaker timer("client.setTempMod");
\r
2694 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2695 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2699 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2700 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2701 client.clearTempMod(nodepos);
\r
2702 client.removeNode(nodepos);
\r
2706 nodig_delay_counter = dig_time_complete
\r
2707 / (float)CRACK_ANIMATION_LENGTH;
\r
2709 // We don't want a corresponding delay to
\r
2710 // very time consuming nodes
\r
2711 if(nodig_delay_counter > 0.5)
\r
2713 nodig_delay_counter = 0.5;
\r
2715 // We want a slight delay to very little
\r
2716 // time consuming nodes
\r
2717 float mindelay = 0.15;
\r
2718 if(nodig_delay_counter < mindelay)
\r
2720 nodig_delay_counter = mindelay;
\r
2724 dig_time += dtime;
\r
2728 if(g_input->getRightClicked())
\r
2730 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2731 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2734 nodepos_old = nodepos;
\r
2739 } // selected_object == NULL
\r
2741 g_input->resetLeftClicked();
\r
2742 g_input->resetRightClicked();
\r
2744 if(g_input->getLeftReleased())
\r
2746 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2748 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2750 if(g_input->getRightReleased())
\r
2752 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2756 g_input->resetLeftReleased();
\r
2757 g_input->resetRightReleased();
\r
2760 Calculate stuff for drawing
\r
2763 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2765 u32 daynight_ratio = client.getDayNightRatio();
\r
2766 /*video::SColor bgcolor = video::SColor(
\r
2768 skycolor.getRed() * daynight_ratio / 1000,
\r
2769 skycolor.getGreen() * daynight_ratio / 1000,
\r
2770 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2772 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2773 video::SColor bgcolor = video::SColor(
\r
2775 skycolor.getRed() * l / 255,
\r
2776 skycolor.getGreen() * l / 255,
\r
2777 skycolor.getBlue() * l / 255);
\r
2783 if(g_settings.getBool("enable_fog") == true)
\r
2785 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2786 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2787 if(draw_control.range_all)
\r
2788 range = 100000*BS;
\r
2792 video::EFT_FOG_LINEAR,
\r
2796 false, // pixel fog
\r
2797 false // range fog
\r
2803 Update gui stuff (0ms)
\r
2806 //TimeTaker guiupdatetimer("Gui updating");
\r
2809 wchar_t temptext[150];
\r
2811 static float drawtime_avg = 0;
\r
2812 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2813 static float beginscenetime_avg = 0;
\r
2814 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2815 static float scenetime_avg = 0;
\r
2816 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2817 static float endscenetime_avg = 0;
\r
2818 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2820 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2822 L", R: range_all=%i"
\r
2824 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2826 draw_control.range_all,
\r
2828 beginscenetime_avg,
\r
2833 guitext->setText(temptext);
\r
2837 wchar_t temptext[150];
\r
2838 swprintf(temptext, 150,
\r
2839 L"(% .1f, % .1f, % .1f)"
\r
2840 L" (% .3f < btime_jitter < % .3f"
\r
2841 L", dtime_jitter = % .1f %%"
\r
2842 L", v_range = %.1f)",
\r
2843 player_position.X/BS,
\r
2844 player_position.Y/BS,
\r
2845 player_position.Z/BS,
\r
2846 busytime_jitter1_min_sample,
\r
2847 busytime_jitter1_max_sample,
\r
2848 dtime_jitter1_max_fraction * 100.0,
\r
2849 draw_control.wanted_range
\r
2852 guitext2->setText(temptext);
\r
2856 guitext_info->setText(infotext.c_str());
\r
2860 Get chat messages from client
\r
2863 // Get new messages
\r
2864 std::wstring message;
\r
2865 while(client.getChatMessage(message))
\r
2867 chat_lines.push_back(ChatLine(message));
\r
2868 /*if(chat_lines.size() > 6)
\r
2870 core::list<ChatLine>::Iterator
\r
2871 i = chat_lines.begin();
\r
2872 chat_lines.erase(i);
\r
2875 // Append them to form the whole static text and throw
\r
2876 // it to the gui element
\r
2877 std::wstring whole;
\r
2878 // This will correspond to the line number counted from
\r
2879 // top to bottom, from size-1 to 0
\r
2880 s16 line_number = chat_lines.size();
\r
2881 // Count of messages to be removed from the top
\r
2882 u16 to_be_removed_count = 0;
\r
2883 for(core::list<ChatLine>::Iterator
\r
2884 i = chat_lines.begin();
\r
2885 i != chat_lines.end(); i++)
\r
2887 // After this, line number is valid for this loop
\r
2890 (*i).age += dtime;
\r
2892 This results in a maximum age of 60*6 to the
\r
2893 lowermost line and a maximum of 6 lines
\r
2895 float allowed_age = (6-line_number) * 60.0;
\r
2897 if((*i).age > allowed_age)
\r
2899 to_be_removed_count++;
\r
2902 whole += (*i).text + L'\n';
\r
2904 for(u16 i=0; i<to_be_removed_count; i++)
\r
2906 core::list<ChatLine>::Iterator
\r
2907 it = chat_lines.begin();
\r
2908 chat_lines.erase(it);
\r
2910 guitext_chat->setText(whole.c_str());
\r
2911 // Update gui element size and position
\r
2912 core::rect<s32> rect(
\r
2914 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2915 screensize.X - 10,
\r
2918 guitext_chat->setRelativePosition(rect);
\r
2920 if(chat_lines.size() == 0)
\r
2921 guitext_chat->setVisible(false);
\r
2923 guitext_chat->setVisible(true);
\r
2930 static u16 old_selected_item = 65535;
\r
2931 if(client.getLocalInventoryUpdated()
\r
2932 || g_selected_item != old_selected_item)
\r
2934 old_selected_item = g_selected_item;
\r
2935 //std::cout<<"Updating local inventory"<<std::endl;
\r
2936 client.getLocalInventory(local_inventory);
\r
2937 quick_inventory->setSelection(g_selected_item);
\r
2938 quick_inventory->update();
\r
2942 Send actions returned by the inventory menu
\r
2944 while(inventory_action_queue.size() != 0)
\r
2946 InventoryAction *a = inventory_action_queue.pop_front();
\r
2948 client.sendInventoryAction(a);
\r
2957 TimeTaker drawtimer("Drawing");
\r
2961 TimeTaker timer("beginScene");
\r
2962 driver->beginScene(true, true, bgcolor);
\r
2963 //driver->beginScene(false, true, bgcolor);
\r
2964 beginscenetime = timer.stop(true);
\r
2969 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2972 TimeTaker timer("smgr");
\r
2974 scenetime = timer.stop(true);
\r
2978 //TimeTaker timer9("auxiliary drawings");
\r
2982 //TimeTaker //timer10("//timer10");
\r
2984 video::SMaterial m;
\r
2985 //m.Thickness = 10;
\r
2987 m.Lighting = false;
\r
2988 driver->setMaterial(m);
\r
2990 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2992 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2993 i != hilightboxes.end(); i++)
\r
2995 /*std::cout<<"hilightbox min="
\r
2996 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2998 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3000 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3006 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3007 displaycenter + core::vector2d<s32>(10,0),
\r
3008 video::SColor(255,255,255,255));
\r
3009 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3010 displaycenter + core::vector2d<s32>(0,10),
\r
3011 video::SColor(255,255,255,255));
\r
3016 if(g_settings.getBool("frametime_graph") == true)
\r
3019 for(core::list<float>::Iterator
\r
3020 i = frametime_log.begin();
\r
3021 i != frametime_log.end();
\r
3024 driver->draw2DLine(v2s32(x,50),
\r
3025 v2s32(x,50+(*i)*1000),
\r
3026 video::SColor(255,255,255,255));
\r
3034 //TimeTaker //timer11("//timer11");
\r
3040 guienv->drawAll();
\r
3044 TimeTaker timer("endScene");
\r
3045 driver->endScene();
\r
3046 endscenetime = timer.stop(true);
\r
3049 drawtime = drawtimer.stop(true);
\r
3055 static s16 lastFPS = 0;
\r
3056 //u16 fps = driver->getFPS();
\r
3057 u16 fps = (1.0/dtime_avg1);
\r
3059 if (lastFPS != fps)
\r
3061 core::stringw str = L"Minetest [";
\r
3062 str += driver->getName();
\r
3066 device->setWindowCaption(str.c_str());
\r
3072 device->yield();*/
\r
3075 delete quick_inventory;
\r
3078 Disable texture fetches and other stuff that is queued
\r
3079 to be processed by the main loop.
\r
3081 This has to be done before client goes out of scope.
\r
3083 g_irrlicht->Shutdown(true);
\r
3085 } // client and server are deleted at this point
\r
3088 catch(con::PeerNotFoundException &e)
\r
3090 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3091 error_message = L"Connection timed out.";
\r
3094 } // Menu-game loop
\r
3099 In the end, delete the Irrlicht device.
\r
3104 Update configuration file
\r
3106 /*if(configpath != "")
\r
3108 g_settings.updateConfigFile(configpath.c_str());
\r
3111 END_DEBUG_EXCEPTION_HANDLER
\r
3113 debugstreams_deinit();
\r