From 4e249fb3fbf75f0359758760d88e22aa5b14533c Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 27 Nov 2010 01:02:21 +0200 Subject: Initial files --- src/main.cpp | 2339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2339 insertions(+) create mode 100644 src/main.cpp (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..971e0eac3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,2339 @@ +/* +(c) 2010 Perttu Ahola + +Minetest + +NOTE: VBO cannot be turned on for fast-changing stuff because there + is an apparanet memory leak in irrlicht when using it + +SUGGESTION: add a second lighting value to the MS nibble of param of + air to tell how bright the air node is when there is no sunlight. + When day changes to night, these two values can be interpolated. +TODO: Fix address to be ipv6 compatible + +TODO: ESC Pause mode in which the cursor is not kept at the center of window. +TODO: Stop player if focus of window is taken away (go to pause mode) +TODO: Optimize and fix makeFastFace or whatever it's called + - Face calculation is the source of CPU usage on the client +SUGGESTION: The client will calculate and send lighting changes and + the server will randomly check some of them and kick the client out + if it fails to calculate them right. + - Actually, it could just start ignoring them and calculate them + itself. +SUGGESTION: Combine MapBlock's face caches to so big pieces that VBO + gets used + - That is >500 vertices + +TODO: Better dungeons +TODO: There should be very slight natural caves also, starting from + only a straightened-up cliff + +TODO: Changing of block with mouse wheel or something +TODO: Menus + +TODO: Mobs + - Server: + - One single map container with ids as keys + - Client: + - ? +TODO: - Keep track of the place of the mob in the last few hundreth's + of a second - then, if a player hits it, take the value that is + avg_rtt/2 before the moment the packet is received. +TODO: - Scripting + +SUGGESTION: Modify client to calculate single changes asynchronously + +TODO: Moving players more smoothly. Calculate moving animation from + data sent by server. + +TODO: There are some lighting-related todos and fixmes in + ServerMap::emergeBlock + +TODO: Make a dirt node and use it under water + +FIXME: When a new sector is generated, it may change the ground level + of it's and it's neighbors border that two blocks that are + above and below each other and that are generated before and + after the sector heightmap generation (order doesn't matter), + can have a small gap between each other at the border. +SUGGESTION: Use same technique for sector heightmaps as what we're + using for UnlimitedHeightmap? (getting all neighbors + when generating) + +TODO: Set server to automatically find a good spawning place in some + place where there is water and land. + - Map to have a getWalkableNear(p) + +TODO: Transfer more blocks in a single packet +SUGG: A blockdata combiner class, to which blocks are added and at + destruction it sends all the stuff in as few packets as possible. + +TODO: If player is on ground, mainly fetch ground-level blocks +TODO: Fetch stuff mainly from the viewing direction + +TODO: Expose Connection's seqnums and ACKs to server and client. + - This enables saving many packets and making a faster connection + - This also enables server to check if client has received the + most recent block sent, for example. + +SUGG: Add a time value to the param of footstepped grass and check it + against a global timer when a block is accessed, to make old + steps fade away. + +FIXME: There still are *some* tiny glitches in lighting as seen from + the client side. The server calculates them right but sometimes + they don't get transferred properly. + - Server probably checks that a block is not sent, then continues + to sending it, then the emerge thread marks it as unsent and then + the sender sends the block as it was before emerging? +TODO: How about adding a "revision" field to MapBlocks? + +TODO: More fine-grained control of client's dumping of blocks from + memory + +TODO: Somehow prioritize the sending of blocks and combine the block + send queue lengths + - Take two blocks to be sent next from each client and assign + a priority value to them + - Priority is the same as distance from player + - Take the highest priority ones and send them. Send as many as + fits in the global send queue maximum length (sum of lengths + of client queues) +TODO: Make the amount of blocks sending to client and the total + amount of blocks dynamically limited. Transferring blocks is the + main network eater of this system, so it is the one that has + to be throttled so that RTTs stay low. +FIXME: There is a bug that sometimes the EmergeThread bumps to + the client's emerge counter being already 0, and also the + sending queue size of the client can float to 1 or 2, which + stops the map from loading at all. + - A quick hack could be applied to ignore the error of + being at 0 and timing out old entries +SUGG: Make client send GOTBLOCKS before updating meshes + +TODO: Server to load starting inventory from disk + +NOTE: iostream.imbue(std::locale("C")) is very slow +NOTE: Global locale is now set at initialization + +TODO: PLayers to only be hidden when the client quits. +TODO: - Players to be saved on disk, with inventory +TODO: Players to be saved as text in map/players/ + +SUGGESTION: A map editing mode (similar to dedicated server mode) + +TODO: Maybe: Create a face calculation queue on the client that is + processed in a separate thread +TODO: Make client's mesh updates to happen in a thread similar to + server's EmergeThread. + - This is not really needed, mesh update is really fast + - Instead, the lighting update can be slow + - So, this todo is not really a todo. It is a not-todo. +SUGG: Make server to send all modified blocks after a node change + after all the stuff including lighting have been updated + +TODO: Make fetching sector's blocks more efficient when rendering + sectors that have very large amounts of blocks (on client) + +TODO: Make the video backend selectable + +TODO: A timestamp to blocks + +TODO: Client side: + - The server sends all active objects of the active blocks + at constant intervals. They should fit in a few packets. + - The client keeps track of what blocks at the moment are + having active objects in them. + - All blocks going in and out of the active buffer are recorded. + - For outgoing blocks, objects are removed from the blocks + and from the scene + - For incoming blocks, objects are added to the blocks and + to the scene. + +TODO: Server side: + - A "near blocks" buffer, in which some nearby blocks are stored. + - For all blocks in the buffer, objects are stepped(). This + means they are active. + - All blocks going in and out of the buffer are recorded. + - For outgoing blocks, a timestamp is written. + - For incoming blocks, the time difference is calculated and + objects are stepped according to it. + +TODO: Add config parameters for server's sending and generating distance + +TODO: Make amount of trees and other plants configurable + - Save to a metafile + +TODO: Copy the text of the last picked sign to inventory in creative + mode + +TODO: Untie client network operations from framerate + +TODO: Make a copy of close-range environment on client for showing + on screen, with minimal mutexes to slow the main loop down + +TODO: Make a PACKET_COMBINED which contains many subpackets. Utilize + it by sending more stuff in a single packet. + +Doing now: +====================================================================== + + +====================================================================== + +*/ + +/* + Setting this to 1 enables a special camera mode that forces + the renderers to think that the camera statically points from + the starting place to a static direction. + + This allows one to move around with the player and see what + is actually drawn behind solid things etc. +*/ +#define FIELD_OF_VIEW_TEST 0 + +#ifdef UNITTEST_DISABLE + #ifdef _WIN32 + #pragma message ("Disabling unit tests") + #else + #warning "Disabling unit tests" + #endif + // Disable unit tests + #define ENABLE_TESTS 0 +#else + // Enable unit tests + #define ENABLE_TESTS 1 +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "Irrlicht.lib") +#pragma comment(lib, "jthread.lib") +// This would get rid of the console window +//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") +#endif + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #define sleep_ms(x) Sleep(x) +#else + #include + #define sleep_ms(x) usleep(x*1000) +#endif + +#include +#include +#include +#include +#include "common_irrlicht.h" +#include "debug.h" +#include "map.h" +#include "player.h" +#include "main.h" +#include "test.h" +#include "environment.h" +#include "server.h" +#include "client.h" +#include "serialization.h" +#include "constants.h" +#include "strfnd.h" +#include "porting.h" +#include + +IrrlichtDevice *g_device = NULL; + +const char *g_material_filenames[MATERIALS_COUNT] = +{ + "../data/stone.png", + "../data/grass.png", + "../data/water.png", + "../data/light.png", + "../data/tree.png", + "../data/leaves.png", + "../data/grass_footsteps.png", + "../data/mese.png" +}; + +video::SMaterial g_materials[MATERIALS_COUNT]; +//video::SMaterial g_mesh_materials[3]; + +// All range-related stuff below is locked behind this +JMutex g_range_mutex; + +// Blocks are generated in this range from the player +// This is limited vertically to half by Client::fetchBlocks() +s16 g_forcedfetch_range_nodes = FORCEDFETCH_RANGE; + +// Blocks are viewed in this range from the player +s16 g_viewing_range_nodes = 60; + +// This is updated by the client's fetchBlocks routine +//s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT; + +// If true, the preceding value has no meaning and all blocks +// already existing in memory are drawn +bool g_viewing_range_all = false; + +// This is the freetime ratio imposed by the dynamic viewing +// range changing code. +// It is controlled by the main loop to the smallest value that +// inhibits glitches (dtime jitter) in the main loop. +//float g_freetime_ratio = FREETIME_RATIO_MAX; + + +/* + Settings. + These are loaded from the config file. +*/ + +std::string g_dedicated_server; + +// Client stuff +float g_wanted_fps = FPS_DEFAULT_WANTED; +float g_fps_max = FPS_DEFAULT_MAX; +s16 g_viewing_range_nodes_max = 300; +s16 g_viewing_range_nodes_min = 20; +std::string g_screenW; +std::string g_screenH; +std::string g_host_game; +std::string g_port; +std::string g_address; +std::string g_name; +bool g_random_input = false; +float g_client_delete_unused_sectors_timeout = 1200; + +// Server stuff +bool g_creative_mode = false; +MapgenParams g_mapgen_params; + +/* + Random stuff +*/ + +//u16 g_selected_material = 0; +u16 g_selected_item = 0; + +bool g_esc_pressed = false; + +std::wstring g_text_buffer; +bool g_text_buffer_accepted = false; + +// When true, the mouse and keyboard are grabbed +bool g_game_focused = true; + +/* + Debug streams +*/ + +// Connection +std::ostream *dout_con_ptr = &dummyout; +std::ostream *derr_con_ptr = &dstream_no_stderr; +//std::ostream *dout_con_ptr = &dstream_no_stderr; +//std::ostream *derr_con_ptr = &dstream_no_stderr; +//std::ostream *dout_con_ptr = &dstream; +//std::ostream *derr_con_ptr = &dstream; + +// Server +std::ostream *dout_server_ptr = &dstream; +std::ostream *derr_server_ptr = &dstream; + +// Client +std::ostream *dout_client_ptr = &dstream; +std::ostream *derr_client_ptr = &dstream; + +/* + Config stuff +*/ + +// Returns false on EOF +bool parseConfigObject(std::istream &is) +{ + // float g_wanted_fps + // s16 g_viewing_range_nodes_max + + if(is.eof()) + return false; + + std::string line; + std::getline(is, line); + //dstream<<"got line: \""< 32767) + v = 32767; + g_viewing_range_nodes_max = v; + } + else if(name == "viewing_range_nodes_min") + { + s32 v = atoi(value.c_str()); + if(v < 0) + v = 0; + if(v > 32767) + v = 32767; + g_viewing_range_nodes_min = v; + } + else if(name=="screenW") + g_screenW = value; + else if(name=="screenH") + g_screenH = value; + else if(name == "host_game") + g_host_game = value; + else if(name == "port") + g_port = value; + else if(name == "address") + g_address = value; + else if(name == "name") + g_name = value; + else if(name == "random_input") + g_random_input = is_yes(value); + else if(name == "client_delete_unused_sectors_timeout") + { + std::istringstream vis(value); + //vis.imbue(std::locale("C")); + vis>>g_client_delete_unused_sectors_timeout; + } + + // Server stuff + else if(name == "creative_mode") + g_creative_mode = is_yes(value); + else if(name == "mapgen_heightmap_blocksize") + { + s32 d = atoi(value.c_str()); + if(d > 0 && (d & (d-1)) == 0) + g_mapgen_params.heightmap_blocksize = d; + else + dstream<<"Invalid value in config file: \"" + < 0) + g_text_buffer = g_text_buffer.substr + (0, g_text_buffer.size()-1); + } + else + { + wchar_t wc = event.KeyInput.Char; + if(wc != 0) + g_text_buffer += wc; + } + } + + if(event.KeyInput.Key == irr::KEY_ESCAPE) + { + if(g_game_focused == true) + { + dstream<setBackgroundColor( + video::SColor(128,0,0,0)); + m_texts[i]->setTextAlignment( + gui::EGUIA_CENTER, + gui::EGUIA_UPPERLEFT); + } + } + + virtual bool OnEvent(const SEvent& event) + { + return false; + } + + void setSelection(s32 i) + { + m_selection = i; + } + + void update() + { + s32 start = 0; + + start = m_selection - m_itemcount / 2; + + for(s32 i=0; i (s32)m_inventory->getSize() - 1) + j -= m_inventory->getSize(); + if(j < 0) + j += m_inventory->getSize(); + + InventoryItem *item = m_inventory->getItem(j); + // Null items + if(item == NULL) + { + m_images[i]->setImage(NULL); + + wchar_t t[10]; + if(m_selection == j) + swprintf(t, 10, L"<-"); + else + swprintf(t, 10, L""); + m_texts[i]->setText(t); + + // The next ifs will segfault with a NULL pointer + continue; + } + + + m_images[i]->setImage(item->getImage()); + + wchar_t t[10]; + if(m_selection == j) + swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str()); + else + swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str()); + m_texts[i]->setText(t); + } + } + +private: + s32 m_itemcount; + core::array m_texts; + core::array m_images; + Inventory *m_inventory; + s32 m_selection; +}; + +int main(int argc, char *argv[]) +{ + /* + Low-level initialization + */ + + bool disable_stderr = false; +#ifdef _WIN32 + disable_stderr = true; +#endif + + debugstreams_init(disable_stderr, DEBUGFILE); + debug_stacks_init(); + + + DSTACK(__FUNCTION_NAME); + + try + { + + /* + Basic initialization + */ + + dstream<= 2) + { + readConfigFile(argv[1]); + } + else + { + const char *filenames[2] = + { + "../minetest.conf", + "../../minetest.conf" + }; + + for(u32 i=0; i<2; i++) + { + bool r = readConfigFile(filenames[i]); + if(r) + break; + } + } + + // Initialize random seed + srand(time(0)); + + g_range_mutex.Init(); + assert(g_range_mutex.IsInitialized()); + + /* + Ask some stuff + */ + + std::cout< yes"< no"< "< list = server.getPlayerInfo(); + core::list::Iterator i; + static u32 sum_old = 0; + u32 sum = PIChecksum(list); + if(sum != sum_old) + { + std::cout<PrintLine(&std::cout); + } + } + sum_old = sum; + } + } + + return 0; + } + + bool hosting = false; + char connect_name[100] = ""; + + std::cout<<"Address to connect to [empty = host a game]: "; + if(g_address != "" && is_yes(g_host_game) == false) + { + std::cout< hosting"< "< \""< res_count || r0 == 0) + r0 = 2; + + { + u16 i = r0-1; + std::cout<<"-> "; + std::cout<<(i+1)<<": "<(screenW, screenH), + 16, fullscreen, false, false, &receiver); + // With vsync + /*device = createDevice(driverType, + core::dimension2d(screenW, screenH), + 16, fullscreen, false, true, &receiver);*/ + + if (device == 0) + return 1; // could not create selected driver. + + g_device = device; + + device->setResizable(true); + + if(g_random_input) + g_input = new RandomInputHandler(); + else + g_input = new RealInputHandler(device, &receiver); + + /* + Continue initialization + */ + + video::IVideoDriver* driver = device->getVideoDriver(); + // These make the textures not to show at all + //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT); + //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED ); + + //driver->setMinHardwareBufferVertexCount(1); + + scene::ISceneManager* smgr = device->getSceneManager(); + + gui::IGUIEnvironment* guienv = device->getGUIEnvironment(); + gui::IGUISkin* skin = guienv->getSkin(); + gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png"); + if(font) + skin->setFont(font); + //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); + skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); + //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); + //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); + skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0)); + skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); + + const wchar_t *text = L"Loading and connecting..."; + core::vector2d center(screenW/2, screenH/2); + core::dimension2d textd = font->getDimension(text); + std::cout<getTexture(filename)); + } + //g_materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT); + g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false); + //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false); + //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true); + if(i == MATERIAL_WATER) + { + g_materials[i].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + //g_materials[i].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + } + } + + /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png")); + g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png")); + g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png")); + for(u32 i=0; i<3; i++) + { + g_mesh_materials[i].Lighting = false; + g_mesh_materials[i].BackfaceCulling = false; + g_mesh_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false); + g_mesh_materials[i].setFlag(video::EMF_FOG_ENABLE, true); + }*/ + + // Make a scope here for the client so that it gets removed + // before the irrlicht device + { + + std::cout< server; + if(hosting){ + server = new Server("../map", g_creative_mode, g_mapgen_params); + server->start(port); + } + + /* + Create client + */ + + // TODO: Get rid of the g_materials parameter or it's globalness + Client client(device, g_materials, + g_client_delete_unused_sectors_timeout, + playername); + + Address connect_address(0,0,0,0, port); + try{ + connect_address.Resolve(connect_name); + } + catch(ResolveError &e) + { + std::cout<step(0.1); + } + sleep_ms(100); + } + } + catch(con::PeerNotFoundException &e) + { + std::cout<addCameraSceneNode( + 0, // Camera parent + v3f(BS*100, BS*2, BS*100), // Look from + v3f(BS*100+1, BS*2, BS*100), // Look to + -1 // Camera ID + ); + + if(camera == NULL) + return 1; + + video::SColor skycolor = video::SColor(255,90,140,200); + + camera->setFOV(FOV_ANGLE); + + // Just so big a value that everything rendered is visible + camera->setFarValue(100000*BS); + + /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9; + f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9; + + camera->setFarValue(range); + + driver->setFog( + skycolor, + video::EFT_FOG_LINEAR, + range*0.8, + range, + 0.01, + false, + false + );*/ + + f32 camera_yaw = 0; // "right/left" + f32 camera_pitch = 0; // "up/down" + + gui_loadingtext->remove(); + + /* + Add some gui stuff + */ + + // First line of debug text + gui::IGUIStaticText *guitext = guienv->addStaticText( + L"Minetest-c55", + core::rect(5, 5, 5+600, 5+textsize.Y), + false, false); + // Second line of debug text + gui::IGUIStaticText *guitext2 = guienv->addStaticText( + L"", + core::rect(5, 5+(textsize.Y+5)*1, 5+600, (5+textsize.Y)*2), + false, false); + + // At the middle of the screen + // Object infos are shown in this + gui::IGUIStaticText *guitext_info = guienv->addStaticText( + L"test", + core::rect(100, 70, 100+400, 70+(textsize.Y+5)), + false, false); + + // This is a copy of the inventory that the client's environment has + Inventory local_inventory(PLAYER_INVENTORY_SIZE); + + GUIQuickInventory *quick_inventory = new GUIQuickInventory + (guienv, NULL, v2s32(10, 70), 5, &local_inventory); + + /* + Some statistics are collected in these + */ + u32 drawtime = 0; + u32 scenetime = 0; + u32 endscenetime = 0; + + /* + Text input system + */ + + struct TextDest + { + virtual void sendText(std::string text) = 0; + }; + + struct TextDestSign : public TextDest + { + TextDestSign(v3s16 blockpos, s16 id, Client *client) + { + m_blockpos = blockpos; + m_id = id; + m_client = client; + } + void sendText(std::string text) + { + dstream<<"Changing text of a sign object: " + <sendSignText(m_blockpos, m_id, text); + } + + v3s16 m_blockpos; + s16 m_id; + Client *m_client; + }; + + TextDest *textbuf_dest = NULL; + + //gui::IGUIWindow* input_window = NULL; + gui::IGUIStaticText* input_guitext = NULL; + + /* + Main loop + */ + + bool first_loop_after_window_activation = true; + + // Time is in milliseconds + // NOTE: getRealTime() without run()s causes strange problems in wine + // NOTE: Have to call run() between calls of this to update the timer + u32 lasttime = device->getTimer()->getTime(); + + while(device->run()) + { + // Hilight boxes collected during the loop and displayed + core::list< core::aabbox3d > hilightboxes; + + // Info text + std::wstring infotext; + + //TimeTaker //timer1("//timer1", device); + + // Time of frame without fps limit + float busytime; + u32 busytime_u32; + { + // not using getRealTime is necessary for wine + u32 time = device->getTimer()->getTime(); + if(time > lasttime) + busytime_u32 = time - lasttime; + else + busytime_u32 = 0; + busytime = busytime_u32 / 1000.0; + } + + //std::cout<<"busytime_u32="<run(); + + /* + Viewing range + */ + + //updateViewingRange(dtime, &client); + updateViewingRange(busytime, &client); + + /* + FPS limiter + */ + + { + float fps_max = g_fps_max; + u32 frametime_min = 1000./fps_max; + + if(busytime_u32 < frametime_min) + { + u32 sleeptime = frametime_min - busytime_u32; + device->sleep(sleeptime); + } + } + + // Absolutelu necessary for wine! + device->run(); + + /* + Time difference calculation + */ + f32 dtime; // in seconds + + u32 time = device->getTimer()->getTime(); + if(time > lasttime) + dtime = (time - lasttime) / 1000.0; + else + dtime = 0; + lasttime = time; + + /* + Time average and jitter calculation + */ + + static f32 dtime_avg1 = 0.0; + dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02; + f32 dtime_jitter1 = dtime - dtime_avg1; + + static f32 dtime_jitter1_max_sample = 0.0; + static f32 dtime_jitter1_max_fraction = 0.0; + { + static f32 jitter1_max = 0.0; + static f32 counter = 0.0; + if(dtime_jitter1 > jitter1_max) + jitter1_max = dtime_jitter1; + counter += dtime; + if(counter > 0.0) + { + counter -= 3.0; + dtime_jitter1_max_sample = jitter1_max; + dtime_jitter1_max_fraction + = dtime_jitter1_max_sample / (dtime_avg1+0.001); + jitter1_max = 0.0; + + /* + Control freetime ratio + */ + /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION) + { + if(g_freetime_ratio < FREETIME_RATIO_MAX) + g_freetime_ratio += 0.01; + } + else + { + if(g_freetime_ratio > FREETIME_RATIO_MIN) + g_freetime_ratio -= 0.01; + }*/ + } + } + + /* + Busytime average and jitter calculation + */ + + static f32 busytime_avg1 = 0.0; + busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02; + f32 busytime_jitter1 = busytime - busytime_avg1; + + static f32 busytime_jitter1_max_sample = 0.0; + static f32 busytime_jitter1_min_sample = 0.0; + { + static f32 jitter1_max = 0.0; + static f32 jitter1_min = 0.0; + static f32 counter = 0.0; + if(busytime_jitter1 > jitter1_max) + jitter1_max = busytime_jitter1; + if(busytime_jitter1 < jitter1_min) + jitter1_min = busytime_jitter1; + counter += dtime; + if(counter > 0.0){ + counter -= 3.0; + busytime_jitter1_max_sample = jitter1_max; + busytime_jitter1_min_sample = jitter1_min; + jitter1_max = 0.0; + jitter1_min = 0.0; + } + } + + /* + Debug info for client + */ + { + static float counter = 0.0; + counter -= dtime; + if(counter < 0) + { + counter = 30.0; + client.printDebugInfo(std::cout); + } + } + + /* + Input handler step() + */ + g_input->step(dtime); + + /* + Special keys + */ + if(g_esc_pressed) + { + break; + } + + /* + Player speed control + */ + + if(g_game_focused) + { + /*bool a_up, + bool a_down, + bool a_left, + bool a_right, + bool a_jump, + bool a_superspeed, + float a_pitch, + float a_yaw*/ + PlayerControl control( + g_input->isKeyDown(irr::KEY_KEY_W), + g_input->isKeyDown(irr::KEY_KEY_S), + g_input->isKeyDown(irr::KEY_KEY_A), + g_input->isKeyDown(irr::KEY_KEY_D), + g_input->isKeyDown(irr::KEY_SPACE), + g_input->isKeyDown(irr::KEY_KEY_2), + camera_pitch, + camera_yaw + ); + client.setPlayerControl(control); + } + else + { + // Set every key to inactive + PlayerControl control; + client.setPlayerControl(control); + } + + //timer1.stop(); + /* + Process environment + */ + + { + //TimeTaker timer("client.step(dtime)", device); + client.step(dtime); + //client.step(dtime_avg1); + } + + if(server != NULL) + { + //TimeTaker timer("server->step(dtime)", device); + server->step(dtime); + } + + v3f player_position = client.getPlayerPosition(); + + //TimeTaker //timer2("//timer2", device); + + /* + Mouse and camera control + */ + + if(device->isWindowActive() && g_game_focused) + { + device->getCursorControl()->setVisible(false); + + if(first_loop_after_window_activation){ + //std::cout<<"window active, first loop"<getMousePos().X - 320; + s32 dy = g_input->getMousePos().Y - 240; + //std::cout<<"window active, pos difference "< 89.5) camera_pitch = 89.5; + } + g_input->setMousePos(320, 240); + } + else{ + device->getCursorControl()->setVisible(true); + + //std::cout<<"window inactive"<setPosition(camera_position); + // *100.0 helps in large map coordinates + camera->setTarget(camera_position + camera_direction * 100.0); + + if(FIELD_OF_VIEW_TEST){ + //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1)); + client.updateCamera(v3f(0,0,0), v3f(0,0,1)); + } + else{ + //client.m_env.getMap().updateCamera(camera_position, camera_direction); + //TimeTaker timer("client.updateCamera", device); + client.updateCamera(camera_position, camera_direction); + } + + //timer2.stop(); + //TimeTaker //timer3("//timer3", device); + + /* + Calculate what block is the crosshair pointing to + */ + + //u32 t1 = device->getTimer()->getRealTime(); + + //f32 d = 4; // max. distance + f32 d = 4; // max. distance + core::line3d shootline(camera_position, + camera_position + camera_direction * BS * (d+1)); + + MapBlockObject *selected_object = client.getSelectedObject + (d*BS, camera_position, shootline); + + if(selected_object != NULL) + { + //dstream<<"Client returned selected_object != NULL"< box_on_map + = selected_object->getSelectionBoxOnMap(); + + hilightboxes.push_back(box_on_map); + + infotext = narrow_to_wide(selected_object->infoText()); + + if(g_input->getLeftClicked()) + { + std::cout<getBlock()->getPos(), + selected_object->getId(), g_selected_item); + } + else if(g_input->getRightClicked()) + { + std::cout<getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN) + { + dstream<<"Sign object right-clicked"<addStaticText(L"", + core::rect(150,100,350,120), + true, // border? + false, // wordwrap? + NULL); + + input_guitext->setDrawBackground(true); + + g_text_buffer = L""; + g_text_buffer_accepted = false; + textbuf_dest = new TextDestSign( + selected_object->getBlock()->getPos(), + selected_object->getId(), + &client); + } + /* + Otherwise pass the event to the server as-is + */ + else + { + client.clickObject(1, selected_object->getBlock()->getPos(), + selected_object->getId(), g_selected_item); + } + } + } + else // selected_object == NULL + { + + bool nodefound = false; + v3s16 nodepos; + v3s16 neighbourpos; + core::aabbox3d nodefacebox; + f32 mindistance = BS * 1001; + + v3s16 pos_i = floatToInt(player_position); + + /*std::cout<<"pos_i=("<0 ? a : 1); + s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1); + s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1); + + for(s16 y = ystart; y <= yend; y++){ + for(s16 z = zstart; z <= zend; z++){ + for(s16 x = xstart; x <= xend; x++) + { + try{ + if(client.getNode(v3s16(x,y,z)).d == MATERIAL_AIR){ + continue; + } + }catch(InvalidPositionException &e){ + continue; + } + + v3s16 np(x,y,z); + v3f npf = intToFloat(np); + + f32 d = 0.01; + + v3s16 directions[6] = { + v3s16(0,0,1), // back + v3s16(0,1,0), // top + v3s16(1,0,0), // right + v3s16(0,0,-1), + v3s16(0,-1,0), + v3s16(-1,0,0), + }; + + for(u16 i=0; i<6; i++){ + //{u16 i=3; + v3f dir_f = v3f(directions[i].X, + directions[i].Y, directions[i].Z); + v3f centerpoint = npf + dir_f * BS/2; + f32 distance = + (centerpoint - camera_position).getLength(); + + if(distance < mindistance){ + //std::cout< m; + m.buildRotateFromTo(v3f(0,0,1), dir_f); + + // This is the back face + v3f corners[2] = { + v3f(BS/2, BS/2, BS/2), + v3f(-BS/2, -BS/2, BS/2+d) + }; + + for(u16 j=0; j<2; j++){ + m.rotateVect(corners[j]); + corners[j] += npf; + //std::cout< facebox(corners[0],corners[1]); + core::aabbox3d facebox(corners[0]); + facebox.addInternalPoint(corners[1]); + + if(facebox.intersectsWithLine(shootline)){ + nodefound = true; + nodepos = np; + neighbourpos = np + directions[i]; + mindistance = distance; + nodefacebox = facebox; + } + } + } + }}} + + if(nodefound) + { + //std::cout<setText(positiontext);*/ + } + + hilightboxes.push_back(nodefacebox); + + if(g_input->getLeftClicked()) + { + //std::cout<getRightClicked()) + { + //std::cout<setText(L""); + } + + } // selected_object == NULL + + g_input->resetLeftClicked(); + g_input->resetRightClicked(); + + /* + Calculate stuff for drawing + */ + + v2u32 screensize = driver->getScreenSize(); + core::vector2d displaycenter(screensize.X/2,screensize.Y/2); + + camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y); + + /* + Update gui stuff (0ms) + */ + + //TimeTaker guiupdatetimer("Gui updating", device); + + { + wchar_t temptext[100]; + + static float drawtime_avg = 0; + drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02; + static float scenetime_avg = 0; + scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02; + static float endscenetime_avg = 0; + endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02; + + swprintf(temptext, 100, L"Minetest-c55 (" + L"F: item=%i" + L", R: range_all=%i" + L")" + L" drawtime=%.0f, scenetime=%.0f, endscenetime=%.0f", + g_selected_item, + g_viewing_range_all, + drawtime_avg, + scenetime_avg, + endscenetime_avg + ); + + guitext->setText(temptext); + } + + { + wchar_t temptext[100]; + /*swprintf(temptext, 100, + L"(" + L"% .3f < btime_jitter < % .3f" + L", dtime_jitter = % .1f %%" + //L", ftime_ratio = % .3f" + L")", + busytime_jitter1_min_sample, + busytime_jitter1_max_sample, + dtime_jitter1_max_fraction * 100.0 + //g_freetime_ratio + );*/ + swprintf(temptext, 100, + L"(% .1f, % .1f, % .1f)" + L" (% .3f < btime_jitter < % .3f" + L", dtime_jitter = % .1f %%)", + player_position.X/BS, + player_position.Y/BS, + player_position.Z/BS, + busytime_jitter1_min_sample, + busytime_jitter1_max_sample, + dtime_jitter1_max_fraction * 100.0 + ); + + guitext2->setText(temptext); + } + + { + /*wchar_t temptext[100]; + swprintf(temptext, 100, + SWPRINTF_CHARSTRING, + infotext.substr(0,99).c_str() + ); + + guitext_info->setText(temptext);*/ + + guitext_info->setText(infotext.c_str()); + } + + /* + Inventory + */ + + static u16 old_selected_item = 65535; + if(client.getLocalInventoryUpdated() + || g_selected_item != old_selected_item) + { + old_selected_item = g_selected_item; + //std::cout<<"Updating local inventory"<setSelection(g_selected_item); + quick_inventory->update(); + } + + if(input_guitext != NULL) + { + /*wchar_t temptext[100]; + swprintf(temptext, 100, + SWPRINTF_CHARSTRING, + g_text_buffer.substr(0,99).c_str() + );*/ + input_guitext->setText(g_text_buffer.c_str()); + } + + /* + Text input stuff + */ + if(input_guitext != NULL && g_text_buffer_accepted) + { + input_guitext->remove(); + input_guitext = NULL; + + if(textbuf_dest != NULL) + { + std::string text = wide_to_narrow(g_text_buffer); + dstream<<"Sending text: "<sendText(text); + delete textbuf_dest; + textbuf_dest = NULL; + } + + focusGame(); + } + + //guiupdatetimer.stop(); + + /* + Drawing begins + */ + + TimeTaker drawtimer("Drawing", device); + + /* + Background color is choosen based on whether the player is + much beyond the initial ground level + */ + /*video::SColor bgcolor; + v3s16 p0 = Map::floatToInt(player_position); + // Does this make short random delays? + // NOTE: no need for this, sky doesn't show underground with + // enough range + bool is_underground = client.isNodeUnderground(p0); + //bool is_underground = false; + if(is_underground == false) + bgcolor = video::SColor(255,90,140,200); + else + bgcolor = video::SColor(255,0,0,0);*/ + + //video::SColor bgcolor = video::SColor(255,90,140,200); + video::SColor bgcolor = skycolor; + + // 0ms + driver->beginScene(true, true, bgcolor); + + //timer3.stop(); + + //std::cout<drawAll()"<drawAll(); + scenetime = timer.stop(true); + } + + { + //TimeTaker timer9("auxiliary drawings", device); + // 0ms + + driver->draw2DLine(displaycenter - core::vector2d(10,0), + displaycenter + core::vector2d(10,0), + video::SColor(255,255,255,255)); + driver->draw2DLine(displaycenter - core::vector2d(0,10), + displaycenter + core::vector2d(0,10), + video::SColor(255,255,255,255)); + + //timer9.stop(); + //TimeTaker //timer10("//timer10", device); + + video::SMaterial m; + m.Thickness = 10; + m.Lighting = false; + driver->setMaterial(m); + + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); + + for(core::list< core::aabbox3d >::Iterator i=hilightboxes.begin(); + i != hilightboxes.end(); i++) + { + /*std::cout<<"hilightbox min=" + <<"("<MinEdge.X<<","<MinEdge.Y<<","<MinEdge.Z<<")" + <<" max=" + <<"("<MaxEdge.X<<","<MaxEdge.Y<<","<MaxEdge.Z<<")" + <draw3DBox(*i, video::SColor(255,0,0,0)); + } + + } + + //timer10.stop(); + //TimeTaker //timer11("//timer11", device); + + /* + Draw gui + */ + // 0-1ms + guienv->drawAll(); + + // End drawing + { + TimeTaker timer("endScene", device); + driver->endScene(); + endscenetime = timer.stop(true); + } + + drawtime = drawtimer.stop(true); + + /* + Drawing ends + */ + + static s16 lastFPS = 0; + //u16 fps = driver->getFPS(); + u16 fps = (1.0/dtime_avg1); + + if (lastFPS != fps) + { + core::stringw str = L"Minetest ["; + str += driver->getName(); + str += "] FPS:"; + str += fps; + + device->setWindowCaption(str.c_str()); + lastFPS = fps; + } + + /*} + else + device->yield();*/ + } + + } // client is deleted at this point + + delete g_input; + + /* + In the end, delete the Irrlicht device. + */ + device->drop(); + + } //try + catch(con::PeerNotFoundException &e) + { + dstream<