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 FIXME: Some network errors on Windows that cause local game to not work
\r
116 - See siggjen's emails.
\r
117 - Is this the famous "windows 7 problem"?
\r
118 - Apparently there might be other errors too
\r
120 Networking and serialization:
\r
121 -----------------------------
\r
123 TODO: Get rid of GotSplitPacketException
\r
128 TODO: Add gui option to remove map
\r
130 TODO: Configuration menu, at least for keys
\r
135 TODO: Optimize day/night mesh updating somehow
\r
136 - create copies of all textures for all lighting values and only
\r
137 change texture for material?
\r
138 - Umm... the collecting of the faces is the slow part
\r
139 -> what about just changing the color values of the existing
\r
140 meshbuffers? It should go quite fast.
\r
141 - This is not easy; There'd need to be a buffer somewhere
\r
142 that would contain the night and day lighting values.
\r
143 - Actually if FastFaces would be stored, they could
\r
146 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
148 - That is >500 vertices
\r
149 - This is not easy; all the MapBlocks close to the player would
\r
150 still need to be drawn separately and combining the blocks
\r
151 would have to happen in a background thread
\r
153 TODO: Make fetching sector's blocks more efficient when rendering
\r
154 sectors that have very large amounts of blocks (on client)
\r
155 - Is this necessary at all?
\r
157 TODO: Flowing water animation
\r
159 NOTE(FIXED): A lock condition is possible:
\r
160 1) MapBlock::updateMesh() is called from client asynchronously:
\r
161 - AsyncProcessData() -> Map::updateMeshes()
\r
162 2) Asynchronous locks m_temp_mods_mutex
\r
163 3) MapBlock::updateMesh() is called from client synchronously:
\r
164 - Client::step() -> Environment::step()
\r
165 4) Synchronous starts waiting for m_temp_mods_mutex
\r
166 5) Asynchronous calls getTexture, which starts waiting for main thread
\r
171 TODO: Make the video backend selectable
\r
176 TODO: Untie client network operations from framerate
\r
177 - Needs some input queues or something
\r
179 TODO: Make morning and evening transition more smooth and maybe shorter
\r
181 TODO: Don't update all meshes always on single node changes, but
\r
182 check which ones should be updated
\r
183 - implement Map::updateNodeMeshes()
\r
188 TODO: When player dies, throw items on map
\r
190 TODO: Make an option to the server to disable building and digging near
\r
191 the starting position
\r
193 TODO: Save players with inventories to disk
\r
194 TODO: Players to be saved as text in map/players/<name>
\r
196 TODO: Copy the text of the last picked sign to inventory in creative
\r
199 TODO: Check what goes wrong with caching map to disk (Kray)
\r
202 TODO: When server sees that client is removing an inexistent block to
\r
203 an existent position, resend the MapBlock.
\r
205 FIXME: Server went into some infinite PeerNotFoundException loop
\r
210 TODO: Better handling of objects and mobs
\r
212 - There has to be some way to do it with less messy code
\r
213 - Make separate classes for client and server
\r
214 - Client should not discriminate between blocks, server should
\r
215 - Make other players utilize the same framework
\r
216 - This is also needed for objects that don't get sent to client
\r
217 but are used for triggers etc
\r
219 TODO: There has to be some better way to handle static objects than to
\r
220 send them all the time. This affects signs and item objects.
\r
221 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
222 need an additional metadata field for the texts
\r
223 - This is also needed for item container chests
\r
225 Block object server side:
\r
226 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
227 - For all blocks in the buffer, objects are stepped(). This
\r
228 means they are active.
\r
229 - TODO: A global active buffer is needed for the server
\r
230 - TODO: A timestamp to blocks
\r
231 - TODO: All blocks going in and out of the buffer are recorded.
\r
232 - TODO: For outgoing blocks, timestamp is written.
\r
233 - TODO: For incoming blocks, time difference is calculated and
\r
234 objects are stepped according to it.
\r
239 TODO: Mineral and ground material properties
\r
240 - This way mineral ground toughness can be calculated with just
\r
241 some formula, as well as tool strengths
\r
243 TODO: Flowing water to actually contain flow direction information
\r
245 TODO: Remove duplicate lighting implementation from Map (leave
\r
246 VoxelManipulator, which is faster)
\r
248 FEATURE: Create a system that allows a huge amount of different "map
\r
249 generator modules/filters"
\r
251 FEATURE: Erosion simulation at map generation time
\r
252 - Simulate water flows, which would carve out dirt fast and
\r
253 then turn stone into gravel and sand and relocate it.
\r
254 - How about relocating minerals, too? Coal and gold in
\r
255 downstream sand and gravel would be kind of cool
\r
256 - This would need a better way of handling minerals, mainly
\r
257 to have mineral content as a separate field. the first
\r
258 parameter field is free for this.
\r
259 - Simulate rock falling from cliffs when water has removed
\r
260 enough solid rock from the bottom
\r
262 Doing now (most important at the top):
\r
263 --------------------------------------
\r
267 === Stuff to do before release
\r
268 * Save the new mapgen stuff
\r
269 - map/meta.txt, which should contain only plain text, something like this:
\r
270 seed = 7ff1bafcd7118800
\r
272 - map/chunks.dat: chunk positions and flags in binary format
\r
273 * Make server find the spawning place from the real map data, not from
\r
275 - But the changing borders of chunk have to be avoided, because
\r
276 there is time to generate only one chunk.
\r
277 * Make the generator to run in background and not blocking block
\r
278 placement and transfer
\r
279 * only_from_disk might not work anymore - check and fix it.
\r
280 * Check the fixmes in the list above
\r
282 === Stuff to do after release
\r
283 * Add some kind of erosion and other stuff that now is possible
\r
284 * Make client to fetch stuff asynchronously
\r
285 - Needs method SyncProcessData
\r
286 * Fix the problem with the server constantly saving one or a few
\r
287 blocks? List the first saved block, maybe it explains.
\r
288 - It is probably caused by oscillating water
\r
289 * Water doesn't start flowing after map generation like it should
\r
290 - Are there still problems?
\r
291 * Better water generation (spread it to underwater caverns but don't
\r
292 fill dungeons that don't touch big water masses)
\r
293 * When generating a chunk and the neighboring chunk doesn't have mud
\r
294 and stuff yet and the ground is fairly flat, the mud will flow to
\r
295 the other chunk making nasty straight walls when the other chunk
\r
296 is generated. Fix it.
\r
297 * Make a small history check to transformLiquids to detect and log
\r
298 continuous oscillations, in such detail that they can be fixed.
\r
300 ======================================================================
\r
305 Setting this to 1 enables a special camera mode that forces
\r
306 the renderers to think that the camera statically points from
\r
307 the starting place to a static direction.
\r
309 This allows one to move around with the player and see what
\r
310 is actually drawn behind solid things and behind the player.
\r
312 #define FIELD_OF_VIEW_TEST 0
\r
316 #pragma message ("Disabling unit tests")
\r
318 #warning "Disabling unit tests"
\r
320 // Disable unit tests
\r
321 #define ENABLE_TESTS 0
\r
323 // Enable unit tests
\r
324 #define ENABLE_TESTS 1
\r
328 #pragma comment(lib, "Irrlicht.lib")
\r
329 //#pragma comment(lib, "jthread.lib")
\r
330 #pragma comment(lib, "zlibwapi.lib")
\r
331 #pragma comment(lib, "Shell32.lib")
\r
332 // This would get rid of the console window
\r
333 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
336 #include <iostream>
\r
338 #include <jmutexautolock.h>
\r
339 #include <locale.h>
\r
340 #include "common_irrlicht.h"
\r
343 #include "player.h"
\r
346 #include "environment.h"
\r
347 #include "server.h"
\r
348 #include "client.h"
\r
349 #include "serialization.h"
\r
350 #include "constants.h"
\r
351 #include "strfnd.h"
\r
352 #include "porting.h"
\r
353 #include "irrlichtwrapper.h"
\r
354 #include "gettime.h"
\r
355 #include "porting.h"
\r
356 #include "guiPauseMenu.h"
\r
357 #include "guiInventoryMenu.h"
\r
358 #include "guiTextInputMenu.h"
\r
359 #include "materials.h"
\r
360 #include "guiMessageMenu.h"
\r
361 #include "filesys.h"
\r
362 #include "config.h"
\r
363 #include "guiMainMenu.h"
\r
364 #include "mineral.h"
\r
367 IrrlichtWrapper *g_irrlicht;
\r
369 MapDrawControl draw_control;
\r
373 These are loaded from the config file.
\r
376 Settings g_settings;
\r
378 extern void set_default_settings();
\r
384 IrrlichtDevice *g_device = NULL;
\r
385 Client *g_client = NULL;
\r
391 gui::IGUIEnvironment* guienv = NULL;
\r
392 gui::IGUIStaticText *guiroot = NULL;
\r
394 class MainMenuManager : public IMenuManager
\r
397 virtual void createdMenu(GUIModalMenu *menu)
\r
399 for(core::list<GUIModalMenu*>::Iterator
\r
400 i = m_stack.begin();
\r
401 i != m_stack.end(); i++)
\r
403 assert(*i != menu);
\r
406 if(m_stack.size() != 0)
\r
407 (*m_stack.getLast())->setVisible(false);
\r
408 m_stack.push_back(menu);
\r
411 virtual void deletingMenu(GUIModalMenu *menu)
\r
413 // Remove all entries if there are duplicates
\r
414 bool removed_entry;
\r
416 removed_entry = false;
\r
417 for(core::list<GUIModalMenu*>::Iterator
\r
418 i = m_stack.begin();
\r
419 i != m_stack.end(); i++)
\r
424 removed_entry = true;
\r
428 }while(removed_entry);
\r
430 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
431 assert(*i == menu);
\r
432 m_stack.erase(i);*/
\r
434 if(m_stack.size() != 0)
\r
435 (*m_stack.getLast())->setVisible(true);
\r
440 return m_stack.size();
\r
443 core::list<GUIModalMenu*> m_stack;
\r
446 MainMenuManager g_menumgr;
\r
448 bool noMenuActive()
\r
450 return (g_menumgr.menuCount() == 0);
\r
453 bool g_disconnect_requested = false;
\r
455 class MainGameCallback : public IGameCallback
\r
458 virtual void exitToOS()
\r
460 g_device->closeDevice();
\r
463 virtual void disconnect()
\r
465 g_disconnect_requested = true;
\r
469 MainGameCallback g_gamecallback;
\r
471 // Inventory actions from the menu are buffered here before sending
\r
472 Queue<InventoryAction*> inventory_action_queue;
\r
473 // This is a copy of the inventory that the client's environment has
\r
474 Inventory local_inventory;
\r
476 u16 g_selected_item = 0;
\r
483 std::ostream *dout_con_ptr = &dummyout;
\r
484 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
485 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
486 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
487 //std::ostream *dout_con_ptr = &dstream;
\r
488 //std::ostream *derr_con_ptr = &dstream;
\r
491 std::ostream *dout_server_ptr = &dstream;
\r
492 std::ostream *derr_server_ptr = &dstream;
\r
495 std::ostream *dout_client_ptr = &dstream;
\r
496 std::ostream *derr_client_ptr = &dstream;
\r
499 gettime.h implementation
\r
505 Use irrlicht because it is more precise than porting.h's
\r
508 if(g_irrlicht == NULL)
\r
510 return g_irrlicht->getTime();
\r
517 struct TextDestSign : public TextDest
\r
519 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
521 m_blockpos = blockpos;
\r
525 void gotText(std::wstring text)
\r
527 std::string ntext = wide_to_narrow(text);
\r
528 dstream<<"Changing text of a sign object: "
\r
529 <<ntext<<std::endl;
\r
530 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
538 struct TextDestChat : public TextDest
\r
540 TextDestChat(Client *client)
\r
544 void gotText(std::wstring text)
\r
546 m_client->sendChatMessage(text);
\r
547 m_client->addChatMessage(text);
\r
553 class MyEventReceiver : public IEventReceiver
\r
556 // This is the one method that we have to implement
\r
557 virtual bool OnEvent(const SEvent& event)
\r
560 React to nothing here if a menu is active
\r
562 if(noMenuActive() == false)
\r
568 // Remember whether each key is down or up
\r
569 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
571 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
573 if(event.KeyInput.PressedDown)
\r
575 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
581 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
583 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
585 dstream<<DTIME<<"MyEventReceiver: "
\r
586 <<"Launching pause menu"<<std::endl;
\r
587 // It will delete itself by itself
\r
588 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
589 &g_menumgr))->drop();
\r
592 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
594 dstream<<DTIME<<"MyEventReceiver: "
\r
595 <<"Launching inventory"<<std::endl;
\r
596 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
597 &local_inventory, &inventory_action_queue,
\r
598 &g_menumgr))->drop();
\r
601 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
603 TextDest *dest = new TextDestChat(g_client);
\r
605 (new GUITextInputMenu(guienv, guiroot, -1,
\r
611 // Material selection
\r
612 /*if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
614 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
617 g_selected_item = 0;
\r
618 dstream<<DTIME<<"Selected item: "
\r
619 <<g_selected_item<<std::endl;
\r
622 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
623 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
625 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
626 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
628 if(s1 < PLAYER_INVENTORY_SIZE)
\r
629 g_selected_item = s1-1;
\r
630 dstream<<DTIME<<"Selected item: "
\r
631 <<g_selected_item<<std::endl;
\r
634 // Viewing range selection
\r
635 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
637 if(draw_control.range_all)
\r
639 draw_control.range_all = false;
\r
640 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
644 draw_control.range_all = true;
\r
645 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
649 // Print debug stacks
\r
650 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
652 dstream<<"-----------------------------------------"
\r
654 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
655 dstream<<"-----------------------------------------"
\r
657 debug_stacks_print();
\r
662 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
664 if(noMenuActive() == false)
\r
666 left_active = false;
\r
667 middle_active = false;
\r
668 right_active = false;
\r
672 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
673 left_active = event.MouseInput.isLeftPressed();
\r
674 middle_active = event.MouseInput.isMiddlePressed();
\r
675 right_active = event.MouseInput.isRightPressed();
\r
677 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
679 leftclicked = true;
\r
681 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
683 rightclicked = true;
\r
685 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
687 leftreleased = true;
\r
689 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
691 rightreleased = true;
\r
693 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
695 /*dstream<<"event.MouseInput.Wheel="
\r
696 <<event.MouseInput.Wheel<<std::endl;*/
\r
697 if(event.MouseInput.Wheel < 0)
\r
699 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
702 g_selected_item = 0;
\r
704 else if(event.MouseInput.Wheel > 0)
\r
706 if(g_selected_item > 0)
\r
709 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
718 // This is used to check whether a key is being held down
\r
719 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
721 return keyIsDown[keyCode];
\r
726 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
727 keyIsDown[i] = false;
\r
729 leftclicked = false;
\r
730 rightclicked = false;
\r
731 leftreleased = false;
\r
732 rightreleased = false;
\r
734 left_active = false;
\r
735 middle_active = false;
\r
736 right_active = false;
\r
747 bool rightreleased;
\r
750 bool middle_active;
\r
754 // We use this array to store the current state of each key
\r
755 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
758 IrrlichtDevice *m_device;
\r
767 virtual ~InputHandler()
\r
771 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
773 virtual v2s32 getMousePos() = 0;
\r
774 virtual void setMousePos(s32 x, s32 y) = 0;
\r
776 virtual bool getLeftState() = 0;
\r
777 virtual bool getRightState() = 0;
\r
779 virtual bool getLeftClicked() = 0;
\r
780 virtual bool getRightClicked() = 0;
\r
781 virtual void resetLeftClicked() = 0;
\r
782 virtual void resetRightClicked() = 0;
\r
784 virtual bool getLeftReleased() = 0;
\r
785 virtual bool getRightReleased() = 0;
\r
786 virtual void resetLeftReleased() = 0;
\r
787 virtual void resetRightReleased() = 0;
\r
789 virtual void step(float dtime) {};
\r
791 virtual void clear() {};
\r
794 InputHandler *g_input = NULL;
\r
796 class RealInputHandler : public InputHandler
\r
799 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
801 m_receiver(receiver)
\r
804 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
806 return m_receiver->IsKeyDown(keyCode);
\r
808 virtual v2s32 getMousePos()
\r
810 return m_device->getCursorControl()->getPosition();
\r
812 virtual void setMousePos(s32 x, s32 y)
\r
814 m_device->getCursorControl()->setPosition(x, y);
\r
817 virtual bool getLeftState()
\r
819 return m_receiver->left_active;
\r
821 virtual bool getRightState()
\r
823 return m_receiver->right_active;
\r
826 virtual bool getLeftClicked()
\r
828 return m_receiver->leftclicked;
\r
830 virtual bool getRightClicked()
\r
832 return m_receiver->rightclicked;
\r
834 virtual void resetLeftClicked()
\r
836 m_receiver->leftclicked = false;
\r
838 virtual void resetRightClicked()
\r
840 m_receiver->rightclicked = false;
\r
843 virtual bool getLeftReleased()
\r
845 return m_receiver->leftreleased;
\r
847 virtual bool getRightReleased()
\r
849 return m_receiver->rightreleased;
\r
851 virtual void resetLeftReleased()
\r
853 m_receiver->leftreleased = false;
\r
855 virtual void resetRightReleased()
\r
857 m_receiver->rightreleased = false;
\r
862 resetRightClicked();
\r
863 resetLeftClicked();
\r
866 IrrlichtDevice *m_device;
\r
867 MyEventReceiver *m_receiver;
\r
870 class RandomInputHandler : public InputHandler
\r
873 RandomInputHandler()
\r
875 leftclicked = false;
\r
876 rightclicked = false;
\r
877 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
878 keydown[i] = false;
\r
880 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
882 return keydown[keyCode];
\r
884 virtual v2s32 getMousePos()
\r
888 virtual void setMousePos(s32 x, s32 y)
\r
890 mousepos = v2s32(x,y);
\r
893 virtual bool getLeftState()
\r
897 virtual bool getRightState()
\r
902 virtual bool getLeftClicked()
\r
904 return leftclicked;
\r
906 virtual bool getRightClicked()
\r
908 return rightclicked;
\r
910 virtual void resetLeftClicked()
\r
912 leftclicked = false;
\r
914 virtual void resetRightClicked()
\r
916 rightclicked = false;
\r
919 virtual bool getLeftReleased()
\r
923 virtual bool getRightReleased()
\r
927 virtual void resetLeftReleased()
\r
930 virtual void resetRightReleased()
\r
934 virtual void step(float dtime)
\r
937 static float counter1 = 0;
\r
941 counter1 = 0.1*Rand(1,10);
\r
942 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
943 g_selected_material++;
\r
945 g_selected_material = 0;*/
\r
946 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
949 g_selected_item = 0;
\r
953 static float counter1 = 0;
\r
957 counter1 = 0.1*Rand(1, 40);
\r
958 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
962 static float counter1 = 0;
\r
966 counter1 = 0.1*Rand(1, 40);
\r
967 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
971 static float counter1 = 0;
\r
975 counter1 = 0.1*Rand(1, 40);
\r
976 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
980 static float counter1 = 0;
\r
984 counter1 = 0.1*Rand(1, 40);
\r
985 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
989 static float counter1 = 0;
\r
993 counter1 = 0.1*Rand(1, 20);
\r
994 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
998 static float counter1 = 0;
\r
1000 if(counter1 < 0.0)
\r
1002 counter1 = 0.1*Rand(1, 30);
\r
1003 leftclicked = true;
\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 rightclicked = true;
\r
1015 mousepos += mousespeed;
\r
1018 s32 Rand(s32 min, s32 max)
\r
1020 return (myrand()%(max-min+1))+min;
\r
1023 bool keydown[KEY_KEY_CODES_COUNT];
\r
1027 bool rightclicked;
\r
1030 void updateViewingRange(f32 frametime_in, Client *client)
\r
1032 if(draw_control.range_all == true)
\r
1035 static f32 added_frametime = 0;
\r
1036 static s16 added_frames = 0;
\r
1038 added_frametime += frametime_in;
\r
1039 added_frames += 1;
\r
1041 // Actually this counter kind of sucks because frametime is busytime
\r
1042 static f32 counter = 0;
\r
1043 counter -= frametime_in;
\r
1049 /*dstream<<__FUNCTION_NAME
\r
1050 <<": Collected "<<added_frames<<" frames, total of "
\r
1051 <<added_frametime<<"s."<<std::endl;*/
\r
1053 /*dstream<<"draw_control.blocks_drawn="
\r
1054 <<draw_control.blocks_drawn
\r
1055 <<", draw_control.blocks_would_have_drawn="
\r
1056 <<draw_control.blocks_would_have_drawn
\r
1059 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1060 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1062 draw_control.wanted_min_range = range_min;
\r
1063 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1065 float block_draw_ratio = 1.0;
\r
1066 if(draw_control.blocks_would_have_drawn != 0)
\r
1068 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1069 / (float)draw_control.blocks_would_have_drawn;
\r
1072 // Calculate the average frametime in the case that all wanted
\r
1073 // blocks had been drawn
\r
1074 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1076 added_frametime = 0.0;
\r
1079 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1080 float wanted_frametime = 1.0 / wanted_fps;
\r
1082 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1083 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1085 // If needed frametime change is very small, just return
\r
1086 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1088 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1092 float range = draw_control.wanted_range;
\r
1093 float new_range = range;
\r
1095 static s16 range_old = 0;
\r
1096 static f32 frametime_old = 0;
\r
1098 float d_range = range - range_old;
\r
1099 f32 d_frametime = frametime - frametime_old;
\r
1100 // A sane default of 30ms per 50 nodes of range
\r
1101 static f32 time_per_range = 30. / 50;
\r
1104 time_per_range = d_frametime / d_range;
\r
1107 // The minimum allowed calculated frametime-range derivative:
\r
1108 // Practically this sets the maximum speed of changing the range.
\r
1109 // The lower this value, the higher the maximum changing speed.
\r
1110 // A low value here results in wobbly range (0.001)
\r
1111 // A high value here results in slow changing range (0.0025)
\r
1112 // SUGG: This could be dynamically adjusted so that when
\r
1113 // the camera is turning, this is lower
\r
1114 //float min_time_per_range = 0.0015;
\r
1115 float min_time_per_range = 0.0010;
\r
1116 //float min_time_per_range = 0.05 / range;
\r
1117 if(time_per_range < min_time_per_range)
\r
1119 time_per_range = min_time_per_range;
\r
1120 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1124 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1127 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1128 // Dampen the change a bit to kill oscillations
\r
1129 //wanted_range_change *= 0.9;
\r
1130 //wanted_range_change *= 0.75;
\r
1131 wanted_range_change *= 0.5;
\r
1132 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1134 // If needed range change is very small, just return
\r
1135 if(fabs(wanted_range_change) < 0.001)
\r
1137 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1141 new_range += wanted_range_change;
\r
1142 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1144 //float new_range_unclamped = new_range;
\r
1145 if(new_range < range_min)
\r
1146 new_range = range_min;
\r
1147 if(new_range > range_max)
\r
1148 new_range = range_max;
\r
1150 /*if(new_range != new_range_unclamped)
\r
1151 dstream<<", clamped to "<<new_range<<std::endl;
\r
1153 dstream<<std::endl;*/
\r
1155 draw_control.wanted_range = new_range;
\r
1157 range_old = new_range;
\r
1158 frametime_old = frametime;
\r
1161 class GUIQuickInventory : public IEventReceiver
\r
1164 GUIQuickInventory(
\r
1165 gui::IGUIEnvironment* env,
\r
1166 gui::IGUIElement* parent,
\r
1169 Inventory *inventory):
\r
1170 m_itemcount(itemcount),
\r
1171 m_inventory(inventory)
\r
1173 core::rect<s32> imgsize(0,0,48,48);
\r
1174 core::rect<s32> textsize(0,0,48,16);
\r
1175 v2s32 spacing(0, 64);
\r
1176 for(s32 i=0; i<m_itemcount; i++)
\r
1178 m_images.push_back(env->addImage(
\r
1179 imgsize + pos + spacing*i
\r
1181 m_images[i]->setScaleImage(true);
\r
1182 m_texts.push_back(env->addStaticText(
\r
1184 textsize + pos + spacing*i,
\r
1187 m_texts[i]->setBackgroundColor(
\r
1188 video::SColor(128,0,0,0));
\r
1189 m_texts[i]->setTextAlignment(
\r
1190 gui::EGUIA_CENTER,
\r
1191 gui::EGUIA_UPPERLEFT);
\r
1195 ~GUIQuickInventory()
\r
1197 for(u32 i=0; i<m_texts.size(); i++)
\r
1199 m_texts[i]->remove();
\r
1201 for(u32 i=0; i<m_images.size(); i++)
\r
1203 m_images[i]->remove();
\r
1207 virtual bool OnEvent(const SEvent& event)
\r
1212 void setSelection(s32 i)
\r
1221 start = m_selection - m_itemcount / 2;
\r
1223 InventoryList *mainlist = m_inventory->getList("main");
\r
1225 for(s32 i=0; i<m_itemcount; i++)
\r
1227 s32 j = i + start;
\r
1229 if(j > (s32)mainlist->getSize() - 1)
\r
1230 j -= mainlist->getSize();
\r
1232 j += mainlist->getSize();
\r
1234 InventoryItem *item = mainlist->getItem(j);
\r
1238 m_images[i]->setImage(NULL);
\r
1241 if(m_selection == j)
\r
1242 swprintf(t, 10, L"<-");
\r
1244 swprintf(t, 10, L"");
\r
1245 m_texts[i]->setText(t);
\r
1247 // The next ifs will segfault with a NULL pointer
\r
1252 m_images[i]->setImage(item->getImage());
\r
1255 if(m_selection == j)
\r
1256 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1258 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1259 m_texts[i]->setText(t);
\r
1265 core::array<gui::IGUIStaticText*> m_texts;
\r
1266 core::array<gui::IGUIImage*> m_images;
\r
1267 Inventory *m_inventory;
\r
1278 ChatLine(const std::wstring &a_text):
\r
1284 std::wstring text;
\r
1287 // These are defined global so that they're not optimized too much.
\r
1288 // Can't change them to volatile.
\r
1293 std::string tempstring;
\r
1294 std::string tempstring2;
\r
1299 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1300 TimeTaker timer("Testing std::string speed");
\r
1301 const u32 jj = 10000;
\r
1302 for(u32 j=0; j<jj; j++)
\r
1306 const u32 ii = 10;
\r
1307 for(u32 i=0; i<ii; i++){
\r
1308 tempstring2 += "asd";
\r
1310 for(u32 i=0; i<ii+1; i++){
\r
1311 tempstring += "asd";
\r
1312 if(tempstring == tempstring2)
\r
1318 dstream<<"All of the following tests should take around 100ms each."
\r
1322 TimeTaker timer("Testing floating-point conversion speed");
\r
1324 for(u32 i=0; i<4000000; i++){
\r
1331 TimeTaker timer("Testing floating-point vector speed");
\r
1333 tempv3f1 = v3f(1,2,3);
\r
1334 tempv3f2 = v3f(4,5,6);
\r
1335 for(u32 i=0; i<10000000; i++){
\r
1336 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1337 tempv3f2 += v3f(7,8,9);
\r
1342 TimeTaker timer("Testing core::map speed");
\r
1344 core::map<v2s16, f32> map1;
\r
1347 for(s16 y=0; y<ii; y++){
\r
1348 for(s16 x=0; x<ii; x++){
\r
1349 map1.insert(v2s16(x,y), tempf);
\r
1353 for(s16 y=ii-1; y>=0; y--){
\r
1354 for(s16 x=0; x<ii; x++){
\r
1355 tempf = map1[v2s16(x,y)];
\r
1361 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1362 TimeTaker timer("Testing mutex speed");
\r
1375 // Do at least 10ms
\r
1376 while(timer.getTime() < 10);
\r
1378 u32 dtime = timer.stop();
\r
1379 u32 per_ms = n / dtime;
\r
1380 std::cout<<"Done. "<<dtime<<"ms, "
\r
1381 <<per_ms<<"/ms"<<std::endl;
\r
1385 int main(int argc, char *argv[])
\r
1388 Parse command line
\r
1391 // List all allowed options
\r
1392 core::map<std::string, ValueSpec> allowed_options;
\r
1393 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1394 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1395 "Run server directly"));
\r
1396 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1397 "Load configuration from specified file"));
\r
1398 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1399 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1400 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1401 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1402 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1403 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1405 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1407 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1409 Settings cmd_args;
\r
1411 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1413 if(ret == false || cmd_args.getFlag("help"))
\r
1415 dstream<<"Allowed options:"<<std::endl;
\r
1416 for(core::map<std::string, ValueSpec>::Iterator
\r
1417 i = allowed_options.getIterator();
\r
1418 i.atEnd() == false; i++)
\r
1420 dstream<<" --"<<i.getNode()->getKey();
\r
1421 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1426 dstream<<" <value>";
\r
1428 dstream<<std::endl;
\r
1430 if(i.getNode()->getValue().help != NULL)
\r
1432 dstream<<" "<<i.getNode()->getValue().help
\r
1437 return cmd_args.getFlag("help") ? 0 : 1;
\r
1441 Low-level initialization
\r
1444 bool disable_stderr = false;
\r
1446 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1447 disable_stderr = true;
\r
1450 // Initialize debug streams
\r
1451 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1452 // Initialize debug stacks
\r
1453 debug_stacks_init();
\r
1455 DSTACK(__FUNCTION_NAME);
\r
1457 porting::initializePaths();
\r
1458 // Create user data directory
\r
1459 fs::CreateDir(porting::path_userdata);
\r
1461 // C-style stuff initialization
\r
1462 initializeMaterialProperties();
\r
1465 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1467 // Print startup message
\r
1468 dstream<<DTIME<<"minetest-c55"
\r
1469 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1470 <<", "<<BUILD_INFO
\r
1474 Basic initialization
\r
1477 // Initialize default settings
\r
1478 set_default_settings();
\r
1480 // Set locale. This is for forcing '.' as the decimal point.
\r
1481 std::locale::global(std::locale("C"));
\r
1482 // This enables printing all characters in bitmap font
\r
1483 setlocale(LC_CTYPE, "en_US");
\r
1485 // Initialize sockets
\r
1487 atexit(sockets_cleanup);
\r
1497 // Path of configuration file in use
\r
1498 std::string configpath = "";
\r
1500 if(cmd_args.exists("config"))
\r
1502 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1505 dstream<<"Could not read configuration from \""
\r
1506 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1509 configpath = cmd_args.get("config");
\r
1513 core::array<std::string> filenames;
\r
1514 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1515 #ifdef RUN_IN_PLACE
\r
1516 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1519 for(u32 i=0; i<filenames.size(); i++)
\r
1521 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1524 configpath = filenames[i];
\r
1530 // Initialize random seed
\r
1535 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1537 These are needed for unit tests at least.
\r
1540 IIrrlichtWrapper irrlicht_dummy;
\r
1542 init_mapnode(&irrlicht_dummy);
\r
1548 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1549 || cmd_args.getFlag("enable-unittests") == true)
\r
1554 /*for(s16 y=-100; y<100; y++)
\r
1555 for(s16 x=-100; x<100; x++)
\r
1557 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1567 if(cmd_args.exists("port"))
\r
1568 port = cmd_args.getU16("port");
\r
1569 else if(cmd_args.exists("port"))
\r
1570 port = g_settings.getU16("port");
\r
1573 std::string map_dir = porting::path_userdata+"/map";
\r
1574 if(cmd_args.exists("map-dir"))
\r
1575 map_dir = cmd_args.get("map-dir");
\r
1576 else if(g_settings.exists("map-dir"))
\r
1577 map_dir = g_settings.get("map-dir");
\r
1579 // Run dedicated server if asked to
\r
1580 if(cmd_args.getFlag("server"))
\r
1582 DSTACK("Dedicated server branch");
\r
1585 Server server(map_dir.c_str());
\r
1586 server.start(port);
\r
1589 dedicated_server_loop(server);
\r
1598 // Address to connect to
\r
1599 std::string address = "";
\r
1601 if(cmd_args.exists("address"))
\r
1603 address = cmd_args.get("address");
\r
1607 address = g_settings.get("address");
\r
1610 std::string playername = g_settings.get("name");
\r
1613 Resolution selection
\r
1616 bool fullscreen = false;
\r
1617 u16 screenW = g_settings.getU16("screenW");
\r
1618 u16 screenH = g_settings.getU16("screenH");
\r
1622 MyEventReceiver receiver;
\r
1624 video::E_DRIVER_TYPE driverType;
\r
1627 //driverType = video::EDT_DIRECT3D9;
\r
1628 driverType = video::EDT_OPENGL;
\r
1630 driverType = video::EDT_OPENGL;
\r
1631 //driverType = video::EDT_BURNINGSVIDEO; // Best software renderer
\r
1634 // create device and exit if creation failed
\r
1636 IrrlichtDevice *device;
\r
1637 device = createDevice(driverType,
\r
1638 core::dimension2d<u32>(screenW, screenH),
\r
1639 16, fullscreen, false, false, &receiver);
\r
1642 return 1; // could not create selected driver.
\r
1644 g_device = device;
\r
1645 g_irrlicht = new IrrlichtWrapper(device);
\r
1648 Speed tests (done after irrlicht is loaded to get timer)
\r
1650 if(cmd_args.getFlag("speedtests"))
\r
1652 dstream<<"Running speed tests"<<std::endl;
\r
1657 device->setResizable(true);
\r
1659 bool random_input = g_settings.getBool("random_input")
\r
1660 || cmd_args.getFlag("random-input");
\r
1662 g_input = new RandomInputHandler();
\r
1664 g_input = new RealInputHandler(device, &receiver);
\r
1667 Continue initialization
\r
1670 video::IVideoDriver* driver = device->getVideoDriver();
\r
1673 This changes the minimum allowed number of vertices in a VBO
\r
1675 //driver->setMinHardwareBufferVertexCount(50);
\r
1677 scene::ISceneManager* smgr = device->getSceneManager();
\r
1679 guienv = device->getGUIEnvironment();
\r
1680 gui::IGUISkin* skin = guienv->getSkin();
\r
1681 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1683 skin->setFont(font);
\r
1685 dstream<<"WARNING: Font file was not found."
\r
1686 " Using default font."<<std::endl;
\r
1687 // If font was not found, this will get us one
\r
1688 font = skin->getFont();
\r
1691 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1692 dstream<<"text_height="<<text_height<<std::endl;
\r
1694 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1695 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1696 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1697 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1698 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1699 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1702 Preload some textures and stuff
\r
1705 init_content_inventory_texture_paths();
\r
1706 init_mapnode(g_irrlicht);
\r
1707 init_mineral(g_irrlicht);
\r
1714 We need some kind of a root node to be able to add
\r
1715 custom gui elements directly on the screen.
\r
1716 Otherwise they won't be automatically drawn.
\r
1718 guiroot = guienv->addStaticText(L"",
\r
1719 core::rect<s32>(0, 0, 10000, 10000));
\r
1721 // First line of debug text
\r
1722 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1724 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1726 // Second line of debug text
\r
1727 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1729 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1732 // At the middle of the screen
\r
1733 // Object infos are shown in this
\r
1734 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1736 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1740 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1742 core::rect<s32>(0,0,0,0),
\r
1744 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1745 core::list<ChatLine> chat_lines;
\r
1748 If an error occurs, this is set to something and the
\r
1749 menu-game loop is restarted. It is then displayed before
\r
1752 std::wstring error_message = L"";
\r
1757 while(g_device->run())
\r
1760 // This is used for catching disconnects
\r
1765 Out-of-game menu loop.
\r
1767 Loop quits when menu returns proper parameters.
\r
1771 // Cursor can be non-visible when coming from the game
\r
1772 device->getCursorControl()->setVisible(true);
\r
1773 // Some stuff are left to scene manager when coming from the game
\r
1774 // (map at least?)
\r
1776 // Reset or hide the debug gui texts
\r
1777 guitext->setText(L"Minetest-c55");
\r
1778 guitext2->setVisible(false);
\r
1779 guitext_info->setVisible(false);
\r
1780 guitext_chat->setVisible(false);
\r
1782 // Initialize menu data
\r
1783 MainMenuData menudata;
\r
1784 menudata.address = narrow_to_wide(address);
\r
1785 menudata.name = narrow_to_wide(playername);
\r
1786 menudata.port = narrow_to_wide(itos(port));
\r
1787 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1789 GUIMainMenu *menu =
\r
1790 new GUIMainMenu(guienv, guiroot, -1,
\r
1791 &g_menumgr, &menudata, &g_gamecallback);
\r
1792 menu->allowFocusRemoval(true);
\r
1794 if(error_message != L"")
\r
1796 GUIMessageMenu *menu2 =
\r
1797 new GUIMessageMenu(guienv, guiroot, -1,
\r
1798 &g_menumgr, error_message.c_str());
\r
1800 error_message = L"";
\r
1803 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1805 dstream<<"Created main menu"<<std::endl;
\r
1807 while(g_device->run())
\r
1809 // Run global IrrlichtWrapper's main thread processing stuff
\r
1810 g_irrlicht->Run();
\r
1812 if(menu->getStatus() == true)
\r
1815 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1816 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1817 guienv->drawAll();
\r
1818 driver->endScene();
\r
1821 // Break out of menu-game loop to shut down cleanly
\r
1822 if(g_device->run() == false)
\r
1825 dstream<<"Dropping main menu"<<std::endl;
\r
1829 // Delete map if requested
\r
1830 if(menudata.delete_map)
\r
1832 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1834 error_message = L"Delete failed";
\r
1838 playername = wide_to_narrow(menudata.name);
\r
1839 address = wide_to_narrow(menudata.address);
\r
1840 port = stoi(wide_to_narrow(menudata.port));
\r
1841 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1843 // Check for valid parameters, restart menu if invalid.
\r
1844 if(playername == "")
\r
1846 error_message = L"Name required.";
\r
1851 g_settings.set("name", playername);
\r
1852 g_settings.set("address", address);
\r
1853 g_settings.set("port", itos(port));
\r
1854 // Update configuration file
\r
1855 if(configpath != "")
\r
1856 g_settings.updateConfigFile(configpath.c_str());
\r
1858 // Continue to game
\r
1862 // Break out of menu-game loop to shut down cleanly
\r
1863 if(g_device->run() == false)
\r
1867 Make a scope here so that the client and the server and other
\r
1868 stuff gets removed when disconnected or the irrlicht device
\r
1873 // This is set to true at the end of the scope
\r
1874 g_irrlicht->Shutdown(false);
\r
1877 Draw "Loading" screen
\r
1879 const wchar_t *text = L"Loading and connecting...";
\r
1880 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1881 core::vector2d<s32> textsize(300, text_height);
\r
1882 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1884 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1885 text, textrect, false, false);
\r
1886 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1888 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1889 guienv->drawAll();
\r
1890 driver->endScene();
\r
1892 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1896 SharedPtr will delete it when it goes out of scope.
\r
1898 SharedPtr<Server> server;
\r
1899 if(address == ""){
\r
1900 server = new Server(map_dir);
\r
1901 server->start(port);
\r
1908 Client client(device, playername.c_str(), draw_control);
\r
1910 g_client = &client;
\r
1912 Address connect_address(0,0,0,0, port);
\r
1915 //connect_address.Resolve("localhost");
\r
1916 connect_address.setAddress(127,0,0,1);
\r
1918 connect_address.Resolve(address.c_str());
\r
1920 catch(ResolveError &e)
\r
1922 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1924 error_message = L"Couldn't resolve address";
\r
1925 gui_loadingtext->remove();
\r
1929 dstream<<DTIME<<"Connecting to server at ";
\r
1930 connect_address.print(&dstream);
\r
1931 dstream<<std::endl;
\r
1932 client.connect(connect_address);
\r
1935 while(client.connectedAndInitialized() == false)
\r
1938 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1939 guienv->drawAll();
\r
1940 driver->endScene();
\r
1942 // Update client and server
\r
1946 if(server != NULL)
\r
1947 server->step(0.1);
\r
1953 catch(con::PeerNotFoundException &e)
\r
1955 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1957 error_message = L"Connection timed out.";
\r
1958 gui_loadingtext->remove();
\r
1965 /*scene::ISceneNode* skybox;
\r
1966 skybox = smgr->addSkyBoxSceneNode(
\r
1967 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1968 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1969 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1970 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1971 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1972 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1975 Create the camera node
\r
1978 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1979 0, // Camera parent
\r
1980 v3f(BS*100, BS*2, BS*100), // Look from
\r
1981 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1985 if(camera == NULL)
\r
1988 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1990 camera->setFOV(FOV_ANGLE);
\r
1992 // Just so big a value that everything rendered is visible
\r
1993 camera->setFarValue(100000*BS);
\r
1995 f32 camera_yaw = 0; // "right/left"
\r
1996 f32 camera_pitch = 0; // "up/down"
\r
2002 gui_loadingtext->remove();
\r
2005 Add some gui stuff
\r
2008 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2009 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
2011 // Test the text input system
\r
2012 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2014 /*GUIMessageMenu *menu =
\r
2015 new GUIMessageMenu(guienv, guiroot, -1,
\r
2020 // Launch pause menu
\r
2021 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2022 &g_menumgr))->drop();
\r
2025 guitext2->setVisible(true);
\r
2026 guitext_info->setVisible(true);
\r
2027 guitext_chat->setVisible(true);
\r
2030 Some statistics are collected in these
\r
2033 u32 beginscenetime = 0;
\r
2034 u32 scenetime = 0;
\r
2035 u32 endscenetime = 0;
\r
2038 //throw con::PeerNotFoundException("lol");
\r
2044 bool first_loop_after_window_activation = true;
\r
2046 // Time is in milliseconds
\r
2047 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2048 // NOTE: So we have to use getTime() and call run()s between them
\r
2049 u32 lasttime = device->getTimer()->getTime();
\r
2051 while(device->run())
\r
2053 if(g_disconnect_requested)
\r
2055 g_disconnect_requested = false;
\r
2060 Run global IrrlichtWrapper's main thread processing stuff
\r
2062 g_irrlicht->Run();
\r
2065 Random calculations
\r
2067 v2u32 screensize = driver->getScreenSize();
\r
2068 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2070 // Hilight boxes collected during the loop and displayed
\r
2071 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2074 std::wstring infotext;
\r
2076 //TimeTaker //timer1("//timer1");
\r
2078 // Time of frame without fps limit
\r
2082 // not using getRealTime is necessary for wine
\r
2083 u32 time = device->getTimer()->getTime();
\r
2084 if(time > lasttime)
\r
2085 busytime_u32 = time - lasttime;
\r
2088 busytime = busytime_u32 / 1000.0;
\r
2091 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2093 // Absolutelu necessary for wine!
\r
2100 updateViewingRange(busytime, &client);
\r
2107 float fps_max = g_settings.getFloat("fps_max");
\r
2108 u32 frametime_min = 1000./fps_max;
\r
2110 if(busytime_u32 < frametime_min)
\r
2112 u32 sleeptime = frametime_min - busytime_u32;
\r
2113 device->sleep(sleeptime);
\r
2117 // Absolutelu necessary for wine!
\r
2121 Time difference calculation
\r
2123 f32 dtime; // in seconds
\r
2125 u32 time = device->getTimer()->getTime();
\r
2126 if(time > lasttime)
\r
2127 dtime = (time - lasttime) / 1000.0;
\r
2133 Time average and jitter calculation
\r
2136 static f32 dtime_avg1 = 0.0;
\r
2137 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2138 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2140 static f32 dtime_jitter1_max_sample = 0.0;
\r
2141 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2143 static f32 jitter1_max = 0.0;
\r
2144 static f32 counter = 0.0;
\r
2145 if(dtime_jitter1 > jitter1_max)
\r
2146 jitter1_max = dtime_jitter1;
\r
2151 dtime_jitter1_max_sample = jitter1_max;
\r
2152 dtime_jitter1_max_fraction
\r
2153 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2154 jitter1_max = 0.0;
\r
2159 Busytime average and jitter calculation
\r
2162 static f32 busytime_avg1 = 0.0;
\r
2163 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2164 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2166 static f32 busytime_jitter1_max_sample = 0.0;
\r
2167 static f32 busytime_jitter1_min_sample = 0.0;
\r
2169 static f32 jitter1_max = 0.0;
\r
2170 static f32 jitter1_min = 0.0;
\r
2171 static f32 counter = 0.0;
\r
2172 if(busytime_jitter1 > jitter1_max)
\r
2173 jitter1_max = busytime_jitter1;
\r
2174 if(busytime_jitter1 < jitter1_min)
\r
2175 jitter1_min = busytime_jitter1;
\r
2177 if(counter > 0.0){
\r
2179 busytime_jitter1_max_sample = jitter1_max;
\r
2180 busytime_jitter1_min_sample = jitter1_min;
\r
2181 jitter1_max = 0.0;
\r
2182 jitter1_min = 0.0;
\r
2187 Debug info for client
\r
2190 static float counter = 0.0;
\r
2195 client.printDebugInfo(std::cout);
\r
2200 Input handler step()
\r
2202 g_input->step(dtime);
\r
2205 Player speed control
\r
2214 bool a_superspeed,
\r
2217 PlayerControl control(
\r
2218 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2219 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2220 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2221 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2222 g_input->isKeyDown(irr::KEY_SPACE),
\r
2223 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2227 client.setPlayerControl(control);
\r
2231 Process environment
\r
2235 //TimeTaker timer("client.step(dtime)");
\r
2236 client.step(dtime);
\r
2237 //client.step(dtime_avg1);
\r
2240 if(server != NULL)
\r
2242 //TimeTaker timer("server->step(dtime)");
\r
2243 server->step(dtime);
\r
2246 v3f player_position = client.getPlayerPosition();
\r
2248 //TimeTaker //timer2("//timer2");
\r
2251 Mouse and camera control
\r
2254 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2257 device->getCursorControl()->setVisible(false);
\r
2259 if(first_loop_after_window_activation){
\r
2260 //std::cout<<"window active, first loop"<<std::endl;
\r
2261 first_loop_after_window_activation = false;
\r
2264 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2265 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2266 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2267 camera_yaw -= dx*0.2;
\r
2268 camera_pitch += dy*0.2;
\r
2269 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2270 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2272 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2275 device->getCursorControl()->setVisible(true);
\r
2277 //std::cout<<"window inactive"<<std::endl;
\r
2278 first_loop_after_window_activation = true;
\r
2281 camera_yaw = wrapDegrees(camera_yaw);
\r
2282 camera_pitch = wrapDegrees(camera_pitch);
\r
2284 v3f camera_direction = v3f(0,0,1);
\r
2285 camera_direction.rotateYZBy(camera_pitch);
\r
2286 camera_direction.rotateXZBy(camera_yaw);
\r
2288 // This is at the height of the eyes of the current figure
\r
2289 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2290 // This is more like in minecraft
\r
2291 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2293 camera->setPosition(camera_position);
\r
2294 // *100.0 helps in large map coordinates
\r
2295 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2297 if(FIELD_OF_VIEW_TEST){
\r
2298 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2299 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2302 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2303 //TimeTaker timer("client.updateCamera");
\r
2304 client.updateCamera(camera_position, camera_direction);
\r
2308 //TimeTaker //timer3("//timer3");
\r
2311 Calculate what block is the crosshair pointing to
\r
2314 //u32 t1 = device->getTimer()->getRealTime();
\r
2316 //f32 d = 4; // max. distance
\r
2317 f32 d = 4; // max. distance
\r
2318 core::line3d<f32> shootline(camera_position,
\r
2319 camera_position + camera_direction * BS * (d+1));
\r
2321 MapBlockObject *selected_object = client.getSelectedObject
\r
2322 (d*BS, camera_position, shootline);
\r
2325 If it's pointing to a MapBlockObject
\r
2328 if(selected_object != NULL)
\r
2330 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2332 core::aabbox3d<f32> box_on_map
\r
2333 = selected_object->getSelectionBoxOnMap();
\r
2335 hilightboxes.push_back(box_on_map);
\r
2337 infotext = narrow_to_wide(selected_object->infoText());
\r
2339 if(g_input->getLeftClicked())
\r
2341 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2342 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2343 selected_object->getId(), g_selected_item);
\r
2345 else if(g_input->getRightClicked())
\r
2347 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2349 Check if we want to modify the object ourselves
\r
2351 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2353 dstream<<"Sign object right-clicked"<<std::endl;
\r
2355 if(random_input == false)
\r
2357 // Get a new text for it
\r
2359 TextDest *dest = new TextDestSign(
\r
2360 selected_object->getBlock()->getPos(),
\r
2361 selected_object->getId(),
\r
2364 SignObject *sign_object = (SignObject*)selected_object;
\r
2366 std::wstring wtext =
\r
2367 narrow_to_wide(sign_object->getText());
\r
2369 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2375 Otherwise pass the event to the server as-is
\r
2379 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2380 selected_object->getId(), g_selected_item);
\r
2384 else // selected_object == NULL
\r
2388 Find out which node we are pointing at
\r
2391 bool nodefound = false;
\r
2393 v3s16 neighbourpos;
\r
2394 core::aabbox3d<f32> nodehilightbox;
\r
2395 f32 mindistance = BS * 1001;
\r
2397 v3s16 pos_i = floatToInt(player_position);
\r
2399 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2403 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2404 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2405 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2406 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2407 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2408 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2410 for(s16 y = ystart; y <= yend; y++)
\r
2411 for(s16 z = zstart; z <= zend; z++)
\r
2412 for(s16 x = xstart; x <= xend; x++)
\r
2417 n = client.getNode(v3s16(x,y,z));
\r
2418 if(content_pointable(n.d) == false)
\r
2421 catch(InvalidPositionException &e)
\r
2427 v3f npf = intToFloat(np);
\r
2432 v3s16(0,0,1), // back
\r
2433 v3s16(0,1,0), // top
\r
2434 v3s16(1,0,0), // right
\r
2435 v3s16(0,0,-1), // front
\r
2436 v3s16(0,-1,0), // bottom
\r
2437 v3s16(-1,0,0), // left
\r
2443 if(n.d == CONTENT_TORCH)
\r
2445 v3s16 dir = unpackDir(n.dir);
\r
2446 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2447 dir_f *= BS/2 - BS/6 - BS/20;
\r
2448 v3f cpf = npf + dir_f;
\r
2449 f32 distance = (cpf - camera_position).getLength();
\r
2451 core::aabbox3d<f32> box;
\r
2454 if(dir == v3s16(0,-1,0))
\r
2456 box = core::aabbox3d<f32>(
\r
2457 npf - v3f(BS/6, BS/2, BS/6),
\r
2458 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2462 else if(dir == v3s16(0,1,0))
\r
2464 box = core::aabbox3d<f32>(
\r
2465 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2466 npf + v3f(BS/6, BS/2, BS/6)
\r
2472 box = core::aabbox3d<f32>(
\r
2473 cpf - v3f(BS/6, BS/3, BS/6),
\r
2474 cpf + v3f(BS/6, BS/3, BS/6)
\r
2478 if(distance < mindistance)
\r
2480 if(box.intersectsWithLine(shootline))
\r
2484 neighbourpos = np;
\r
2485 mindistance = distance;
\r
2486 nodehilightbox = box;
\r
2495 for(u16 i=0; i<6; i++)
\r
2497 v3f dir_f = v3f(dirs[i].X,
\r
2498 dirs[i].Y, dirs[i].Z);
\r
2499 v3f centerpoint = npf + dir_f * BS/2;
\r
2501 (centerpoint - camera_position).getLength();
\r
2503 if(distance < mindistance)
\r
2505 core::CMatrix4<f32> m;
\r
2506 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2508 // This is the back face
\r
2509 v3f corners[2] = {
\r
2510 v3f(BS/2, BS/2, BS/2),
\r
2511 v3f(-BS/2, -BS/2, BS/2+d)
\r
2514 for(u16 j=0; j<2; j++)
\r
2516 m.rotateVect(corners[j]);
\r
2517 corners[j] += npf;
\r
2520 core::aabbox3d<f32> facebox(corners[0]);
\r
2521 facebox.addInternalPoint(corners[1]);
\r
2523 if(facebox.intersectsWithLine(shootline))
\r
2527 neighbourpos = np + dirs[i];
\r
2528 mindistance = distance;
\r
2530 //nodehilightbox = facebox;
\r
2532 const float d = 0.502;
\r
2533 core::aabbox3d<f32> nodebox
\r
2534 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2535 v3f nodepos_f = intToFloat(nodepos);
\r
2536 nodebox.MinEdge += nodepos_f;
\r
2537 nodebox.MaxEdge += nodepos_f;
\r
2538 nodehilightbox = nodebox;
\r
2540 } // if distance < mindistance
\r
2542 } // regular block
\r
2545 static float nodig_delay_counter = 0.0;
\r
2549 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2551 static float dig_time = 0.0;
\r
2552 static u16 dig_index = 0;
\r
2554 // Visualize selection
\r
2556 hilightboxes.push_back(nodehilightbox);
\r
2560 if(g_input->getLeftReleased())
\r
2562 client.clearTempMod(nodepos);
\r
2566 if(nodig_delay_counter > 0.0)
\r
2568 nodig_delay_counter -= dtime;
\r
2572 if(nodepos != nodepos_old)
\r
2574 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2575 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2577 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2579 client.clearTempMod(nodepos_old);
\r
2584 if(g_input->getLeftClicked() ||
\r
2585 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2587 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2588 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2590 if(g_input->getLeftClicked())
\r
2592 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2594 if(g_input->getLeftState())
\r
2596 MapNode n = client.getNode(nodepos);
\r
2598 // Get tool name. Default is "" = bare hands
\r
2599 std::string toolname = "";
\r
2600 InventoryList *mlist = local_inventory.getList("main");
\r
2603 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2604 if(item && (std::string)item->getName() == "ToolItem")
\r
2606 ToolItem *titem = (ToolItem*)item;
\r
2607 toolname = titem->getToolName();
\r
2611 // Get digging properties for material and tool
\r
2612 u8 material = n.d;
\r
2613 DiggingProperties prop =
\r
2614 getDiggingProperties(material, toolname);
\r
2616 float dig_time_complete = 0.0;
\r
2618 if(prop.diggable == false)
\r
2620 /*dstream<<"Material "<<(int)material
\r
2621 <<" not diggable with \""
\r
2622 <<toolname<<"\""<<std::endl;*/
\r
2623 // I guess nobody will wait for this long
\r
2624 dig_time_complete = 10000000.0;
\r
2628 dig_time_complete = prop.time;
\r
2631 if(dig_time_complete >= 0.001)
\r
2633 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2634 * dig_time/dig_time_complete);
\r
2636 // This is for torches
\r
2639 dig_index = CRACK_ANIMATION_LENGTH;
\r
2642 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2644 //TimeTaker timer("client.setTempMod");
\r
2645 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2646 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2650 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2651 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2652 client.clearTempMod(nodepos);
\r
2653 client.removeNode(nodepos);
\r
2657 nodig_delay_counter = dig_time_complete
\r
2658 / (float)CRACK_ANIMATION_LENGTH;
\r
2660 // We don't want a corresponding delay to
\r
2661 // very time consuming nodes
\r
2662 if(nodig_delay_counter > 0.5)
\r
2664 nodig_delay_counter = 0.5;
\r
2666 // We want a slight delay to very little
\r
2667 // time consuming nodes
\r
2668 float mindelay = 0.15;
\r
2669 if(nodig_delay_counter < mindelay)
\r
2671 nodig_delay_counter = mindelay;
\r
2675 dig_time += dtime;
\r
2679 if(g_input->getRightClicked())
\r
2681 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2682 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2685 nodepos_old = nodepos;
\r
2690 } // selected_object == NULL
\r
2692 g_input->resetLeftClicked();
\r
2693 g_input->resetRightClicked();
\r
2695 if(g_input->getLeftReleased())
\r
2697 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2699 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2701 if(g_input->getRightReleased())
\r
2703 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2707 g_input->resetLeftReleased();
\r
2708 g_input->resetRightReleased();
\r
2711 Calculate stuff for drawing
\r
2714 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2716 u32 daynight_ratio = client.getDayNightRatio();
\r
2717 /*video::SColor bgcolor = video::SColor(
\r
2719 skycolor.getRed() * daynight_ratio / 1000,
\r
2720 skycolor.getGreen() * daynight_ratio / 1000,
\r
2721 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2723 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2724 video::SColor bgcolor = video::SColor(
\r
2726 skycolor.getRed() * l / 255,
\r
2727 skycolor.getGreen() * l / 255,
\r
2728 skycolor.getBlue() * l / 255);
\r
2734 if(g_settings.getBool("enable_fog") == true)
\r
2736 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
2737 f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/3*BS;
\r
2738 if(draw_control.range_all)
\r
2739 range = 100000*BS;
\r
2743 video::EFT_FOG_LINEAR,
\r
2747 false, // pixel fog
\r
2748 false // range fog
\r
2754 Update gui stuff (0ms)
\r
2757 //TimeTaker guiupdatetimer("Gui updating");
\r
2760 wchar_t temptext[150];
\r
2762 static float drawtime_avg = 0;
\r
2763 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2764 static float beginscenetime_avg = 0;
\r
2765 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2766 static float scenetime_avg = 0;
\r
2767 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2768 static float endscenetime_avg = 0;
\r
2769 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2771 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2773 L", R: range_all=%i"
\r
2775 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2777 draw_control.range_all,
\r
2779 beginscenetime_avg,
\r
2784 guitext->setText(temptext);
\r
2788 wchar_t temptext[150];
\r
2789 swprintf(temptext, 150,
\r
2790 L"(% .1f, % .1f, % .1f)"
\r
2791 L" (% .3f < btime_jitter < % .3f"
\r
2792 L", dtime_jitter = % .1f %%"
\r
2793 L", v_range = %.1f)",
\r
2794 player_position.X/BS,
\r
2795 player_position.Y/BS,
\r
2796 player_position.Z/BS,
\r
2797 busytime_jitter1_min_sample,
\r
2798 busytime_jitter1_max_sample,
\r
2799 dtime_jitter1_max_fraction * 100.0,
\r
2800 draw_control.wanted_range
\r
2803 guitext2->setText(temptext);
\r
2807 guitext_info->setText(infotext.c_str());
\r
2811 Get chat messages from client
\r
2814 // Get new messages
\r
2815 std::wstring message;
\r
2816 while(client.getChatMessage(message))
\r
2818 chat_lines.push_back(ChatLine(message));
\r
2819 /*if(chat_lines.size() > 6)
\r
2821 core::list<ChatLine>::Iterator
\r
2822 i = chat_lines.begin();
\r
2823 chat_lines.erase(i);
\r
2826 // Append them to form the whole static text and throw
\r
2827 // it to the gui element
\r
2828 std::wstring whole;
\r
2829 // This will correspond to the line number counted from
\r
2830 // top to bottom, from size-1 to 0
\r
2831 s16 line_number = chat_lines.size();
\r
2832 // Count of messages to be removed from the top
\r
2833 u16 to_be_removed_count = 0;
\r
2834 for(core::list<ChatLine>::Iterator
\r
2835 i = chat_lines.begin();
\r
2836 i != chat_lines.end(); i++)
\r
2838 // After this, line number is valid for this loop
\r
2841 (*i).age += dtime;
\r
2843 This results in a maximum age of 60*6 to the
\r
2844 lowermost line and a maximum of 6 lines
\r
2846 float allowed_age = (6-line_number) * 60.0;
\r
2848 if((*i).age > allowed_age)
\r
2850 to_be_removed_count++;
\r
2853 whole += (*i).text + L'\n';
\r
2855 for(u16 i=0; i<to_be_removed_count; i++)
\r
2857 core::list<ChatLine>::Iterator
\r
2858 it = chat_lines.begin();
\r
2859 chat_lines.erase(it);
\r
2861 guitext_chat->setText(whole.c_str());
\r
2862 // Update gui element size and position
\r
2863 core::rect<s32> rect(
\r
2865 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2866 screensize.X - 10,
\r
2869 guitext_chat->setRelativePosition(rect);
\r
2871 if(chat_lines.size() == 0)
\r
2872 guitext_chat->setVisible(false);
\r
2874 guitext_chat->setVisible(true);
\r
2881 static u16 old_selected_item = 65535;
\r
2882 if(client.getLocalInventoryUpdated()
\r
2883 || g_selected_item != old_selected_item)
\r
2885 old_selected_item = g_selected_item;
\r
2886 //std::cout<<"Updating local inventory"<<std::endl;
\r
2887 client.getLocalInventory(local_inventory);
\r
2888 quick_inventory->setSelection(g_selected_item);
\r
2889 quick_inventory->update();
\r
2893 Send actions returned by the inventory menu
\r
2895 while(inventory_action_queue.size() != 0)
\r
2897 InventoryAction *a = inventory_action_queue.pop_front();
\r
2899 client.sendInventoryAction(a);
\r
2908 TimeTaker drawtimer("Drawing");
\r
2912 TimeTaker timer("beginScene");
\r
2913 driver->beginScene(true, true, bgcolor);
\r
2914 //driver->beginScene(false, true, bgcolor);
\r
2915 beginscenetime = timer.stop(true);
\r
2920 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2923 TimeTaker timer("smgr");
\r
2925 scenetime = timer.stop(true);
\r
2929 //TimeTaker timer9("auxiliary drawings");
\r
2933 //TimeTaker //timer10("//timer10");
\r
2935 video::SMaterial m;
\r
2936 //m.Thickness = 10;
\r
2938 m.Lighting = false;
\r
2939 driver->setMaterial(m);
\r
2941 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2943 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2944 i != hilightboxes.end(); i++)
\r
2946 /*std::cout<<"hilightbox min="
\r
2947 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2949 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2951 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2957 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2958 displaycenter + core::vector2d<s32>(10,0),
\r
2959 video::SColor(255,255,255,255));
\r
2960 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2961 displaycenter + core::vector2d<s32>(0,10),
\r
2962 video::SColor(255,255,255,255));
\r
2967 //TimeTaker //timer11("//timer11");
\r
2973 guienv->drawAll();
\r
2977 TimeTaker timer("endScene");
\r
2978 driver->endScene();
\r
2979 endscenetime = timer.stop(true);
\r
2982 drawtime = drawtimer.stop(true);
\r
2988 static s16 lastFPS = 0;
\r
2989 //u16 fps = driver->getFPS();
\r
2990 u16 fps = (1.0/dtime_avg1);
\r
2992 if (lastFPS != fps)
\r
2994 core::stringw str = L"Minetest [";
\r
2995 str += driver->getName();
\r
2999 device->setWindowCaption(str.c_str());
\r
3005 device->yield();*/
\r
3008 delete quick_inventory;
\r
3011 Disable texture fetches and other stuff that is queued
\r
3012 to be processed by the main loop.
\r
3014 This has to be done before client goes out of scope.
\r
3016 g_irrlicht->Shutdown(true);
\r
3018 } // client and server are deleted at this point
\r
3021 catch(con::PeerNotFoundException &e)
\r
3023 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3024 error_message = L"Connection timed out.";
\r
3027 } // Menu-game loop
\r
3032 In the end, delete the Irrlicht device.
\r
3037 Update configuration file
\r
3039 /*if(configpath != "")
\r
3041 g_settings.updateConfigFile(configpath.c_str());
\r
3044 END_DEBUG_EXCEPTION_HANDLER
\r
3046 debugstreams_deinit();
\r