if something is already in it)\r
- Use it in active block queue in water flowing\r
\r
-SUGG: Signs could be done in the same way as torches. For this, blocks\r
- need an additional metadata field for the texts\r
-\r
SUGG: Precalculate lighting translation table at runtime (at startup)\r
\r
SUGG: A version number to blocks, which increments when the block is\r
- This can then be used to make sure the most recent version of\r
a block has been sent to client\r
\r
-TODO: Stop player if focus of window is taken away (go to pause mode)\r
+SUGG: Make the amount of blocks sending to client and the total\r
+ amount of blocks dynamically limited. Transferring blocks is the\r
+ main network eater of this system, so it is the one that has\r
+ to be throttled so that RTTs stay low.\r
+\r
+SUGG: Meshes of blocks could be split into 6 meshes facing into\r
+ different directions and then only those drawn that need to be\r
+ - Also an 1-dimensional tile map would be nice probably\r
+\r
+TODO: Untie client network operations from framerate\r
+ - Needs some input queues or something\r
+ - Not really necessary?\r
\r
TODO: Combine MapBlock's face caches to so big pieces that VBO\r
gets used\r
TODO: Better dungeons\r
TODO: Cliffs, arcs\r
\r
-TODO: Menus\r
-\r
-TODO: Mobs\r
- - Server:\r
- - One single map container with ids as keys\r
- - Client:\r
- - ?\r
-TODO: - Keep track of the place of the mob in the last few hundreth's\r
- of a second - then, if a player hits it, take the value that is\r
- avg_rtt/2 before the moment the packet is received.\r
-TODO: - Scripting\r
-\r
-TODO: Moving players more smoothly. Calculate moving animation\r
- in a way that doesn't make the player jump to the right place\r
- immediately when the server sends a new position\r
+TODO: Startup and configuration menu\r
\r
TODO: There are some lighting-related todos and fixmes in\r
ServerMap::emergeBlock\r
is not in the middle of an ocean (some land to stand on at\r
least) and save it in map config.\r
\r
-TODO: Make the amount of blocks sending to client and the total\r
- amount of blocks dynamically limited. Transferring blocks is the\r
- main network eater of this system, so it is the one that has\r
- to be throttled so that RTTs stay low.\r
-\r
-TODO: Server to load starting inventory from disk\r
-\r
TODO: Players to only be hidden when the client quits.\r
TODO: - Players to be saved on disk, with inventory\r
TODO: Players to be saved as text in map/players/<name>\r
+TODO: Player inventory to be saved on disk\r
\r
TODO: Make fetching sector's blocks more efficient when rendering\r
sectors that have very large amounts of blocks (on client)\r
- TODO: For incoming blocks, time difference is calculated and\r
objects are stepped according to it.\r
\r
-TODO: Add config parameters for server's sending and generating distance\r
-\r
TODO: Copy the text of the last picked sign to inventory in creative\r
mode\r
\r
-TODO: Untie client network operations from framerate\r
- - Needs some input queues or something\r
-\r
TODO: Get rid of GotSplitPacketException\r
\r
TODO: Check what goes wrong with caching map to disk (Kray)\r
\r
TODO: Remove LazyMeshUpdater. It is not used as supposed.\r
\r
-TODO: Node cracking animation when digging\r
- - TODO: A way to generate new textures by combining textures\r
- - TODO: Mesh update to fetch cracked faces from the former\r
+TODO: TOSERVER_LEAVE\r
\r
-TODO: Add server unused sector deletion settings to settings\r
+TODO: Better handling of objects and mobs\r
+ - Scripting?\r
+ - There has to be some way to do it with less spaghetti code\r
+ - Make separate classes for client and server\r
+ - Client should not discriminate between blocks, server should\r
+ - Make other players utilize the same framework\r
+ - This is also needed for objects that don't get sent to client\r
+ but are used for triggers etc\r
\r
-TODO: TOSERVER_LEAVE\r
+TODO: Draw big amounts of torches better (that is, throw them in the\r
+ same meshbuffer (can the meshcollector class be used?))\r
+\r
+TODO: Check if the usage of Client::isFetchingBlocks() in\r
+ updateViewingRange() actually does something\r
+\r
+TODO: Make an option to the server to disable building and digging near\r
+ the starting position\r
+\r
+SUGG: Signs could be done in the same way as torches. For this, blocks\r
+ need an additional metadata field for the texts\r
+ - This is also needed for item container chests\r
+TODO: There has to be some better way to handle static objects than to\r
+ send them all the time. This affects signs and item objects.\r
\r
Doing now:\r
======================================================================\r
\r
+TODO: When server sees that client is removing an inexistent block or\r
+ adding a block to an existent position, resend the MapBlock.\r
+\r
+TODO: Fix viewing range updater's oscillation when there is large non-\r
+ linearity in range-speed relation\r
+\r
======================================================================\r
\r
*/\r
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")\r
#endif\r
\r
-#ifdef _WIN32\r
- #define WIN32_LEAN_AND_MEAN\r
- #include <windows.h>\r
- #define sleep_ms(x) Sleep(x)\r
-#else\r
- #include <unistd.h>\r
- #define sleep_ms(x) usleep(x*1000)\r
-#endif\r
-\r
#include <iostream>\r
#include <fstream>\r
-#include <time.h>\r
#include <jmutexautolock.h>\r
#include <locale.h>\r
#include "common_irrlicht.h"\r
#include "constants.h"\r
#include "strfnd.h"\r
#include "porting.h"\r
+#include "irrlichtwrapper.h"\r
+#include "gettime.h"\r
+#include "porting.h"\r
#include "guiPauseMenu.h"\r
+#include "guiInventoryMenu.h"\r
+#include "guiTextInputMenu.h"\r
+#include "materials.h"\r
+#include "guiMessageMenu.h"\r
\r
-IrrlichtDevice *g_device = NULL;\r
-\r
-/*const char *g_content_filenames[MATERIALS_COUNT] =\r
-{\r
- "../data/stone.png",\r
- "../data/grass.png",\r
- "../data/water.png",\r
- "../data/torch_on_floor.png",\r
- "../data/tree.png",\r
- "../data/leaves.png",\r
- "../data/grass_footsteps.png",\r
- "../data/mese.png",\r
- "../data/mud.png",\r
- "../data/water.png", // CONTENT_OCEAN\r
-};\r
-\r
-// Material cache\r
-video::SMaterial g_materials[MATERIALS_COUNT];*/\r
-\r
-// Texture cache\r
-TextureCache g_texturecache;\r
-\r
-\r
-// All range-related stuff below is locked behind this\r
-JMutex g_range_mutex;\r
-\r
-// Blocks are viewed in this range from the player\r
-s16 g_viewing_range_nodes = 60;\r
-//s16 g_viewing_range_nodes = 0;\r
-\r
-// This is updated by the client's fetchBlocks routine\r
-//s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;\r
-\r
-// If true, the preceding value has no meaning and all blocks\r
-// already existing in memory are drawn\r
-bool g_viewing_range_all = false;\r
-\r
-// This is the freetime ratio imposed by the dynamic viewing\r
-// range changing code.\r
-// It is controlled by the main loop to the smallest value that\r
-// inhibits glitches (dtime jitter) in the main loop.\r
-//float g_freetime_ratio = FREETIME_RATIO_MAX;\r
+IrrlichtWrapper *g_irrlicht;\r
\r
+MapDrawControl draw_control;\r
\r
/*\r
Settings.\r
\r
Settings g_settings;\r
\r
-// Sets default settings\r
-void set_default_settings()\r
-{\r
- // Client stuff\r
- g_settings.setDefault("wanted_fps", "30");\r
- g_settings.setDefault("fps_max", "60");\r
- g_settings.setDefault("viewing_range_nodes_max", "300");\r
- g_settings.setDefault("viewing_range_nodes_min", "35");\r
- g_settings.setDefault("screenW", "");\r
- g_settings.setDefault("screenH", "");\r
- g_settings.setDefault("host_game", "");\r
- g_settings.setDefault("port", "");\r
- g_settings.setDefault("address", "");\r
- g_settings.setDefault("name", "");\r
- g_settings.setDefault("random_input", "false");\r
- g_settings.setDefault("client_delete_unused_sectors_timeout", "1200");\r
- g_settings.setDefault("enable_fog", "true");\r
-\r
- // Server stuff\r
- g_settings.setDefault("creative_mode", "false");\r
- g_settings.setDefault("heightmap_blocksize", "32");\r
- g_settings.setDefault("height_randmax", "constant 50.0");\r
- g_settings.setDefault("height_randfactor", "constant 0.6");\r
- g_settings.setDefault("height_base", "linear 0 0 0");\r
- g_settings.setDefault("plants_amount", "1.0");\r
- g_settings.setDefault("ravines_amount", "1.0");\r
- g_settings.setDefault("objectdata_interval", "0.2");\r
- g_settings.setDefault("active_object_range", "2");\r
- g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");\r
- g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");\r
- g_settings.setDefault("disable_water_climb", "true");\r
- g_settings.setDefault("endless_water", "true");\r
- g_settings.setDefault("max_block_send_distance", "5");\r
- g_settings.setDefault("max_block_generate_distance", "4");\r
-}\r
+extern void set_default_settings();\r
\r
/*\r
Random stuff\r
*/\r
\r
-//u16 g_selected_material = 0;\r
-u16 g_selected_item = 0;\r
+IrrlichtDevice *g_device = NULL;\r
+Client *g_client = NULL;\r
\r
-bool g_esc_pressed = false;\r
+/*\r
+ GUI Stuff\r
+*/\r
+gui::IGUIEnvironment* guienv = NULL;\r
+gui::IGUIStaticText *guiroot = NULL;\r
+int g_active_menu_count = 0;\r
\r
-std::wstring g_text_buffer;\r
-bool g_text_buffer_accepted = false;\r
+bool noMenuActive()\r
+{\r
+ return (g_active_menu_count == 0);\r
+}\r
\r
-// When true, the mouse and keyboard are grabbed\r
-bool g_game_focused = true;\r
+// Inventory actions from the menu are buffered here before sending\r
+Queue<InventoryAction*> inventory_action_queue;\r
+// This is a copy of the inventory that the client's environment has\r
+Inventory local_inventory;\r
+\r
+u16 g_selected_item = 0;\r
\r
/*\r
Debug streams\r
std::ostream *dout_client_ptr = &dstream;\r
std::ostream *derr_client_ptr = &dstream;\r
\r
+/*\r
+ gettime.h implementation\r
+*/\r
+\r
+u32 getTimeMs()\r
+{\r
+ /*\r
+ Use irrlicht because it is more precise than porting.h's\r
+ getTimeMs()\r
+ */\r
+ if(g_irrlicht == NULL)\r
+ return 0;\r
+ return g_irrlicht->getTime();\r
+}\r
\r
/*\r
- Timestamp stuff\r
+ Text input system\r
*/\r
\r
-JMutex g_timestamp_mutex;\r
-//std::string g_timestamp;\r
+struct TextDestSign : public TextDest\r
+{\r
+ TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
+ {\r
+ m_blockpos = blockpos;\r
+ m_id = id;\r
+ m_client = client;\r
+ }\r
+ void gotText(std::wstring text)\r
+ {\r
+ std::string ntext = wide_to_narrow(text);\r
+ dstream<<"Changing text of a sign object: "\r
+ <<ntext<<std::endl;\r
+ m_client->sendSignText(m_blockpos, m_id, ntext);\r
+ }\r
+\r
+ v3s16 m_blockpos;\r
+ s16 m_id;\r
+ Client *m_client;\r
+};\r
\r
-std::string getTimestamp()\r
+struct TextDestChat : public TextDest\r
{\r
- if(g_timestamp_mutex.IsInitialized()==false)\r
- return "";\r
- JMutexAutoLock lock(g_timestamp_mutex);\r
- //return g_timestamp;\r
- time_t t = time(NULL);\r
- struct tm *tm = localtime(&t);\r
- char cs[20];\r
- strftime(cs, 20, "%H:%M:%S", tm);\r
- return cs;\r
-}\r
+ TextDestChat(Client *client)\r
+ {\r
+ m_client = client;\r
+ }\r
+ void gotText(std::wstring text)\r
+ {\r
+ m_client->sendChatMessage(text);\r
+ m_client->addChatMessage(text);\r
+ }\r
+\r
+ Client *m_client;\r
+};\r
\r
class MyEventReceiver : public IEventReceiver\r
{\r
// This is the one method that we have to implement\r
virtual bool OnEvent(const SEvent& event)\r
{\r
+ /*\r
+ React to nothing here if a menu is active\r
+ */\r
+ if(noMenuActive() == false)\r
+ {\r
+ clearInput();\r
+ return false;\r
+ }\r
+\r
// Remember whether each key is down or up\r
if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
{\r
if(event.KeyInput.PressedDown)\r
{\r
//dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;\r
- if(g_game_focused == false)\r
+ \r
+ /*\r
+ Launch menus\r
+ */\r
+\r
+ if(guienv != NULL && guiroot != NULL && g_device != NULL)\r
{\r
- s16 key = event.KeyInput.Key;\r
- if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)\r
+ if(event.KeyInput.Key == irr::KEY_ESCAPE)\r
{\r
- g_text_buffer_accepted = true;\r
+ dstream<<DTIME<<"MyEventReceiver: "\r
+ <<"Launching pause menu"<<std::endl;\r
+ // It will delete itself by itself\r
+ (new GUIPauseMenu(guienv, guiroot, -1, g_device,\r
+ &g_active_menu_count))->drop();\r
+ return true;\r
}\r
- else if(key == irr::KEY_BACK)\r
- {\r
- if(g_text_buffer.size() > 0)\r
- g_text_buffer = g_text_buffer.substr\r
- (0, g_text_buffer.size()-1);\r
- }\r
- else\r
+ if(event.KeyInput.Key == irr::KEY_KEY_I)\r
{\r
- wchar_t wc = event.KeyInput.Char;\r
- if(wc != 0)\r
- g_text_buffer += wc;\r
+ dstream<<DTIME<<"MyEventReceiver: "\r
+ <<"Launching inventory"<<std::endl;\r
+ (new GUIInventoryMenu(guienv, guiroot, -1,\r
+ &local_inventory, &inventory_action_queue,\r
+ &g_active_menu_count))->drop();\r
+ return true;\r
}\r
- }\r
- \r
- if(event.KeyInput.Key == irr::KEY_ESCAPE)\r
- {\r
- //TODO: Not used anymore?\r
- if(g_game_focused == true)\r
+ if(event.KeyInput.Key == irr::KEY_KEY_T)\r
{\r
- dstream<<DTIME<<"ESC pressed"<<std::endl;\r
- g_esc_pressed = true;\r
+ TextDest *dest = new TextDestChat(g_client);\r
+\r
+ (new GUITextInputMenu(guienv, guiroot, -1,\r
+ &g_active_menu_count, dest,\r
+ L""))->drop();\r
}\r
}\r
\r
// Material selection\r
if(event.KeyInput.Key == irr::KEY_KEY_F)\r
{\r
- if(g_game_focused == true)\r
- {\r
- if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
- g_selected_item++;\r
- else\r
- g_selected_item = 0;\r
- dstream<<DTIME<<"Selected item: "\r
- <<g_selected_item<<std::endl;\r
- }\r
+ if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
+ g_selected_item++;\r
+ else\r
+ g_selected_item = 0;\r
+ dstream<<DTIME<<"Selected item: "\r
+ <<g_selected_item<<std::endl;\r
}\r
\r
// Viewing range selection\r
- if(event.KeyInput.Key == irr::KEY_KEY_R\r
- && g_game_focused)\r
+ if(event.KeyInput.Key == irr::KEY_KEY_R)\r
{\r
- JMutexAutoLock lock(g_range_mutex);\r
- if(g_viewing_range_all)\r
+ if(draw_control.range_all)\r
{\r
- g_viewing_range_all = false;\r
+ draw_control.range_all = false;\r
dstream<<DTIME<<"Disabled full viewing range"<<std::endl;\r
}\r
else\r
{\r
- g_viewing_range_all = true;\r
+ draw_control.range_all = true;\r
dstream<<DTIME<<"Enabled full viewing range"<<std::endl;\r
}\r
}\r
\r
// Print debug stacks\r
- if(event.KeyInput.Key == irr::KEY_KEY_P\r
- && g_game_focused)\r
+ if(event.KeyInput.Key == irr::KEY_KEY_P)\r
{\r
dstream<<"-----------------------------------------"\r
<<std::endl;\r
\r
if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
{\r
- left_active = event.MouseInput.isLeftPressed();\r
- middle_active = event.MouseInput.isMiddlePressed();\r
- right_active = event.MouseInput.isRightPressed();\r
-\r
- if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
- {\r
- leftclicked = true;\r
- }\r
- if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
- {\r
- rightclicked = true;\r
- }\r
- if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
- {\r
- leftreleased = true;\r
- }\r
- if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
+ if(noMenuActive() == false)\r
{\r
- rightreleased = true;\r
+ left_active = false;\r
+ middle_active = false;\r
+ right_active = false;\r
}\r
- if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
+ else\r
{\r
- /*dstream<<"event.MouseInput.Wheel="\r
- <<event.MouseInput.Wheel<<std::endl;*/\r
- if(event.MouseInput.Wheel < 0)\r
+ //dstream<<"MyEventReceiver: mouse input"<<std::endl;\r
+ left_active = event.MouseInput.isLeftPressed();\r
+ middle_active = event.MouseInput.isMiddlePressed();\r
+ right_active = event.MouseInput.isRightPressed();\r
+\r
+ if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
{\r
- if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
- g_selected_item++;\r
- else\r
- g_selected_item = 0;\r
+ leftclicked = true;\r
}\r
- else if(event.MouseInput.Wheel > 0)\r
+ if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
{\r
- if(g_selected_item > 0)\r
- g_selected_item--;\r
- else\r
- g_selected_item = PLAYER_INVENTORY_SIZE-1;\r
+ rightclicked = true;\r
+ }\r
+ if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
+ {\r
+ leftreleased = true;\r
+ }\r
+ if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
+ {\r
+ rightreleased = true;\r
+ }\r
+ if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
+ {\r
+ /*dstream<<"event.MouseInput.Wheel="\r
+ <<event.MouseInput.Wheel<<std::endl;*/\r
+ if(event.MouseInput.Wheel < 0)\r
+ {\r
+ if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
+ g_selected_item++;\r
+ else\r
+ g_selected_item = 0;\r
+ }\r
+ else if(event.MouseInput.Wheel > 0)\r
+ {\r
+ if(g_selected_item > 0)\r
+ g_selected_item--;\r
+ else\r
+ g_selected_item = PLAYER_INVENTORY_SIZE-1;\r
+ }\r
}\r
}\r
}\r
return keyIsDown[keyCode];\r
}\r
\r
- MyEventReceiver()\r
+ void clearInput()\r
{\r
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
keyIsDown[i] = false;\r
+ \r
leftclicked = false;\r
rightclicked = false;\r
leftreleased = false;\r
right_active = false;\r
}\r
\r
+ MyEventReceiver()\r
+ {\r
+ clearInput();\r
+ }\r
+\r
bool leftclicked;\r
bool rightclicked;\r
bool leftreleased;\r
bool keyIsDown[KEY_KEY_CODES_COUNT];\r
//s32 mouseX;\r
//s32 mouseY;\r
+ IrrlichtDevice *m_device;\r
};\r
\r
class InputHandler\r
\r
InputHandler *g_input = NULL;\r
\r
-void focusGame()\r
-{\r
- g_input->clear();\r
- g_game_focused = true;\r
-}\r
-\r
-void unFocusGame()\r
-{\r
- g_game_focused = false;\r
-}\r
-\r
class RealInputHandler : public InputHandler\r
{\r
public:\r
\r
virtual bool getLeftClicked()\r
{\r
- if(g_game_focused == false)\r
- return false;\r
return m_receiver->leftclicked;\r
}\r
virtual bool getRightClicked()\r
{\r
- if(g_game_focused == false)\r
- return false;\r
return m_receiver->rightclicked;\r
}\r
virtual void resetLeftClicked()\r
\r
virtual bool getLeftReleased()\r
{\r
- if(g_game_focused == false)\r
- return false;\r
return m_receiver->leftreleased;\r
}\r
virtual bool getRightReleased()\r
{\r
- if(g_game_focused == false)\r
- return false;\r
return m_receiver->rightreleased;\r
}\r
virtual void resetLeftReleased()\r
\r
s32 Rand(s32 min, s32 max)\r
{\r
- return (rand()%(max-min+1))+min;\r
+ return (myrand()%(max-min+1))+min;\r
}\r
private:\r
bool keydown[KEY_KEY_CODES_COUNT];\r
bool rightclicked;\r
};\r
\r
-void updateViewingRange(f32 frametime, Client *client)\r
+void updateViewingRange(f32 frametime_in, Client *client)\r
{\r
- // Range_all messes up frametime_avg\r
- if(g_viewing_range_all == true)\r
+ if(draw_control.range_all == true)\r
return;\r
-\r
- float wanted_fps = g_settings.getFloat("wanted_fps");\r
\r
- // Initialize to the target value\r
- static float frametime_avg = 1.0/wanted_fps;\r
- //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;\r
- frametime_avg = frametime_avg * 0.7 + frametime * 0.3;\r
+ static f32 added_frametime = 0;\r
+ static s16 added_frames = 0;\r
+\r
+ added_frametime += frametime_in;\r
+ added_frames += 1;\r
\r
+ // Actually this counter kind of sucks because frametime is busytime\r
static f32 counter = 0;\r
- if(counter > 0){\r
- counter -= frametime;\r
+ counter -= frametime_in;\r
+ if(counter > 0)\r
return;\r
- }\r
- //counter = 1.0; //seconds\r
- counter = 0.5; //seconds\r
+ //counter = 0.1;\r
+ counter = 0.2;\r
\r
- //float freetime_ratio = 0.2;\r
- //float freetime_ratio = 0.4;\r
- float freetime_ratio = FREETIME_RATIO;\r
-\r
- float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));\r
-\r
- float fraction = sqrt(frametime_avg / frametime_wanted);\r
-\r
- /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0\r
- + frametime_avg / frametime_wanted / 2.0;*/\r
+ /*dstream<<__FUNCTION_NAME\r
+ <<": Collected "<<added_frames<<" frames, total of "\r
+ <<added_frametime<<"s."<<std::endl;*/\r
\r
- //float fraction = frametime_avg / frametime_wanted;\r
-\r
- static bool fraction_is_good = false;\r
+ /*dstream<<"draw_control.blocks_drawn="\r
+ <<draw_control.blocks_drawn\r
+ <<", draw_control.blocks_would_have_drawn="\r
+ <<draw_control.blocks_would_have_drawn\r
+ <<std::endl;*/\r
\r
- float fraction_good_threshold = 0.1;\r
- //float fraction_bad_threshold = 0.25;\r
- float fraction_bad_threshold = 0.1;\r
- float fraction_limit;\r
- // Use high limit if fraction is good AND the fraction would\r
- // lower the range. We want to keep the range fairly high.\r
- if(fraction_is_good && fraction > 1.0)\r
- fraction_limit = fraction_bad_threshold;\r
- else\r
- fraction_limit = fraction_good_threshold;\r
-\r
- if(fabs(fraction - 1.0) < fraction_limit)\r
+ float range_min = g_settings.getS16("viewing_range_nodes_min");\r
+ float range_max = g_settings.getS16("viewing_range_nodes_max");\r
+ \r
+ draw_control.wanted_min_range = range_min;\r
+ draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;\r
+ \r
+ float block_draw_ratio = 1.0;\r
+ if(draw_control.blocks_would_have_drawn != 0)\r
{\r
- fraction_is_good = true;\r
- return;\r
+ block_draw_ratio = (float)draw_control.blocks_drawn\r
+ / (float)draw_control.blocks_would_have_drawn;\r
}\r
- else\r
+\r
+ // Calculate the average frametime in the case that all wanted\r
+ // blocks had been drawn\r
+ f32 frametime = added_frametime / added_frames / block_draw_ratio;\r
+ \r
+ added_frametime = 0.0;\r
+ added_frames = 0;\r
+ \r
+ float wanted_fps = g_settings.getFloat("wanted_fps");\r
+ float wanted_frametime = 1.0 / wanted_fps;\r
+ \r
+ f32 wanted_frametime_change = wanted_frametime - frametime;\r
+ //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;\r
+ \r
+ // If needed frametime change is very small, just return\r
+ if(fabs(wanted_frametime_change) < wanted_frametime*0.2)\r
{\r
- fraction_is_good = false;\r
+ //dstream<<"ignoring small wanted_frametime_change"<<std::endl;\r
+ return;\r
}\r
\r
- //dstream<<"frametime_avg="<<frametime_avg<<std::endl;\r
- //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;\r
- /*dstream<<"fetching="<<client->isFetchingBlocks()\r
- <<" faction = "<<fraction<<std::endl;*/\r
+ float range = draw_control.wanted_range;\r
+ float new_range = range;\r
\r
- JMutexAutoLock lock(g_range_mutex);\r
+ static s16 range_old = 0;\r
+ static f32 frametime_old = 0;\r
+ \r
+ float d_range = range - range_old;\r
+ f32 d_frametime = frametime - frametime_old;\r
+ // A sane default of 30ms per 50 nodes of range\r
+ static f32 time_per_range = 30. / 50;\r
+ if(d_range != 0)\r
+ {\r
+ time_per_range = d_frametime / d_range;\r
+ }\r
\r
- s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");\r
- s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");\r
+ // The minimum allowed calculated frametime-range derivative:\r
+ // Practically this sets the maximum speed of changing the range.\r
+ // The lower this value, the higher the maximum changing speed.\r
+ // A low value here results in wobbly range (0.001)\r
+ // A high value here results in slow changing range (0.0025)\r
+ // SUGG: This could be dynamically adjusted so that when\r
+ // the camera is turning, this is lower\r
+ //float min_time_per_range = 0.0015;\r
+ float min_time_per_range = 0.0010;\r
+ //float min_time_per_range = 0.05 / range;\r
+ if(time_per_range < min_time_per_range)\r
+ {\r
+ time_per_range = min_time_per_range;\r
+ //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;\r
+ }\r
+ else\r
+ {\r
+ //dstream<<"time_per_range="<<time_per_range<<std::endl;\r
+ }\r
\r
- s16 n = (float)g_viewing_range_nodes / fraction;\r
- if(n < viewing_range_nodes_min)\r
- n = viewing_range_nodes_min;\r
- if(n > viewing_range_nodes_max)\r
- n = viewing_range_nodes_max;\r
+ f32 wanted_range_change = wanted_frametime_change / time_per_range;\r
+ // Dampen the change a bit to kill oscillations\r
+ //wanted_range_change *= 0.9;\r
+ //wanted_range_change *= 0.75;\r
+ wanted_range_change *= 0.5;\r
+ //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;\r
\r
- bool can_change = true;\r
+ // If needed range change is very small, just return\r
+ if(fabs(wanted_range_change) < 0.001)\r
+ {\r
+ //dstream<<"ignoring small wanted_range_change"<<std::endl;\r
+ return;\r
+ }\r
\r
- if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)\r
- can_change = false;\r
+ new_range += wanted_range_change;\r
+ //dstream<<"new_range="<<new_range/*<<std::endl*/;\r
+ \r
+ //float new_range_unclamped = new_range;\r
+ if(new_range < range_min)\r
+ new_range = range_min;\r
+ if(new_range > range_max)\r
+ new_range = range_max;\r
\r
- if(can_change)\r
- g_viewing_range_nodes = n;\r
+ /*if(new_range != new_range_unclamped)\r
+ dstream<<", clamped to "<<new_range<<std::endl;\r
+ else\r
+ dstream<<std::endl;*/\r
+\r
+ draw_control.wanted_range = new_range;\r
\r
- /*dstream<<"g_viewing_range_nodes = "\r
- <<g_viewing_range_nodes<<std::endl;*/\r
+ range_old = new_range;\r
+ frametime_old = frametime;\r
}\r
\r
class GUIQuickInventory : public IEventReceiver\r
\r
start = m_selection - m_itemcount / 2;\r
\r
+ InventoryList *mainlist = m_inventory->getList("main");\r
+\r
for(s32 i=0; i<m_itemcount; i++)\r
{\r
s32 j = i + start;\r
\r
- if(j > (s32)m_inventory->getSize() - 1)\r
- j -= m_inventory->getSize();\r
+ if(j > (s32)mainlist->getSize() - 1)\r
+ j -= mainlist->getSize();\r
if(j < 0)\r
- j += m_inventory->getSize();\r
+ j += mainlist->getSize();\r
\r
- InventoryItem *item = m_inventory->getItem(j);\r
+ InventoryItem *item = mainlist->getItem(j);\r
// Null items\r
if(item == NULL)\r
{\r
s32 m_selection;\r
};\r
\r
+// Chat data\r
+struct ChatLine\r
+{\r
+ ChatLine():\r
+ age(0.0)\r
+ {\r
+ }\r
+ ChatLine(const std::wstring &a_text):\r
+ age(0.0),\r
+ text(a_text)\r
+ {\r
+ }\r
+ float age;\r
+ std::wstring text;\r
+};\r
+\r
int main(int argc, char *argv[])\r
{\r
/*\r
debug_stacks_init();\r
\r
DSTACK(__FUNCTION_NAME);\r
+ \r
+ initializeMaterialProperties();\r
\r
try\r
{\r
sockets_init();\r
atexit(sockets_cleanup);\r
\r
- // Initialize timestamp mutex\r
- g_timestamp_mutex.Init();\r
-\r
/*\r
Initialization\r
*/\r
\r
// Initialize random seed\r
srand(time(0));\r
+ mysrand(time(0));\r
\r
/*\r
Run unit tests\r
run_tests();\r
}\r
\r
- /*\r
- Global range mutex\r
- */\r
- g_range_mutex.Init();\r
- assert(g_range_mutex.IsInitialized());\r
-\r
// Read map parameters from settings\r
\r
HMParams hm_params;\r
<<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl\r
<<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl\r
<<" \\/ \\/ \\/ \\/ \\/ "<<std::endl\r
- <<std::endl\r
- <<"Now with more waterish water!"\r
<<std::endl;\r
\r
std::cout<<std::endl;\r
- char templine[100];\r
+ //char templine[100];\r
\r
// Port?\r
u16 port = 30000;\r
{\r
snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());\r
}\r
- else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)\r
- {\r
- std::cout<<g_settings.get("address")<<std::endl;\r
- snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());\r
- }\r
- else\r
+ else if(is_yes(g_settings.get("host_game")) == false)\r
{\r
- std::cout<<"Address to connect to [empty = host a game]: ";\r
- std::cin.getline(connect_name, 100);\r
+ if(g_settings.get("address") != "")\r
+ {\r
+ std::cout<<g_settings.get("address")<<std::endl;\r
+ snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());\r
+ }\r
+ else\r
+ {\r
+ std::cout<<"Address to connect to [empty = host a game]: ";\r
+ std::cin.getline(connect_name, 100);\r
+ }\r
}\r
\r
if(connect_name[0] == 0){\r
/*\r
Resolution selection\r
*/\r
-\r
- u16 screenW;\r
- u16 screenH;\r
- bool fullscreen = false;\r
\r
- if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")\r
- {\r
- screenW = atoi(g_settings.get("screenW").c_str());\r
- screenH = atoi(g_settings.get("screenH").c_str());\r
- }\r
- else\r
- {\r
- u16 resolutions[][3] = {\r
- //W, H, fullscreen\r
- {640,480, 0},\r
- {800,600, 0},\r
- {1024,768, 0},\r
- {1280,1024, 0},\r
- /*{640,480, 1},\r
- {800,600, 1},\r
- {1024,768, 1},\r
- {1280,1024, 1},*/\r
- };\r
-\r
- u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);\r
- \r
- for(u16 i=0; i<res_count; i++)\r
- {\r
- std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"\r
- <<resolutions[i][1];\r
- if(resolutions[i][2])\r
- std::cout<<" fullscreen"<<std::endl;\r
- else\r
- std::cout<<" windowed"<<std::endl;\r
- }\r
- std::cout<<"Select a window resolution number [empty = 2]: ";\r
- std::cin.getline(templine, 100);\r
-\r
- u16 r0;\r
- if(templine[0] == 0)\r
- r0 = 2;\r
- else\r
- r0 = atoi(templine);\r
-\r
- if(r0 > res_count || r0 == 0)\r
- r0 = 2;\r
- \r
- {\r
- u16 i = r0-1;\r
- std::cout<<"-> ";\r
- std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"\r
- <<resolutions[i][1];\r
- if(resolutions[i][2])\r
- std::cout<<" fullscreen"<<std::endl;\r
- else\r
- std::cout<<" windowed"<<std::endl;\r
- }\r
-\r
- screenW = resolutions[r0-1][0];\r
- screenH = resolutions[r0-1][1];\r
- fullscreen = resolutions[r0-1][2];\r
- }\r
+ bool fullscreen = false;\r
+ u16 screenW = atoi(g_settings.get("screenW").c_str());\r
+ u16 screenH = atoi(g_settings.get("screenH").c_str());\r
\r
//\r
\r
device = createDevice(driverType,\r
core::dimension2d<u32>(screenW, screenH),\r
16, fullscreen, false, false, &receiver);\r
- // With vsync\r
- /*device = createDevice(driverType,\r
- core::dimension2d<u32>(screenW, screenH),\r
- 16, fullscreen, false, true, &receiver);*/\r
\r
if (device == 0)\r
return 1; // could not create selected driver.\r
-\r
+ \r
g_device = device;\r
+ g_irrlicht = new IrrlichtWrapper(device);\r
+\r
+ //g_device = device;\r
\r
device->setResizable(true);\r
\r
*/\r
\r
video::IVideoDriver* driver = device->getVideoDriver();\r
- // These make the textures not to show at all\r
- //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);\r
- //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );\r
\r
+ /*\r
+ This changes the minimum allowed number of vertices in a VBO\r
+ */\r
//driver->setMinHardwareBufferVertexCount(1);\r
\r
scene::ISceneManager* smgr = device->getSceneManager();\r
\r
- // Pause menu\r
- guiPauseMenu pauseMenu(device, &receiver);\r
-\r
- gui::IGUIEnvironment* guienv = device->getGUIEnvironment();\r
+ guienv = device->getGUIEnvironment();\r
gui::IGUISkin* skin = guienv->getSkin();\r
gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");\r
if(font)\r
skin->setFont(font);\r
+ \r
+ u32 text_height = font->getDimension(L"Hello, world!").Height;\r
+ dstream<<"text_height="<<text_height<<std::endl;\r
+\r
//skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
//skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
\r
const wchar_t *text = L"Loading and connecting...";\r
core::vector2d<s32> center(screenW/2, screenH/2);\r
- core::dimension2d<u32> textd = font->getDimension(text);\r
- std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;\r
- // Have to add a bit to disable the text from word wrapping\r
- //core::vector2d<s32> textsize(textd.Width+4, textd.Height);\r
- core::vector2d<s32> textsize(300, textd.Height);\r
+ core::vector2d<s32> textsize(300, text_height);\r
core::rect<s32> textrect(center - textsize/2, center + textsize/2);\r
\r
gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(\r
driver->endScene();\r
\r
/*\r
- Preload some random textures that are used in threads\r
- */\r
- \r
- g_texturecache.set("torch", driver->getTexture("../data/torch.png"));\r
- g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));\r
- g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));\r
- \r
- /*\r
- Load tile textures\r
+ Preload some textures\r
*/\r
- for(s32 i=0; i<TILES_COUNT; i++)\r
- {\r
- if(g_tile_texture_names[i] == NULL)\r
- continue;\r
- std::string name = g_tile_texture_names[i];\r
- std::string filename;\r
- filename += "../data/";\r
- filename += name;\r
- filename += ".png";\r
- g_texturecache.set(name, driver->getTexture(filename.c_str()));\r
- }\r
\r
- tile_materials_preload(g_texturecache);\r
+ tile_materials_preload(g_irrlicht);\r
\r
/*\r
Make a scope here for the client so that it gets removed\r
Create client\r
*/\r
\r
- Client client(device, playername);\r
+ Client client(device, playername, draw_control);\r
+ \r
+ g_client = &client;\r
\r
Address connect_address(0,0,0,0, port);\r
try{\r
\r
gui_loadingtext->remove();\r
\r
- pauseMenu.setVisible(true);\r
-\r
/*\r
Add some gui stuff\r
*/\r
+\r
+ GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
+ (guienv, NULL, v2s32(10, 70), 5, &local_inventory);\r
+ \r
+ /*\r
+ We need some kind of a root node to be able to add\r
+ custom elements directly on the screen.\r
+ Otherwise they won't be automatically drawn.\r
+ */\r
+ guiroot = guienv->addStaticText(L"",\r
+ core::rect<s32>(0, 0, 10000, 10000));\r
\r
+ // Test the text input system\r
+ /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,\r
+ NULL))->drop();*/\r
+ /*GUIMessageMenu *menu =\r
+ new GUIMessageMenu(guienv, guiroot, -1, \r
+ &g_active_menu_count,\r
+ L"Asd");\r
+ menu->drop();*/\r
+ \r
+ // Launch pause menu\r
+ (new GUIPauseMenu(guienv, guiroot, -1, g_device,\r
+ &g_active_menu_count))->drop();\r
+\r
// First line of debug text\r
gui::IGUIStaticText *guitext = guienv->addStaticText(\r
L"Minetest-c55",\r
core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),\r
false, false);\r
\r
- // This is a copy of the inventory that the client's environment has\r
- Inventory local_inventory(PLAYER_INVENTORY_SIZE);\r
- \r
- GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
- (guienv, NULL, v2s32(10, 70), 5, &local_inventory);\r
+ // Chat text\r
+ gui::IGUIStaticText *chat_guitext = guienv->addStaticText(\r
+ L"Chat here\nOther line\nOther line\nOther line\nOther line",\r
+ core::rect<s32>(70, 60, 795, 150),\r
+ false, true);\r
+ chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));\r
+ core::list<ChatLine> chat_lines;\r
\r
/*\r
Some statistics are collected in these\r
u32 beginscenetime = 0;\r
u32 scenetime = 0;\r
u32 endscenetime = 0;\r
-\r
- /*\r
- Text input system\r
- */\r
\r
- struct TextDest\r
- {\r
- virtual void sendText(std::string text) = 0;\r
- };\r
- \r
- struct TextDestSign : public TextDest\r
- {\r
- TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
- {\r
- m_blockpos = blockpos;\r
- m_id = id;\r
- m_client = client;\r
- }\r
- void sendText(std::string text)\r
- {\r
- dstream<<"Changing text of a sign object: "\r
- <<text<<std::endl;\r
- m_client->sendSignText(m_blockpos, m_id, text);\r
- }\r
-\r
- v3s16 m_blockpos;\r
- s16 m_id;\r
- Client *m_client;\r
- };\r
-\r
- TextDest *textbuf_dest = NULL;\r
- \r
- //gui::IGUIWindow* input_window = NULL;\r
- gui::IGUIStaticText* input_guitext = NULL;\r
-\r
- /*\r
- Digging animation\r
- */\r
- //f32 \r
+ // A test\r
+ //throw con::PeerNotFoundException("lol");\r
\r
/*\r
Main loop\r
\r
while(device->run())\r
{\r
+ /*\r
+ Run global IrrlichtWrapper's main thread processing stuff\r
+ */\r
+ g_irrlicht->Run();\r
+\r
/*\r
Random calculations\r
*/\r
v2u32 screensize = driver->getScreenSize();\r
core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);\r
-\r
+ \r
// Hilight boxes collected during the loop and displayed\r
core::list< core::aabbox3d<f32> > hilightboxes;\r
\r
// Info text\r
std::wstring infotext;\r
\r
- //TimeTaker //timer1("//timer1", device);\r
+ //TimeTaker //timer1("//timer1");\r
\r
// Time of frame without fps limit\r
float busytime;\r
Viewing range\r
*/\r
\r
- //updateViewingRange(dtime, &client);\r
updateViewingRange(busytime, &client);\r
\r
/*\r
*/\r
g_input->step(dtime);\r
\r
- /*\r
- Special keys\r
- */\r
- /*if(g_esc_pressed)\r
- {\r
- break;\r
- }*/\r
-\r
/*\r
Player speed control\r
*/\r
\r
- if(g_game_focused)\r
{\r
/*bool a_up,\r
bool a_down,\r
);\r
client.setPlayerControl(control);\r
}\r
- else\r
- {\r
- // Set every key to inactive\r
- PlayerControl control;\r
- client.setPlayerControl(control);\r
- }\r
\r
- //timer1.stop();\r
/*\r
Process environment\r
*/\r
\r
{\r
- //TimeTaker timer("client.step(dtime)", device);\r
+ //TimeTaker timer("client.step(dtime)");\r
client.step(dtime);\r
//client.step(dtime_avg1);\r
}\r
\r
if(server != NULL)\r
{\r
- //TimeTaker timer("server->step(dtime)", device);\r
+ //TimeTaker timer("server->step(dtime)");\r
server->step(dtime);\r
}\r
\r
v3f player_position = client.getPlayerPosition();\r
\r
- //TimeTaker //timer2("//timer2", device);\r
+ //TimeTaker //timer2("//timer2");\r
\r
/*\r
Mouse and camera control\r
*/\r
\r
- if((device->isWindowActive() && g_game_focused && !pauseMenu.isVisible())\r
- || random_input)\r
+ if((device->isWindowActive() && noMenuActive()) || random_input)\r
{\r
if(!random_input)\r
device->getCursorControl()->setVisible(false);\r
}\r
else{\r
//client.m_env.getMap().updateCamera(camera_position, camera_direction);\r
- //TimeTaker timer("client.updateCamera", device);\r
+ //TimeTaker timer("client.updateCamera");\r
client.updateCamera(camera_position, camera_direction);\r
}\r
\r
//timer2.stop();\r
- //TimeTaker //timer3("//timer3", device);\r
+ //TimeTaker //timer3("//timer3");\r
\r
/*\r
Calculate what block is the crosshair pointing to\r
MapBlockObject *selected_object = client.getSelectedObject\r
(d*BS, camera_position, shootline);\r
\r
+ /*\r
+ If it's pointing to a MapBlockObject\r
+ */\r
+\r
if(selected_object != NULL)\r
{\r
//dstream<<"Client returned selected_object != NULL"<<std::endl;\r
{\r
dstream<<"Sign object right-clicked"<<std::endl;\r
\r
- unFocusGame();\r
+ if(random_input == false)\r
+ {\r
+ // Get a new text for it\r
\r
- input_guitext = guienv->addStaticText(L"",\r
- core::rect<s32>(150,100,350,120),\r
- true, // border?\r
- false, // wordwrap?\r
- NULL);\r
+ TextDest *dest = new TextDestSign(\r
+ selected_object->getBlock()->getPos(),\r
+ selected_object->getId(),\r
+ &client);\r
\r
- input_guitext->setDrawBackground(true);\r
+ SignObject *sign_object = (SignObject*)selected_object;\r
\r
- if(random_input)\r
- {\r
- g_text_buffer = L"ASD LOL 8)";\r
- g_text_buffer_accepted = true;\r
- }\r
- else\r
- {\r
- g_text_buffer = L"";\r
- g_text_buffer_accepted = false;\r
- }\r
+ std::wstring wtext =\r
+ narrow_to_wide(sign_object->getText());\r
\r
- textbuf_dest = new TextDestSign(\r
- selected_object->getBlock()->getPos(),\r
- selected_object->getId(),\r
- &client);\r
+ (new GUITextInputMenu(guienv, guiroot, -1,\r
+ &g_active_menu_count, dest,\r
+ wtext))->drop();\r
+ }\r
}\r
/*\r
Otherwise pass the event to the server as-is\r
} // regular block\r
} // for coords\r
\r
- /*static v3s16 oldnodepos;\r
- static bool oldnodefound = false;*/\r
+ static float nodig_delay_counter = 0.0;\r
\r
if(nodefound)\r
{\r
- //std::cout<<DTIME<<"nodefound == true"<<std::endl;\r
- //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
- //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;\r
-\r
- static v3s16 nodepos_old(-1,-1,-1);\r
- if(nodepos != nodepos_old){\r
- std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","\r
- <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
- }\r
+ static v3s16 nodepos_old(-32768,-32768,-32768);\r
+\r
+ static float dig_time = 0.0;\r
+ static u16 dig_index = 0;\r
\r
hilightboxes.push_back(nodefacebox);\r
\r
- //if(g_input->getLeftClicked())\r
- if(g_input->getLeftClicked() ||\r
- (g_input->getLeftState() && nodepos != nodepos_old))\r
+ if(g_input->getLeftReleased())\r
+ {\r
+ client.clearTempMod(nodepos);\r
+ dig_time = 0.0;\r
+ }\r
+ \r
+ if(nodig_delay_counter > 0.0)\r
{\r
- std::cout<<DTIME<<"Ground left-clicked"<<std::endl;\r
- client.pressGround(0, nodepos, neighbourpos, g_selected_item);\r
+ nodig_delay_counter -= dtime;\r
}\r
+ else\r
+ {\r
+ if(nodepos != nodepos_old)\r
+ {\r
+ std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","\r
+ <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
+\r
+ if(nodepos_old != v3s16(-32768,-32768,-32768))\r
+ {\r
+ client.clearTempMod(nodepos_old);\r
+ dig_time = 0.0;\r
+ }\r
+ }\r
+\r
+ if(g_input->getLeftClicked() ||\r
+ (g_input->getLeftState() && nodepos != nodepos_old))\r
+ {\r
+ dstream<<DTIME<<"Started digging"<<std::endl;\r
+ client.groundAction(0, nodepos, neighbourpos, g_selected_item);\r
+ }\r
+ if(g_input->getLeftClicked())\r
+ {\r
+ client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));\r
+ }\r
+ if(g_input->getLeftState())\r
+ {\r
+ MapNode n = client.getNode(nodepos);\r
+ \r
+ // Get tool name. Default is "" = bare hands\r
+ std::string toolname = "";\r
+ InventoryList *mlist = local_inventory.getList("main");\r
+ if(mlist != NULL)\r
+ {\r
+ InventoryItem *item = mlist->getItem(g_selected_item);\r
+ if(item && (std::string)item->getName() == "ToolItem")\r
+ {\r
+ ToolItem *titem = (ToolItem*)item;\r
+ toolname = titem->getToolName();\r
+ }\r
+ }\r
+\r
+ // Get digging properties for material and tool\r
+ u8 material = n.d;\r
+ DiggingProperties prop =\r
+ getDiggingProperties(material, toolname);\r
+ \r
+ float dig_time_complete = 0.0;\r
+\r
+ if(prop.diggable == false)\r
+ {\r
+ /*dstream<<"Material "<<(int)material\r
+ <<" not diggable with \""\r
+ <<toolname<<"\""<<std::endl;*/\r
+ // I guess nobody will wait for this long\r
+ dig_time_complete = 10000000.0;\r
+ }\r
+ else\r
+ {\r
+ dig_time_complete = prop.time;\r
+ }\r
+ \r
+ if(dig_time_complete >= 0.001)\r
+ {\r
+ dig_index = (u16)((float)CRACK_ANIMATION_LENGTH\r
+ * dig_time/dig_time_complete);\r
+ }\r
+ // This is for torches\r
+ else\r
+ {\r
+ dig_index = CRACK_ANIMATION_LENGTH;\r
+ }\r
+\r
+ if(dig_index < CRACK_ANIMATION_LENGTH)\r
+ {\r
+ //dstream<<"dig_index="<<dig_index<<std::endl;\r
+ client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));\r
+ }\r
+ else\r
+ {\r
+ dstream<<DTIME<<"Digging completed"<<std::endl;\r
+ client.groundAction(3, nodepos, neighbourpos, g_selected_item);\r
+ client.clearTempMod(nodepos);\r
+ client.removeNode(nodepos);\r
+\r
+ dig_time = 0;\r
+\r
+ nodig_delay_counter = dig_time_complete\r
+ / (float)CRACK_ANIMATION_LENGTH;\r
+\r
+ // We don't want a corresponding delay to\r
+ // very time consuming nodes\r
+ if(nodig_delay_counter > 0.5)\r
+ {\r
+ nodig_delay_counter = 0.5;\r
+ }\r
+ // We want a slight delay to very little\r
+ // time consuming nodes\r
+ //float mindelay = 0.15;\r
+ float mindelay = 0.20;\r
+ if(nodig_delay_counter < mindelay)\r
+ {\r
+ nodig_delay_counter = mindelay;\r
+ }\r
+ }\r
+\r
+ dig_time += dtime;\r
+ }\r
+ }\r
+ \r
if(g_input->getRightClicked())\r
- /*if(g_input->getRightClicked() ||\r
- (g_input->getRightState() && nodepos != nodepos_old))*/\r
{\r
std::cout<<DTIME<<"Ground right-clicked"<<std::endl;\r
- client.pressGround(1, nodepos, neighbourpos, g_selected_item);\r
+ client.groundAction(1, nodepos, neighbourpos, g_selected_item);\r
}\r
\r
nodepos_old = nodepos;\r
}\r
else{\r
- //std::cout<<DTIME<<"nodefound == false"<<std::endl;\r
- //positiontextgui->setText(L"");\r
}\r
\r
- /*oldnodefound = nodefound;\r
- oldnodepos = nodepos;*/\r
-\r
} // selected_object == NULL\r
\r
g_input->resetLeftClicked();\r
\r
if(g_input->getLeftReleased())\r
{\r
- std::cout<<DTIME<<"Left released"<<std::endl;\r
- client.stopDigging();\r
+ std::cout<<DTIME<<"Left button released (stopped digging)"\r
+ <<std::endl;\r
+ client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);\r
}\r
if(g_input->getRightReleased())\r
{\r
\r
camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);\r
\r
- // Background color is choosen based on whether the player is\r
- // much beyond the initial ground level\r
- /*video::SColor bgcolor;\r
- v3s16 p0 = Map::floatToInt(player_position);\r
- // Does this make short random delays?\r
- // NOTE: no need for this, sky doesn't show underground with\r
- // enough range\r
- bool is_underground = client.isNodeUnderground(p0);\r
- //bool is_underground = false;\r
- if(is_underground == false)\r
- bgcolor = video::SColor(255,90,140,200);\r
- else\r
- bgcolor = video::SColor(255,0,0,0);*/\r
- \r
- //video::SColor bgcolor = video::SColor(255,90,140,200);\r
- //video::SColor bgcolor = skycolor;\r
- \r
- //s32 daynight_i = client.getDayNightIndex();\r
- //video::SColor bgcolor = skycolor[daynight_i];\r
-\r
u32 daynight_ratio = client.getDayNightRatio();\r
video::SColor bgcolor = video::SColor(\r
255,\r
\r
if(g_settings.getBool("enable_fog") == true)\r
{\r
- f32 range = g_viewing_range_nodes * BS;\r
- if(g_viewing_range_all)\r
+ f32 range = draw_control.wanted_range * BS;\r
+ if(draw_control.range_all)\r
range = 100000*BS;\r
\r
driver->setFog(\r
Update gui stuff (0ms)\r
*/\r
\r
- //TimeTaker guiupdatetimer("Gui updating", device);\r
+ //TimeTaker guiupdatetimer("Gui updating");\r
\r
{\r
wchar_t temptext[150];\r
\r
static float drawtime_avg = 0;\r
- drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;\r
+ drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;\r
static float beginscenetime_avg = 0;\r
- beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;\r
+ beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;\r
static float scenetime_avg = 0;\r
- scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;\r
+ scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;\r
static float endscenetime_avg = 0;\r
- endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;\r
+ endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;\r
\r
swprintf(temptext, 150, L"Minetest-c55 ("\r
L"F: item=%i"\r
L")"\r
L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",\r
g_selected_item,\r
- g_viewing_range_all,\r
+ draw_control.range_all,\r
drawtime_avg,\r
beginscenetime_avg,\r
scenetime_avg,\r
swprintf(temptext, 150,\r
L"(% .1f, % .1f, % .1f)"\r
L" (% .3f < btime_jitter < % .3f"\r
- L", dtime_jitter = % .1f %%)",\r
+ L", dtime_jitter = % .1f %%"\r
+ L", v_range = %.1f)",\r
player_position.X/BS,\r
player_position.Y/BS,\r
player_position.Z/BS,\r
busytime_jitter1_min_sample,\r
busytime_jitter1_max_sample,\r
- dtime_jitter1_max_fraction * 100.0\r
+ dtime_jitter1_max_fraction * 100.0,\r
+ draw_control.wanted_range\r
);\r
\r
guitext2->setText(temptext);\r
}\r
\r
{\r
- /*wchar_t temptext[100];\r
- swprintf(temptext, 100,\r
- SWPRINTF_CHARSTRING,\r
- infotext.substr(0,99).c_str()\r
- );\r
+ guitext_info->setText(infotext.c_str());\r
+ }\r
+ \r
+ /*\r
+ Get chat messages from client\r
+ */\r
+ {\r
+ // Get new messages\r
+ std::wstring message;\r
+ while(client.getChatMessage(message))\r
+ {\r
+ chat_lines.push_back(ChatLine(message));\r
+ /*if(chat_lines.size() > 6)\r
+ {\r
+ core::list<ChatLine>::Iterator\r
+ i = chat_lines.begin();\r
+ chat_lines.erase(i);\r
+ }*/\r
+ }\r
+ // Append them to form the whole static text and throw\r
+ // it to the gui element\r
+ std::wstring whole;\r
+ // This will correspond to the line number counted from\r
+ // top to bottom, from size-1 to 0\r
+ s16 line_number = chat_lines.size();\r
+ // Count of messages to be removed from the top\r
+ u16 to_be_removed_count = 0;\r
+ for(core::list<ChatLine>::Iterator\r
+ i = chat_lines.begin();\r
+ i != chat_lines.end(); i++)\r
+ {\r
+ // After this, line number is valid for this loop\r
+ line_number--;\r
+ // Increment age\r
+ (*i).age += dtime;\r
+ /*\r
+ This results in a maximum age of 60*6 to the\r
+ lowermost line and a maximum of 6 lines\r
+ */\r
+ float allowed_age = (6-line_number) * 60.0;\r
\r
- guitext_info->setText(temptext);*/\r
+ if((*i).age > allowed_age)\r
+ {\r
+ to_be_removed_count++;\r
+ continue;\r
+ }\r
+ whole += (*i).text + L'\n';\r
+ }\r
+ for(u16 i=0; i<to_be_removed_count; i++)\r
+ {\r
+ core::list<ChatLine>::Iterator\r
+ it = chat_lines.begin();\r
+ chat_lines.erase(it);\r
+ }\r
+ chat_guitext->setText(whole.c_str());\r
+ // Update gui element size and position\r
+ core::rect<s32> rect(\r
+ 10,\r
+ screensize.Y - 10 - text_height*chat_lines.size(),\r
+ screensize.X - 10,\r
+ screensize.Y - 10\r
+ );\r
+ chat_guitext->setRelativePosition(rect);\r
\r
- guitext_info->setText(infotext.c_str());\r
+ if(chat_lines.size() == 0)\r
+ chat_guitext->setVisible(false);\r
+ else\r
+ chat_guitext->setVisible(true);\r
}\r
\r
/*\r
quick_inventory->setSelection(g_selected_item);\r
quick_inventory->update();\r
}\r
-\r
- if(input_guitext != NULL)\r
- {\r
- /*wchar_t temptext[100];\r
- swprintf(temptext, 100,\r
- SWPRINTF_CHARSTRING,\r
- g_text_buffer.substr(0,99).c_str()\r
- );*/\r
- input_guitext->setText(g_text_buffer.c_str());\r
- }\r
-\r
+ \r
/*\r
- Text input stuff\r
+ Send actions returned by the inventory menu\r
*/\r
- if(input_guitext != NULL && g_text_buffer_accepted)\r
+ while(inventory_action_queue.size() != 0)\r
{\r
- input_guitext->remove();\r
- input_guitext = NULL;\r
- \r
- if(textbuf_dest != NULL)\r
- {\r
- std::string text = wide_to_narrow(g_text_buffer);\r
- dstream<<"Sending text: "<<text<<std::endl;\r
- textbuf_dest->sendText(text);\r
- delete textbuf_dest;\r
- textbuf_dest = NULL;\r
- }\r
+ InventoryAction *a = inventory_action_queue.pop_front();\r
\r
- focusGame();\r
+ client.sendInventoryAction(a);\r
+ // Eat it\r
+ delete a;\r
}\r
\r
- //guiupdatetimer.stop();\r
-\r
/*\r
Drawing begins\r
*/\r
\r
- TimeTaker drawtimer("Drawing", device);\r
+ TimeTaker drawtimer("Drawing");\r
\r
\r
{\r
- TimeTaker timer("beginScene", device);\r
- driver->beginScene(true, true, bgcolor);\r
- //driver->beginScene(false, true, bgcolor);\r
- beginscenetime = timer.stop(true);\r
+ TimeTaker timer("beginScene");\r
+ driver->beginScene(true, true, bgcolor);\r
+ //driver->beginScene(false, true, bgcolor);\r
+ beginscenetime = timer.stop(true);\r
}\r
\r
//timer3.stop();\r
//std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;\r
\r
{\r
- TimeTaker timer("smgr", device);\r
- smgr->drawAll();\r
- scenetime = timer.stop(true);\r
+ TimeTaker timer("smgr");\r
+ smgr->drawAll();\r
+ scenetime = timer.stop(true);\r
}\r
\r
{\r
- //TimeTaker timer9("auxiliary drawings", device);\r
+ //TimeTaker timer9("auxiliary drawings");\r
// 0ms\r
-\r
- driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),\r
- displaycenter + core::vector2d<s32>(10,0),\r
- video::SColor(255,255,255,255));\r
- driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),\r
- displaycenter + core::vector2d<s32>(0,10),\r
- video::SColor(255,255,255,255));\r
-\r
+ \r
//timer9.stop();\r
- //TimeTaker //timer10("//timer10", device);\r
+ //TimeTaker //timer10("//timer10");\r
\r
video::SMaterial m;\r
m.Thickness = 10;\r
driver->draw3DBox(*i, video::SColor(255,0,0,0));\r
}\r
\r
+ /*\r
+ Draw crosshair\r
+ */\r
+ driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),\r
+ displaycenter + core::vector2d<s32>(10,0),\r
+ video::SColor(255,255,255,255));\r
+ driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),\r
+ displaycenter + core::vector2d<s32>(0,10),\r
+ video::SColor(255,255,255,255));\r
+\r
}\r
\r
//timer10.stop();\r
- //TimeTaker //timer11("//timer11", device);\r
+ //TimeTaker //timer11("//timer11");\r
\r
/*\r
Draw gui\r
\r
// End drawing\r
{\r
- TimeTaker timer("endScene", device);\r
- driver->endScene();\r
- endscenetime = timer.stop(true);\r
+ TimeTaker timer("endScene");\r
+ driver->endScene();\r
+ endscenetime = timer.stop(true);\r
}\r
\r
drawtime = drawtimer.stop(true);\r
/*\r
Update configuration file\r
*/\r
- if(configpath != "")\r
+ /*if(configpath != "")\r
{\r
g_settings.updateConfigFile(configpath.c_str());\r
- }\r
+ }*/\r
\r
} //try\r
catch(con::PeerNotFoundException &e)\r
{\r
dstream<<DTIME<<"Connection timed out."<<std::endl;\r
+ \r
+ /*if(g_device)\r
+ {\r
+ GUIMessageMenu *menu =\r
+ new GUIMessageMenu(guienv, guiroot, -1, \r
+ &g_active_menu_count,\r
+ L"Connection timed out");\r
+\r
+ video::IVideoDriver* driver = g_device->getVideoDriver();\r
+ \r
+ dstream<<"Created menu"<<std::endl;\r
+\r
+ while(g_device->run() && menu->getStatus() == false)\r
+ {\r
+ driver->beginScene(true, true, video::SColor(255,0,0,0));\r
+ guienv->drawAll();\r
+ driver->endScene();\r
+ }\r
+ \r
+ dstream<<"Dropping menu"<<std::endl;\r
+\r
+ menu->drop();\r
+ }*/\r
}\r
#if CATCH_UNHANDLED_EXCEPTIONS\r
/*\r