From d53a6cd8518edd025acdfc7878ede857867e507b Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 2 Sep 2012 19:38:44 +0300 Subject: [PATCH 01/12] Add overridable function for handling dropped items from nodes --- builtin/item.lua | 21 +++++++++++++-------- doc/lua_api.txt | 6 ++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/builtin/item.lua b/builtin/item.lua index 4e3c74444..0f8dcff1f 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -260,6 +260,16 @@ function minetest.node_punch(pos, node, puncher) end end +function minetest.handle_node_drops(pos, drops, digger) + -- Add dropped items to object's inventory + if digger:get_inventory() then + local _, dropped_item + for _, dropped_item in ipairs(drops) do + digger:get_inventory():add_item("main", dropped_item) + end + end +end + function minetest.node_dig(pos, node, digger) minetest.debug("node_dig") @@ -284,14 +294,9 @@ function minetest.node_dig(pos, node, digger) local dp = minetest.get_dig_params(def.groups, tp) wielded:add_wear(dp.wear) digger:set_wielded_item(wielded) - - -- Add dropped items to object's inventory - if digger:get_inventory() then - local _, dropped_item - for _, dropped_item in ipairs(drops) do - digger:get_inventory():add_item("main", dropped_item) - end - end + + -- Handle drops + minetest.handle_node_drops(pos, drops, digger) local oldmetadata = nil if def.after_dig_node then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d3e254a2d..b71b6d62c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -891,6 +891,12 @@ minetest.get_craft_recipe(output) -> input ^ input.items = for example { stack 1, stack 2, stack 3, stack 4, stack 5, stack 6, stack 7, stack 8, stack 9 } ^ input.items = nil if no recipe found +minetest.handle_node_drops(pos, drops, digger) +^ drops: list of itemstrings +^ Handles drops from nodes after digging: Default action is to put them into + digger's inventory +^ Can be overridden to get different functionality (eg. dropping items on + ground) Rollbacks: minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds From 6495007924d8907ddfff14be09d38a4b1745b95a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 2 Sep 2012 20:13:11 +0300 Subject: [PATCH 02/12] Fix bug in moving a larger sized stack in inventory on top of another - You have 2 stacks: a with x items and b with y Date: Sun, 2 Sep 2012 22:51:38 +0300 Subject: [PATCH 03/12] Make inventory GUI do sane things when server-side inventory acts unusually --- src/guiFormSpecMenu.cpp | 68 +++++++++++++++++++++++++++++++++++++--- src/guiFormSpecMenu.h | 6 ++++ src/inventorymanager.cpp | 12 +++++++ src/inventorymanager.h | 23 ++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 41ec0f3da..ed44e441b 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -733,6 +733,54 @@ void GUIFormSpecMenu::drawMenu() void GUIFormSpecMenu::updateSelectedItem() { + // WARNING: BLACK MAGIC + // See if there is a stack suited for our current guess. + // If such stack does not exist, clear the guess. + if(m_selected_content_guess.name != "") + { + bool found = false; + for(u32 i=0; igetInventory(s.inventoryloc); + if(!inv) + continue; + InventoryList *list = inv->getList(s.listname); + if(!list) + continue; + for(s32 i=0; i= list->getSize()) + continue; + ItemStack stack = list->getItem(item_i); + if(stack.name == m_selected_content_guess.name && + stack.count == m_selected_content_guess.count){ + found = true; + if(m_selected_item){ + // If guessed stack is already selected, all is fine + if(m_selected_item->inventoryloc == s.inventoryloc && + m_selected_item->listname == s.listname && + m_selected_item->i == (s32)item_i && + m_selected_amount == stack.count){ + break; + } + delete m_selected_item; + m_selected_item = NULL; + } + infostream<<"Client: Changing selected content guess to " + <idef()); - if(leftover.count == stack_from.count) + // If source stack cannot be added to destination stack at all, + // they are swapped + if(leftover.count == stack_from.count && leftover.name == stack_from.name) { - // Swap the stacks m_selected_amount = stack_to.count; + // In case the server doesn't directly swap them but instead + // moves stack_to somewhere else, set this + m_selected_content_guess = stack_to; + m_selected_content_guess_inventory = s.inventoryloc; } + // Source stack goes fully into destination stack else if(leftover.empty()) { - // Item fits m_selected_amount -= move_amount; + m_selected_content_guess = ItemStack(); // Clear } + // Source stack goes partly into destination stack else { - // Item only fits partially move_amount -= leftover.count; m_selected_amount -= move_amount; + m_selected_content_guess = ItemStack(); // Clear } infostream<<"Handing IACTION_MOVE to manager"< 0) { + m_selected_content_guess = ItemStack(); // Clear + // Send IACTION_DROP assert(m_selected_item && m_selected_item->isValid()); @@ -1107,6 +1164,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } else if(craft_amount > 0) { + m_selected_content_guess = ItemStack(); // Clear + // Send IACTION_CRAFT assert(s.isValid()); @@ -1126,6 +1185,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_selected_item = NULL; m_selected_amount = 0; m_selected_dragging = false; + m_selected_content_guess = ItemStack(); } } if(event.EventType==EET_GUI_EVENT) diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index f0a5988e9..5c01bdcd2 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -212,6 +212,12 @@ protected: ItemSpec *m_selected_item; u32 m_selected_amount; bool m_selected_dragging; + + // WARNING: BLACK MAGIC + // Used to guess and keep up with some special things the server can do. + // If name is "", no guess exists. + ItemStack m_selected_content_guess; + InventoryLocation m_selected_content_guess_inventory; v2s32 m_pointer; gui::IGUIStaticText *m_tooltip_element; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index e2e537838..1a7f56f31 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -332,6 +332,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // If source is infinite, reset it's stack if(src_can_take_count == -1){ + // If destination stack is of different type and there are leftover + // items, attempt to put the leftover items to a different place in the + // destination inventory. + // The client-side GUI will try to guess if this happens. + if(from_stack_was.name != to_stack_was.name){ + for(u32 i=0; igetSize(); i++){ + if(list_to->getItem(i).empty()){ + list_to->changeItem(i, to_stack_was); + break; + } + } + } list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); } diff --git a/src/inventorymanager.h b/src/inventorymanager.h index dae14f1a6..f81f5b972 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -66,6 +66,29 @@ struct InventoryLocation name = name_; } + bool operator==(const InventoryLocation &other) const + { + if(type != other.type) + return false; + switch(type){ + case UNDEFINED: + return false; + case CURRENT_PLAYER: + return true; + case PLAYER: + return (name == other.name); + case NODEMETA: + return (p == other.p); + case DETACHED: + return (name == other.name); + } + return false; + } + bool operator!=(const InventoryLocation &other) const + { + return !(*this == other); + } + void applyCurrentPlayer(const std::string &name_) { if(type == CURRENT_PLAYER) From cc47ff7c4cd3a2ae1685f19c52da98164befcd0a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 2 Sep 2012 23:01:40 +0300 Subject: [PATCH 04/12] Fix moving stuff into a mismatched stack in a "infinite" inventory --- src/inventorymanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 1a7f56f31..4d862de66 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -351,6 +351,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame if(dst_can_put_count == -1){ list_to->deleteItem(to_i); list_to->addItem(to_i, to_stack_was); + list_from->deleteItem(from_i); + list_from->addItem(from_i, from_stack_was); list_from->takeItem(from_i, count); } From 0e6f7a21c60d162b3b7134882dc22dbd8acd014f Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 3 Sep 2012 23:31:51 +0300 Subject: [PATCH 05/12] Fix shift-descend to ladders from a floor --- src/localplayer.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 46a10c90b..4b5e53fea 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -150,13 +150,16 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd); position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd); - f32 min_y = lwn_f.Y + 0.5*BS; - if(position.Y < min_y) + if(!is_climbing) { - position.Y = min_y; + f32 min_y = lwn_f.Y + 0.5*BS; + if(position.Y < min_y) + { + position.Y = min_y; - if(m_speed.Y < 0) - m_speed.Y = 0; + if(m_speed.Y < 0) + m_speed.Y = 0; + } } } From ee2d9d973a0397ce244f05d49083250956578780 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 4 Sep 2012 09:48:26 +0300 Subject: [PATCH 06/12] Reorganize ClientMap rendering code for a bit more performance - Don't select blocks for drawing in every frame - Sort meshbuffers by material before drawing --- src/clientmap.cpp | 331 ++++++++++++++++++++++++++++++++-------------- src/clientmap.h | 5 +- src/game.cpp | 17 ++- src/map.cpp | 4 +- src/mapblock.cpp | 3 +- src/mapblock.h | 22 +++ 6 files changed, 281 insertions(+), 101 deletions(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 4d14cc1a2..64d5656d4 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -157,43 +157,21 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac, return false; } -void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) +void ClientMap::updateDrawList(video::IVideoDriver* driver) { + ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); + g_profiler->add("CM::updateDrawList() count", 1); + INodeDefManager *nodemgr = m_gamedef->ndef(); - //m_dout<::Iterator + i = m_drawlist.getIterator(); + i.atEnd() == false; i++) { - m_last_drawn_sectors.clear(); + MapBlock *block = i.getNode()->getValue(); + block->refDrop(); } - - /* - Get time for measuring timeout. - - Measuring time is very useful for long delays when the - machine is swapping a lot. - */ - int time1 = time(0); - - /* - Get animation parameters - */ - float animation_time = m_client->getAnimationTime(); - int crack = m_client->getCrackLevel(); - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + m_drawlist.clear(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; @@ -201,17 +179,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); - /* - Get all blocks and draw all visible ones - */ + // Use a higher fov to accomodate faster camera movements. + // Blocks are cropped better when they are drawn. + // Or maybe they aren't? Well whatever. + camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); - v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); - v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; - // Take a fair amount as we will be dropping more out later // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( @@ -223,13 +199,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) p_nodes_max.Y / MAP_BLOCKSIZE + 1, p_nodes_max.Z / MAP_BLOCKSIZE + 1); - u32 vertex_count = 0; - u32 meshbuffer_count = 0; - - // For limiting number of mesh animations per frame - u32 mesh_animate_count = 0; - u32 mesh_animate_count_far = 0; - // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled @@ -242,18 +211,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass - u32 blocks_had_pass_meshbuf = 0; + //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn - u32 blocks_without_stuff = 0; - - /* - Collect a set of blocks for drawing - */ - - core::map drawset; - - { - ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG); + //u32 blocks_without_stuff = 0; for(core::map::Iterator si = m_sectors.getIterator(); @@ -380,36 +340,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) && d > m_control.wanted_min_range * BS) continue; - // Mesh animation - { - //JMutexAutoLock lock(block->mesh_mutex); - MapBlockMesh *mapBlockMesh = block->mesh; - // Pretty random but this should work somewhat nicely - bool faraway = d >= BS*50; - //bool faraway = d >= m_control.wanted_range * BS; - if(mapBlockMesh->isAnimationForced() || - !faraway || - mesh_animate_count_far < (m_control.range_all ? 200 : 50)) - { - bool animated = mapBlockMesh->animate( - faraway, - animation_time, - crack, - daynight_ratio); - if(animated) - mesh_animate_count++; - if(animated && faraway) - mesh_animate_count_far++; - } - else - { - mapBlockMesh->decreaseAnimationForceTimer(); - } - } - // Add to set - drawset[block->getPos()] = block; - + block->refGrab(); + m_drawlist[block->getPos()] = block; + sector_blocks_drawn++; blocks_drawn++; @@ -418,8 +352,127 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if(sector_blocks_drawn != 0) m_last_drawn_sectors[sp] = true; } - } // ScopeProfiler + g_profiler->avg("CM: blocks in range", blocks_in_range); + g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); + if(blocks_in_range != 0) + g_profiler->avg("CM: blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh/blocks_in_range); + g_profiler->avg("CM: blocks drawn", blocks_drawn); +} + +struct MeshBufList +{ + video::SMaterial m; + core::list bufs; +}; + +struct MeshBufListList +{ + core::list lists; + + void clear() + { + lists.clear(); + } + + void add(scene::IMeshBuffer *buf) + { + for(core::list::Iterator i = lists.begin(); + i != lists.end(); i++){ + MeshBufList &l = *i; + if(l.m == buf->getMaterial()){ + l.bufs.push_back(buf); + return; + } + } + MeshBufList l; + l.m = buf->getMaterial(); + l.bufs.push_back(buf); + lists.push_back(l); + } +}; + +void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) +{ + DSTACK(__FUNCTION_NAME); + + bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; + + std::string prefix; + if(pass == scene::ESNRP_SOLID) + prefix = "CM: solid: "; + else + prefix = "CM: transparent: "; + + /* + This is called two times per frame, reset on the non-transparent one + */ + if(pass == scene::ESNRP_SOLID) + { + m_last_drawn_sectors.clear(); + } + + /* + Get time for measuring timeout. + + Measuring time is very useful for long delays when the + machine is swapping a lot. + */ + int time1 = time(0); + + /* + Get animation parameters + */ + float animation_time = m_client->getAnimationTime(); + int crack = m_client->getCrackLevel(); + u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + + m_camera_mutex.Lock(); + v3f camera_position = m_camera_position; + v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; + m_camera_mutex.Unlock(); + + /* + Get all blocks and draw all visible ones + */ + + v3s16 cam_pos_nodes = floatToInt(camera_position, BS); + + v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); + + v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; + v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; + + // Take a fair amount as we will be dropping more out later + // Umm... these additions are a bit strange but they are needed. + v3s16 p_blocks_min( + p_nodes_min.X / MAP_BLOCKSIZE - 3, + p_nodes_min.Y / MAP_BLOCKSIZE - 3, + p_nodes_min.Z / MAP_BLOCKSIZE - 3); + v3s16 p_blocks_max( + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); + + u32 vertex_count = 0; + u32 meshbuffer_count = 0; + + // For limiting number of mesh animations per frame + u32 mesh_animate_count = 0; + u32 mesh_animate_count_far = 0; + + // Blocks that had mesh that would have been drawn according to + // rendering range (if max blocks limit didn't kick in) + u32 blocks_would_have_drawn = 0; + // Blocks that were drawn and had a mesh + u32 blocks_drawn = 0; + // Blocks which had a corresponding meshbuffer for this pass + u32 blocks_had_pass_meshbuf = 0; + // Blocks from which stuff was actually drawn + u32 blocks_without_stuff = 0; + /* Draw the selected MapBlocks */ @@ -427,10 +480,90 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); - int timecheck_counter = 0; + MeshBufListList drawbufs; + for(core::map::Iterator - i = drawset.getIterator(); + i = m_drawlist.getIterator(); i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + + // If the mesh of the block happened to get deleted, ignore it + if(block->mesh == NULL) + continue; + + float d = 0.0; + if(isBlockInSight(block->getPos(), camera_position, + camera_direction, camera_fov, + 100000*BS, &d) == false) + { + continue; + } + + // Mesh animation + { + //JMutexAutoLock lock(block->mesh_mutex); + MapBlockMesh *mapBlockMesh = block->mesh; + assert(mapBlockMesh); + // Pretty random but this should work somewhat nicely + bool faraway = d >= BS*50; + //bool faraway = d >= m_control.wanted_range * BS; + if(mapBlockMesh->isAnimationForced() || + !faraway || + mesh_animate_count_far < (m_control.range_all ? 200 : 50)) + { + bool animated = mapBlockMesh->animate( + faraway, + animation_time, + crack, + daynight_ratio); + if(animated) + mesh_animate_count++; + if(animated && faraway) + mesh_animate_count_far++; + } + else + { + mapBlockMesh->decreaseAnimationForceTimer(); + } + } + + /* + Get the meshbuffers of the block + */ + { + //JMutexAutoLock lock(block->mesh_mutex); + + MapBlockMesh *mapBlockMesh = block->mesh; + assert(mapBlockMesh); + + scene::SMesh *mesh = mapBlockMesh->getMesh(); + assert(mesh); + + u32 c = mesh->getMeshBufferCount(); + for(u32 i=0; igetMeshBuffer(i); + const video::SMaterial& material = buf->getMaterial(); + video::IMaterialRenderer* rnd = + driver->getMaterialRenderer(material.MaterialType); + bool transparent = (rnd && rnd->isTransparent()); + if(transparent == is_transparent_pass) + { + if(buf->getVertexCount() == 0) + errorstream<<"Block ["< &lists = drawbufs.lists; + + int timecheck_counter = 0; + for(core::list::Iterator i = lists.begin(); + i != lists.end(); i++) { { timecheck_counter++; @@ -447,9 +580,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } } - - MapBlock *block = i.getNode()->getValue(); + MeshBufList &list = *i; + + driver->setMaterial(list.m); + + for(core::list::Iterator j = list.bufs.begin(); + j != list.bufs.end(); j++) + { + scene::IMeshBuffer *buf = *j; + driver->drawMeshBuffer(buf); + vertex_count += buf->getVertexCount(); + meshbuffer_count++; + } +#if 0 /* Draw the faces of the block */ @@ -494,17 +638,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) else blocks_without_stuff++; } +#endif } } // ScopeProfiler // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID){ - g_profiler->avg("CM: blocks in range", blocks_in_range); - g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); - if(blocks_in_range != 0) - g_profiler->avg("CM: blocks in range without mesh (frac)", - (float)blocks_in_range_without_mesh/blocks_in_range); - g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } diff --git a/src/clientmap.h b/src/clientmap.h index 29ebed159..f8a69630e 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -113,7 +113,8 @@ public: { return m_box; } - + + void updateDrawList(video::IVideoDriver* driver); void renderMap(video::IVideoDriver* driver, s32 pass); int getBackgroundBrightness(float max_d, u32 daylight_factor, @@ -141,6 +142,8 @@ private: v3f m_camera_direction; f32 m_camera_fov; JMutex m_camera_mutex; + + core::map m_drawlist; core::map m_last_drawn_sectors; }; diff --git a/src/game.cpp b/src/game.cpp index 7d93e3db2..a1a197219 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1235,6 +1235,9 @@ void the_game( float object_hit_delay_timer = 0.0; float time_from_last_punch = 10; + float update_draw_list_timer = 0.0; + v3f update_draw_list_last_cam_dir; + bool invert_mouse = g_settings->getBool("invert_mouse"); bool respawn_menu_active = false; @@ -2697,7 +2700,19 @@ void the_game( item = mlist->getItem(client.getPlayerItem()); camera.wield(item); } - + + /* + Update block draw list every 200ms or when camera direction has + changed much + */ + update_draw_list_timer += dtime; + if(update_draw_list_timer >= 0.2 || + update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2){ + update_draw_list_timer = 0; + client.getEnv().getClientMap().updateDrawList(driver); + update_draw_list_last_cam_dir = camera_direction; + } + /* Drawing begins */ diff --git a/src/map.cpp b/src/map.cpp index 824553b37..39c6d292b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1468,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout, MapBlock *block = (*i); block->incrementUsageTimer(dtime); - - if(block->getUsageTimer() > unload_timeout) + + if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) { v3s16 p = block->getPos(); diff --git a/src/mapblock.cpp b/src/mapblock.cpp index b2da768f5..2ae6e9bd7 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -56,7 +56,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy): m_generated(false), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED), - m_usage_timer(0) + m_usage_timer(0), + m_refcount(0) { data = NULL; if(dummy == false) diff --git a/src/mapblock.h b/src/mapblock.h index 7f901e5d3..d56d93dda 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -430,6 +430,22 @@ public: { return m_usage_timer; } + + /* + See m_refcount + */ + void refGrab() + { + m_refcount++; + } + void refDrop() + { + m_refcount--; + } + int refGet() + { + return m_refcount; + } /* Node Timers @@ -566,6 +582,12 @@ private: Map will unload the block when this reaches a timeout. */ float m_usage_timer; + + /* + Reference count; currently used for determining if this block is in + the list of blocks to be drawn. + */ + int m_refcount; }; inline bool blockpos_over_limit(v3s16 p) From 60932d2bd76cbe006e297f36bc118b830ae969d3 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 4 Sep 2012 23:02:56 +0300 Subject: [PATCH 07/12] Remove "Unknown inventory identifier" deserialization errors, because they can break forward compatibility --- src/inventory.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/inventory.cpp b/src/inventory.cpp index fda11e40f..5e39a41a6 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -542,10 +542,6 @@ void InventoryList::deSerialize(std::istream &is) throw SerializationError("too many items"); m_items[item_i++].clear(); } - else - { - throw SerializationError("Unknown inventory identifier"); - } } } @@ -907,10 +903,6 @@ void Inventory::deSerialize(std::istream &is) m_lists.push_back(list); } - else - { - throw SerializationError("Unknown inventory identifier"); - } } } From 1b63f8d6b9cb3b8775f3a541dded890b0adfa6fd Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 5 Sep 2012 00:39:43 +0300 Subject: [PATCH 08/12] Version 0.4.3-rc0; increase PROTOCOL_VERSION due to backwards-incompatible InventoryList width field --- CMakeLists.txt | 2 +- src/clientserver.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f608f3c76..a9aa0f3a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 2-rc1) +set(VERSION_PATCH 3-rc0) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) endif() diff --git a/src/clientserver.h b/src/clientserver.h index a6ba8fe45..82e485d95 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -65,9 +65,11 @@ with this program; if not, write to the Free Software Foundation, Inc., TOSERVER_INVENTORY_FIELDS 16-bit node ids TOCLIENT_DETACHED_INVENTORY + PROTOCOL_VERSION 13: + InventoryList field "Width" (deserialization fails with old versions) */ -#define PROTOCOL_VERSION 12 +#define PROTOCOL_VERSION 13 #define PROTOCOL_ID 0x4f457403 From a0da6bcf43d71d22b949ccf1e68153b51da53e39 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 5 Sep 2012 00:55:19 +0300 Subject: [PATCH 09/12] Add aux1_descends to key change menu --- src/guiKeyChangeMenu.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index 49b292df4..405a81e0b 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -37,7 +37,7 @@ enum { GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR, - //buttons + // buttons GUI_ID_KEY_FORWARD_BUTTON, GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_LEFT_BUTTON, @@ -53,7 +53,9 @@ enum GUI_ID_KEY_DROP_BUTTON, GUI_ID_KEY_INVENTORY_BUTTON, GUI_ID_KEY_DUMP_BUTTON, - GUI_ID_KEY_RANGE_BUTTON + GUI_ID_KEY_RANGE_BUTTON, + // other + GUI_ID_CB_AUX1_DESCENDS, }; GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, @@ -135,6 +137,18 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) else offset += v2s32(0, 25); } + + { + s32 option_x = offset.X + 10; + s32 option_y = offset.Y; + u32 option_w = 180; + { + core::rect rect(0, 0, option_w, 30); + rect += topleft + v2s32(option_x, option_y); + Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this, + GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down")); + } + } { core::rect < s32 > rect(0, 0, 100, 30); @@ -177,6 +191,11 @@ bool GUIKeyChangeMenu::acceptInput() key_setting *k = key_settings.at(i); g_settings->set(k->setting_name, k->key.sym()); } + { + gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS); + if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) + g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked()); + } clearKeyCache(); return true; } From 5dd1d354f86692e4c08cc78f3d9743557103449e Mon Sep 17 00:00:00 2001 From: Matthew I Date: Sun, 2 Sep 2012 16:51:17 -0400 Subject: [PATCH 10/12] Enforce stricter world names using a blacklist Blacklisted characters are: / \ --- src/guiMainMenu.cpp | 10 +++++++++- src/guiMainMenu.h | 1 + src/subgame.h | 2 ++ src/util/string.h | 23 +++++++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index cdf1bc7d5..4ceecbb5f 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tile.h" // getTexturePath #include "filesys.h" #include "util/string.h" +#include "subgame.h" struct CreateWorldDestMainMenu : public CreateWorldDest { @@ -47,7 +48,10 @@ struct CreateWorldDestMainMenu : public CreateWorldDest {} void accepted(std::wstring name, std::string gameid) { - m_menu->createNewWorld(name, gameid); + if(!string_allowed_blacklist(wide_to_narrow(name), WORLDNAME_BLACKLISTED_CHARS)) + m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters")); + else + m_menu->createNewWorld(name, gameid); } GUIMainMenu *m_menu; }; @@ -929,3 +933,7 @@ int GUIMainMenu::getTab() return TAB_SINGLEPLAYER; // Default } +void GUIMainMenu::displayMessageMenu(std::wstring msg) +{ + (new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop(); +} \ No newline at end of file diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h index fa3d83c45..715deb47d 100644 --- a/src/guiMainMenu.h +++ b/src/guiMainMenu.h @@ -92,6 +92,7 @@ public: void createNewWorld(std::wstring name, std::string gameid); void deleteWorld(const std::vector &paths); int getTab(); + void displayMessageMenu(std::wstring msg); private: MainMenuData *m_data; diff --git a/src/subgame.h b/src/subgame.h index e3a299cbe..bffa86e28 100644 --- a/src/subgame.h +++ b/src/subgame.h @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#define WORLDNAME_BLACKLISTED_CHARS "/\\" + struct SubgameSpec { std::string id; // "" = game does not exist diff --git a/src/util/string.h b/src/util/string.h index 97b07f2ff..71b11de3d 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -242,6 +242,29 @@ inline bool string_allowed(const std::string &s, const std::string &allowed_char return true; } +/* + Checks if a string contains no blacklisted characters (opposite + function of string_allowed()) +*/ +inline bool string_allowed_blacklist(const std::string & s, const std::string & blacklisted_chars) +{ + for(unsigned int i = 0; i < s.length(); i++) + { + bool invalid = false; + for(unsigned int j = 0; j < blacklisted_chars.length(); j++) + { + if(s[i] == blacklisted_chars[j]) + { + invalid = true; + break; + } + } + if(invalid) + return false; + } + return true; +} + /* Forcefully wraps string into rows using \n (no word wrap, used for showing paths in gui) From c8895dd43b098d635ec0ae55fbbbfe5d1fe8bef9 Mon Sep 17 00:00:00 2001 From: Matthew I Date: Thu, 30 Aug 2012 15:41:48 -0400 Subject: [PATCH 11/12] Prevent world creation if the world already exists --- src/guiMainMenu.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index 4ceecbb5f..5e0a3c38f 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -48,10 +48,23 @@ struct CreateWorldDestMainMenu : public CreateWorldDest {} void accepted(std::wstring name, std::string gameid) { - if(!string_allowed_blacklist(wide_to_narrow(name), WORLDNAME_BLACKLISTED_CHARS)) + std::string name_narrow = wide_to_narrow(name); + if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS)) + { m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters")); - else - m_menu->createNewWorld(name, gameid); + return; + } + std::vector worlds = getAvailableWorlds(); + for(std::vector::iterator i = worlds.begin(); + i != worlds.end(); i++) + { + if((*i).name == name_narrow) + { + m_menu->displayMessageMenu(wgettext("Cannot create world: A world by this name already exists")); + return; + } + } + m_menu->createNewWorld(name, gameid); } GUIMainMenu *m_menu; }; @@ -936,4 +949,4 @@ int GUIMainMenu::getTab() void GUIMainMenu::displayMessageMenu(std::wstring msg) { (new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop(); -} \ No newline at end of file +} From 9696ed31a41b5e3ca85bad4a29c190a0d25c7752 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 5 Sep 2012 01:31:06 +0300 Subject: [PATCH 12/12] Version 0.4.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9aa0f3a9..4e7c1f761 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 3-rc0) +set(VERSION_PATCH 3) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) endif()