From 0b1ab41a954e2a2b035888e200d775372a3b394e Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 19 Aug 2012 10:55:04 +0300 Subject: [PATCH 01/28] Fix github issue #213: Dropping unknown items crashes the game The items will now just disappear when dropped. --- builtin/item.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/builtin/item.lua b/builtin/item.lua index 4ed20e4f1..4e3c74444 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -227,10 +227,12 @@ function minetest.item_drop(itemstack, dropper, pos) local v = dropper:get_look_dir() local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z} local obj = minetest.env:add_item(p, itemstack) - v.x = v.x*2 - v.y = v.y*2 + 1 - v.z = v.z*2 - obj:setvelocity(v) + if obj then + v.x = v.x*2 + v.y = v.y*2 + 1 + v.z = v.z*2 + obj:setvelocity(v) + end else minetest.env:add_item(pos, itemstack) end From 1d7408a7b866a8948e7913a7e8bac7d608f90fbb Mon Sep 17 00:00:00 2001 From: Matthew I Date: Sat, 28 Jul 2012 21:41:32 -0400 Subject: [PATCH 02/28] Fix black display inside opaque water Opaque water's solidness was being set to 2, like a normal node. When you swim, it is treated like a solid block, and the display goes black. Setting it to 1 like transparent water allows you to see. It looks somewhat awkward when you swim, look up, and see an opaque wall of water (the surface), but there isn't much that can be done about it. If you made the water transparent so it looked good, it would defeat the purpose :) . --- src/nodedef.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nodedef.cpp b/src/nodedef.cpp index e38061e30..d644dc229 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -556,8 +556,6 @@ public: f->solidness = 0; } else { f->solidness = 1; - if(f->alpha == 255) - f->solidness = 2; f->backface_culling = false; } break; From 8887105ffda38d42eb7dd3958f574317d7ce2ca6 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Fri, 31 Aug 2012 17:58:55 +0300 Subject: [PATCH 03/28] Properly initialize baseimg for texture modifier [combine:WxH:X,Y=filename:X,Y=filename2 --- src/tile.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tile.cpp b/src/tile.cpp index 6dbe4c63a..a9ded3891 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1344,7 +1344,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, u32 h0 = stoi(sf.next(":")); infostream<<"combined w="< Date: Fri, 31 Aug 2012 18:06:47 +0300 Subject: [PATCH 04/28] Use proper alpha blit in some more texture modifiers --- src/tile.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tile.cpp b/src/tile.cpp index a9ded3891..f7f1779ca 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1200,10 +1200,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, // Position to copy the blitted from in the blitted image core::position2d pos_from(0,0); // Blit - image->copyToWithAlpha(baseimg, pos_to, + /*image->copyToWithAlpha(baseimg, pos_to, core::rect(pos_from, dim), video::SColor(255,255,255,255), - NULL); + NULL);*/ + blit_with_alpha(image, baseimg, pos_from, pos_to, dim); // Drop image image->drop(); } @@ -1368,10 +1369,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, driver->createImage(video::ECF_A8R8G8B8, dim); img->copyTo(img2); img->drop(); - img2->copyToWithAlpha(baseimg, pos_base, + /*img2->copyToWithAlpha(baseimg, pos_base, core::rect(v2s32(0,0), dim), video::SColor(255,255,255,255), - NULL); + NULL);*/ + blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim); img2->drop(); } else From f60227f112a36d943c83d4d1ac0b24754fbd537e Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 09:23:39 +0300 Subject: [PATCH 05/28] Don't crash in "unexpected multibyte character"; just print it in log. Github #222 --- src/keycode.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/keycode.cpp b/src/keycode.cpp index df3ebc9e3..5a8df0db4 100644 --- a/src/keycode.cpp +++ b/src/keycode.cpp @@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_settings #include "exceptions.h" #include "settings.h" +#include "log.h" +#include "hex.h" class UnknownKeycode : public BaseException { @@ -295,7 +297,10 @@ KeyPress::KeyPress(const irr::SEvent::SKeyInput &in) } else { m_name.resize(MB_CUR_MAX+1, '\0'); int written = wctomb(&m_name[0], Char); - assert (written >= 0 && "unexpected multibyte character"); + if(written >= 0){ + std::string hexstr = hex_encode((const char*)&Char, sizeof(Char)); + errorstream<<"KeyPress: Unexpected multibyte character "< Date: Sun, 19 Aug 2012 13:15:42 +0200 Subject: [PATCH 06/28] Make shift the default descent control on ladders and when flying "aux1_descends" setting switches to the old descend control. --- minetest.conf.example | 8 ++++- src/defaultsettings.cpp | 1 + src/localplayer.cpp | 75 +++++++++++++++++++++++++++++++---------- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index adeb3c8f6..3f292c01e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -15,7 +15,7 @@ # https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp # # A vim command to convert most of defaultsettings.cpp to conf file format: -# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g +# :'<,'>s/\tsettings->setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g # Note: Some of the settings are implemented in Lua # @@ -49,8 +49,14 @@ #keymap_freemove = KEY_KEY_K #keymap_fastmove = KEY_KEY_J #keymap_screenshot = KEY_F12 +# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending +#aux1_descends = false # Some (temporary) keys for debugging #keymap_print_debug_stacks = KEY_KEY_P +#keymap_quicktune_prev = KEY_HOME +#keymap_quicktune_next = KEY_END +#keymap_quicktune_dec = KEY_NEXT +#keymap_quicktune_inc = KEY_PRIOR # Minimum FPS # The amount of rendered stuff is dynamically set according to this diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6b85feb60..3b37c18dc 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -52,6 +52,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_toggle_profiler", "KEY_F6"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); + settings->setDefault("aux1_descends", "false"); // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("keymap_quicktune_prev", "KEY_HOME"); diff --git a/src/localplayer.cpp b/src/localplayer.cpp index c1bfa3422..ee4fcd4e5 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -359,31 +359,70 @@ void LocalPlayer::applyControl(float dtime) if(free_move && fast_move) superspeed = true; - // Auxiliary button 1 (E) - if(control.aux1) + // Old descend control + if(g_settings->getBool("aux1_descends")) { - if(free_move) + // Auxiliary button 1 (E) + if(control.aux1) { - // In free movement mode, aux1 descends - v3f speed = getSpeed(); - if(fast_move) - speed.Y = -20*BS; + if(free_move) + { + // In free movement mode, aux1 descends + v3f speed = getSpeed(); + if(fast_move) + speed.Y = -20*BS; + else + speed.Y = -walkspeed_max; + setSpeed(speed); + } + else if(is_climbing) + { + v3f speed = getSpeed(); + speed.Y = -3*BS; + setSpeed(speed); + } else - speed.Y = -walkspeed_max; - setSpeed(speed); + { + // If not free movement but fast is allowed, aux1 is + // "Turbo button" + if(fast_move) + superspeed = true; + } } - else if(is_climbing) + } + // New minecraft-like descend control + else + { + // Auxiliary button 1 (E) + if(control.aux1) { - v3f speed = getSpeed(); - speed.Y = -3*BS; - setSpeed(speed); + if(!free_move && !is_climbing) + { + // If not free movement but fast is allowed, aux1 is + // "Turbo button" + if(fast_move) + superspeed = true; + } } - else + + if(control.sneak) { - // If not free movement but fast is allowed, aux1 is - // "Turbo button" - if(fast_move) - superspeed = true; + if(free_move) + { + // In free movement mode, sneak descends + v3f speed = getSpeed(); + if(fast_move) + speed.Y = -20*BS; + else + speed.Y = -walkspeed_max; + setSpeed(speed); + } + else if(is_climbing) + { + v3f speed = getSpeed(); + speed.Y = -3*BS; + setSpeed(speed); + } } } From 6a16075912d016926ee0361fb85f9979c119be52 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Mon, 20 Aug 2012 01:29:56 +0400 Subject: [PATCH 07/28] Add InventoryList width property & allow custom crafting grids. --- doc/lua_api.txt | 2 ++ src/inventory.cpp | 21 +++++++++++++++++++++ src/inventory.h | 4 +++- src/inventorymanager.cpp | 10 ++++------ src/player.cpp | 3 ++- src/scriptapi.cpp | 33 +++++++++++++++++++++++++++++++++ src/test.cpp | 4 ++++ 7 files changed, 69 insertions(+), 8 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8a6ea5dea..88c594eb2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1118,6 +1118,8 @@ methods: - is_empty(listname): return true if list is empty - get_size(listname): get size of a list - set_size(listname, size): set size of a list +- get_width(listname): get width of a list +- set_width(listname, width): set width of list; currently used for crafting - get_stack(listname, i): get a copy of stack index i in list - set_stack(listname, i, stack): copy stack to index i in list - get_list(listname): return full list diff --git a/src/inventory.cpp b/src/inventory.cpp index 0a0a29cd5..fda11e40f 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -431,6 +431,7 @@ InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemde { m_name = name; m_size = size; + m_width = 0; m_itemdef = itemdef; clearItems(); //m_dirty = false; @@ -459,6 +460,11 @@ void InventoryList::setSize(u32 newsize) m_size = newsize; } +void InventoryList::setWidth(u32 newwidth) +{ + m_width = newwidth; +} + void InventoryList::setName(const std::string &name) { m_name = name; @@ -468,6 +474,8 @@ void InventoryList::serialize(std::ostream &os) const { //os.imbue(std::locale("C")); + os<<"Width "<> m_width; + if (iss.fail()) + throw SerializationError("incorrect width property"); + } else if(name == "Item") { if(item_i > getSize() - 1) @@ -543,6 +558,7 @@ InventoryList & InventoryList::operator = (const InventoryList &other) { m_items = other.m_items; m_size = other.m_size; + m_width = other.m_width; m_name = other.m_name; m_itemdef = other.m_itemdef; //setDirty(true); @@ -560,6 +576,11 @@ u32 InventoryList::getSize() const return m_items.size(); } +u32 InventoryList::getWidth() const +{ + return m_width; +} + u32 InventoryList::getUsedSlots() const { u32 num = 0; diff --git a/src/inventory.h b/src/inventory.h index a3c598256..5f90183d2 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -176,6 +176,7 @@ public: ~InventoryList(); void clearItems(); void setSize(u32 newsize); + void setWidth(u32 newWidth); void setName(const std::string &name); void serialize(std::ostream &os) const; void deSerialize(std::istream &is); @@ -185,6 +186,7 @@ public: const std::string &getName() const; u32 getSize() const; + u32 getWidth() const; // Count used slots u32 getUsedSlots() const; u32 getFreeSlots() const; @@ -240,7 +242,7 @@ public: private: std::vector m_items; - u32 m_size; + u32 m_size, m_width; std::string m_name; IItemDefManager *m_itemdef; }; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index ec4d47def..e2e537838 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -769,18 +769,16 @@ bool getCraftingResult(Inventory *inv, ItemStack& result, result.clear(); - // TODO: Allow different sizes of crafting grids - // Get the InventoryList in which we will operate InventoryList *clist = inv->getList("craft"); - if(!clist || clist->getSize() != 9) + if(!clist) return false; // Mangle crafting grid to an another format CraftInput ci; ci.method = CRAFT_METHOD_NORMAL; - ci.width = 3; - for(u16 i=0; i<9; i++) + ci.width = clist->getWidth() ? clist->getWidth() : 3; + for(u16 i=0; igetSize(); i++) ci.items.push_back(clist->getItem(i)); // Find out what is crafted and add it to result item slot @@ -793,7 +791,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result, if(found && decrementInput) { // CraftInput has been changed, apply changes in clist - for(u16 i=0; i<9; i++) + for(u16 i=0; igetSize(); i++) { clist->changeItem(i, ci.items[i]); } diff --git a/src/player.cpp b/src/player.cpp index 2e084b415..36f12c77b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -45,7 +45,8 @@ Player::Player(IGameDef *gamedef): updateName(""); inventory.clear(); inventory.addList("main", PLAYER_INVENTORY_SIZE); - inventory.addList("craft", 9); + InventoryList *craft = inventory.addList("craft", 9); + craft->setWidth(3); inventory.addList("craftpreview", 1); inventory.addList("craftresult", 1); diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 95e2b5d01..81e96aec6 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -1847,6 +1847,20 @@ private: return 1; } + // get_width(self, listname) + static int l_get_width(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getWidth()); + } else { + lua_pushinteger(L, 0); + } + return 1; + } + // set_size(self, listname, size) static int l_set_size(lua_State *L) { @@ -1869,6 +1883,23 @@ private: return 0; } + // set_width(self, listname, size) + static int l_set_width(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newwidth = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + InventoryList *list = inv->getList(listname); + if(list){ + list->setWidth(newwidth); + } else { + return 0; + } + reportInventoryChange(L, ref); + return 0; + } + // get_stack(self, listname, i) -> itemstack static int l_get_stack(lua_State *L) { @@ -2062,6 +2093,8 @@ const luaL_reg InvRef::methods[] = { method(InvRef, is_empty), method(InvRef, get_size), method(InvRef, set_size), + method(InvRef, get_width), + method(InvRef, set_width), method(InvRef, get_stack), method(InvRef, set_stack), method(InvRef, get_list), diff --git a/src/test.cpp b/src/test.cpp index fe5a4f232..f81f2910c 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -698,6 +698,7 @@ struct TestInventory: public TestBase { std::string serialized_inventory = "List 0 32\n" + "Width 3\n" "Empty\n" "Empty\n" "Empty\n" @@ -735,6 +736,7 @@ struct TestInventory: public TestBase std::string serialized_inventory_2 = "List main 32\n" + "Width 5\n" "Empty\n" "Empty\n" "Empty\n" @@ -778,6 +780,8 @@ struct TestInventory: public TestBase inv.getList("0")->setName("main"); UASSERT(!inv.getList("0")); UASSERT(inv.getList("main")); + UASSERT(inv.getList("main")->getWidth() == 3); + inv.getList("main")->setWidth(5); std::ostringstream inv_os(std::ios::binary); inv.serialize(inv_os); UASSERT(inv_os.str() == serialized_inventory_2); From b17d3e7ad7e59a10df50277c15174529f20d0bed Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 12:07:27 +0300 Subject: [PATCH 08/28] Add disable_jump and fall_damage_add_percent node groups --- doc/lua_api.txt | 2 ++ src/environment.cpp | 5 +++++ src/localplayer.cpp | 14 ++++++++++++-- src/localplayer.h | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 88c594eb2..008c9d40c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -453,6 +453,8 @@ Special groups - 2: node is removed without tool wear after 0.5 seconds or so (rail, sign) - 3: node is removed without tool wear immediately (torch) +- disable_jump: Player (and possibly other things) cannot jump from node +- fall_damage_add_percent: damage speed = speed * (1 + value/100) Known damage and digging time defining groups ---------------------------------------------- diff --git a/src/environment.cpp b/src/environment.cpp index b88f55dea..292679542 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2040,6 +2040,11 @@ void ClientEnvironment::step(float dtime) //f32 tolerance = BS*12; // 3 without damage f32 tolerance = BS*14; // 5 without damage f32 factor = 1; + const ContentFeatures &f = m_gamedef->ndef()-> + get(m_map->getNodeNoEx(lplayer->getStandingNodePos())); + // Determine fall damage multiplier + int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); + info.speed *= (1.0 + (float)addp/100.0); if(info.speed > tolerance) { f32 damage_f = (info.speed - tolerance)/BS*factor; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index ee4fcd4e5..16111629e 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -38,7 +38,8 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef): m_sneak_node_exists(false), m_old_node_below(32767,32767,32767), m_old_node_below_type("air"), - m_need_to_get_new_sneak_node(true) + m_need_to_get_new_sneak_node(true), + m_can_jump(false) { // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points @@ -314,6 +315,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, */ m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS); m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name; + + /* + Check properties of the node on which the player is standing + */ + const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos())); + // Determine if jumping is possible + m_can_jump = touching_ground; + if(itemgroup_get(f.groups, "disable_jump")) + m_can_jump = false; } void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) @@ -459,7 +469,7 @@ void LocalPlayer::applyControl(float dtime) speed.Y = walkspeed_max; setSpeed(speed); } - else if(touching_ground) + else if(m_can_jump) { /* NOTE: The d value in move() affects jump height by diff --git a/src/localplayer.h b/src/localplayer.h index 9a9767d38..fb57e6538 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -101,6 +101,7 @@ private: std::string m_old_node_below_type; // Whether recalculation of the sneak node is needed bool m_need_to_get_new_sneak_node; + bool m_can_jump; }; #endif From 3b43c69df4ce7d68aebb4bd02aaa4aca18b9afad Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 12:58:37 +0300 Subject: [PATCH 09/28] Add bouncy node group --- doc/lua_api.txt | 1 + src/collision.cpp | 55 ++++++++++++++++++++++++++++++++++++++++----- src/collision.h | 34 +++++++++++++++++++--------- src/environment.cpp | 29 ++++++++++++------------ src/localplayer.cpp | 24 +++++++++++++------- 5 files changed, 104 insertions(+), 39 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 008c9d40c..d3e254a2d 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -455,6 +455,7 @@ Special groups - 3: node is removed without tool wear immediately (torch) - disable_jump: Player (and possibly other things) cannot jump from node - fall_damage_add_percent: damage speed = speed * (1 + value/100) +- bouncy: value is bounce speed in percent Known damage and digging time defining groups ---------------------------------------------- diff --git a/src/collision.cpp b/src/collision.cpp index 09a7df7c2..143b559fa 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, std::vector cboxes; std::vector is_unloaded; std::vector is_step_up; + std::vector bouncy_values; + std::vector node_positions; { //TimeTaker tt2("collisionMoveSimple collect boxes"); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); @@ -228,11 +230,14 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, for(s16 y = min_y; y <= max_y; y++) for(s16 z = min_z; z <= max_z; z++) { + v3s16 p(x,y,z); try{ // Object collides into walkable nodes - MapNode n = map->getNode(v3s16(x,y,z)); - if(gamedef->getNodeDefManager()->get(n).walkable == false) + MapNode n = map->getNode(p); + const ContentFeatures &f = gamedef->getNodeDefManager()->get(n); + if(f.walkable == false) continue; + int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); std::vector nodeboxes = n.getNodeBoxes(gamedef->ndef()); for(std::vector::iterator @@ -245,21 +250,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, cboxes.push_back(box); is_unloaded.push_back(false); is_step_up.push_back(false); + bouncy_values.push_back(n_bouncy_value); + node_positions.push_back(p); } } catch(InvalidPositionException &e) { // Collide with unloaded nodes - aabb3f box = getNodeBox(v3s16(x,y,z), BS); + aabb3f box = getNodeBox(p, BS); cboxes.push_back(box); is_unloaded.push_back(true); is_step_up.push_back(false); + bouncy_values.push_back(0); + node_positions.push_back(p); } } } // tt2 assert(cboxes.size() == is_unloaded.size()); assert(cboxes.size() == is_step_up.size()); + assert(cboxes.size() == bouncy_values.size()); + assert(cboxes.size() == node_positions.size()); /* Collision detection @@ -342,6 +353,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, cbox.MaxEdge.Y - movingbox.MinEdge.Y, d)); + // Get bounce multiplier + bool bouncy = (bouncy_values[nearest_boxindex] >= 1); + float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0; + // Move to the point of collision and reduce dtime by nearest_dtime if(nearest_dtime < 0) { @@ -361,30 +376,58 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, pos_f += speed_f * nearest_dtime; dtime -= nearest_dtime; } + + bool is_collision = true; + if(is_unloaded[nearest_boxindex]) + is_collision = false; + + CollisionInfo info; + info.type = COLLISION_NODE; + info.node_p = node_positions[nearest_boxindex]; + info.bouncy = bouncy; + info.old_speed = speed_f; // Set the speed component that caused the collision to zero if(step_up) { // Special case: Handle stairs is_step_up[nearest_boxindex] = true; + is_collision = false; } else if(nearest_collided == 0) // X { - speed_f.X = 0; + if(fabs(speed_f.X) > BS*3) + speed_f.X *= bounce; + else + speed_f.X = 0; result.collides = true; result.collides_xz = true; } else if(nearest_collided == 1) // Y { - speed_f.Y = 0; + if(fabs(speed_f.Y) > BS*3) + speed_f.Y *= bounce; + else + speed_f.Y = 0; result.collides = true; } else if(nearest_collided == 2) // Z { - speed_f.Z = 0; + if(fabs(speed_f.Z) > BS*3) + speed_f.Z *= bounce; + else + speed_f.Z = 0; result.collides = true; result.collides_xz = true; } + + info.new_speed = speed_f; + if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS) + is_collision = false; + + if(is_collision){ + result.collisions.push_back(info); + } } } diff --git a/src/collision.h b/src/collision.h index 243c4b294..52a7bbb7d 100644 --- a/src/collision.h +++ b/src/collision.h @@ -26,12 +26,35 @@ with this program; if not, write to the Free Software Foundation, Inc., class Map; class IGameDef; +enum CollisionType +{ + COLLISION_NODE +}; + +struct CollisionInfo +{ + enum CollisionType type; + v3s16 node_p; // COLLISION_NODE + bool bouncy; + v3f old_speed; + v3f new_speed; + + CollisionInfo(): + type(COLLISION_NODE), + node_p(-32768,-32768,-32768), + bouncy(false), + old_speed(0,0,0), + new_speed(0,0,0) + {} +}; + struct collisionMoveResult { bool touching_ground; bool collides; bool collides_xz; bool standing_on_unloaded; + std::vector collisions; collisionMoveResult(): touching_ground(false), @@ -72,16 +95,5 @@ bool wouldCollideWithCeiling( f32 y_increase, f32 d); -enum CollisionType -{ - COLLISION_FALL -}; - -struct CollisionInfo -{ - CollisionType t; - f32 speed; -}; - #endif diff --git a/src/environment.cpp b/src/environment.cpp index 292679542..24943ad5f 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2033,25 +2033,26 @@ void ClientEnvironment::step(float dtime) i = player_collisions.begin(); i != player_collisions.end(); i++) { + f32 pre_factor = 1; // 1 hp per node/s + f32 tolerance = BS*14; // 5 without damage + f32 post_factor = 1; // 1 hp per node/s CollisionInfo &info = *i; - if(info.t == COLLISION_FALL) + if(info.type == COLLISION_NODE) { - //f32 tolerance = BS*10; // 2 without damage - //f32 tolerance = BS*12; // 3 without damage - f32 tolerance = BS*14; // 5 without damage - f32 factor = 1; const ContentFeatures &f = m_gamedef->ndef()-> - get(m_map->getNodeNoEx(lplayer->getStandingNodePos())); + get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); - info.speed *= (1.0 + (float)addp/100.0); - if(info.speed > tolerance) - { - f32 damage_f = (info.speed - tolerance)/BS*factor; - u16 damage = (u16)(damage_f+0.5); - if(damage != 0) - damageLocalPlayer(damage, true); - } + pre_factor = 1.0 + (float)addp/100.0; + } + float speed = (info.new_speed - info.old_speed).getLength(); + speed *= pre_factor; + if(speed > tolerance) + { + f32 damage_f = (speed - tolerance)/BS * post_factor; + u16 damage = (u16)(damage_f+0.5); + if(damage != 0) + damageLocalPlayer(damage, true); } } diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 16111629e..15b6fd15d 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -189,7 +189,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, bool touching_ground_was = touching_ground; touching_ground = result.touching_ground; - bool standing_on_unloaded = result.standing_on_unloaded; + //bool standing_on_unloaded = result.standing_on_unloaded; /* Check the nodes under the player to see from which node the @@ -282,18 +282,25 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, /* Report collisions */ + bool bouncy_jump = false; if(collision_info) { - // Report fall collision - if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded) - { - CollisionInfo info; - info.t = COLLISION_FALL; - info.speed = m_speed.Y - old_speed.Y; + for(size_t i=0; ipush_back(info); + if(info.new_speed.Y - info.old_speed.Y > 0.1*BS && + info.bouncy) + bouncy_jump = true; } } + if(bouncy_jump && control.jump){ + m_speed.Y += 6.5*BS; + touching_ground = false; + MtEvent *e = new SimpleTriggerEvent("PlayerJump"); + m_gamedef->event()->put(e); + } + if(!touching_ground_was && touching_ground){ MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_gamedef->event()->put(e); @@ -479,8 +486,9 @@ void LocalPlayer::applyControl(float dtime) v3f speed = getSpeed(); if(speed.Y >= -0.5*BS) { - speed.Y = 6.5*BS; + speed.Y += 6.5*BS; setSpeed(speed); + m_can_jump = false; MtEvent *e = new SimpleTriggerEvent("PlayerJump"); m_gamedef->event()->put(e); From c129b3852b97b7bd7ea7fe904af68243ad44a333 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 13:21:52 +0300 Subject: [PATCH 10/28] Fix the bouncy node related stuff a bit --- src/environment.cpp | 13 ++++++++++--- src/localplayer.cpp | 3 +-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 24943ad5f..a49a5e384 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2033,10 +2033,18 @@ void ClientEnvironment::step(float dtime) i = player_collisions.begin(); i != player_collisions.end(); i++) { + CollisionInfo &info = *i; + v3f speed_diff = info.new_speed - info.old_speed;; + // Handle only fall damage + // (because otherwise walking against something in fast_move kills you) + if(speed_diff.Y < 0 || info.old_speed.Y >= 0) + continue; + // Get rid of other components + speed_diff.X = 0; + speed_diff.Z = 0; f32 pre_factor = 1; // 1 hp per node/s f32 tolerance = BS*14; // 5 without damage f32 post_factor = 1; // 1 hp per node/s - CollisionInfo &info = *i; if(info.type == COLLISION_NODE) { const ContentFeatures &f = m_gamedef->ndef()-> @@ -2045,8 +2053,7 @@ void ClientEnvironment::step(float dtime) int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); pre_factor = 1.0 + (float)addp/100.0; } - float speed = (info.new_speed - info.old_speed).getLength(); - speed *= pre_factor; + float speed = pre_factor * speed_diff.getLength(); if(speed > tolerance) { f32 damage_f = (speed - tolerance)/BS * post_factor; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 15b6fd15d..46a10c90b 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -486,9 +486,8 @@ void LocalPlayer::applyControl(float dtime) v3f speed = getSpeed(); if(speed.Y >= -0.5*BS) { - speed.Y += 6.5*BS; + speed.Y = 6.5*BS; setSpeed(speed); - m_can_jump = false; MtEvent *e = new SimpleTriggerEvent("PlayerJump"); m_gamedef->event()->put(e); From 0f7728f84decb836bd7d025456315c297fe30435 Mon Sep 17 00:00:00 2001 From: Sergey Gilfanov Date: Thu, 26 Jul 2012 22:52:05 +0400 Subject: [PATCH 11/28] Smooth transition to the desert biome. --- src/mapgen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index af53d0091..b37d03501 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1297,7 +1297,9 @@ BiomeType get_biome(u64 seed, v2s16 p2d) double d = noise2d_perlin( 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250, seed+9130, 3, 0.50); - if(d > 0.35) + if(d > 0.45) + return BT_DESERT; + if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 ) return BT_DESERT; return BT_NORMAL; }; From c0bd96d9b314e78a7aeb74b1bff70e1023b2f9e2 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 13:41:15 +0300 Subject: [PATCH 12/28] Attempt to fix flying gravel and dirt --- src/mapgen.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index b37d03501..67e92f449 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1761,8 +1761,9 @@ void make_block(BlockMakeData *data) vmanip.m_data[i] = airnode; } } else { - // Don't replace air or water or lava - if(vmanip.m_data[i].getContent() == CONTENT_AIR || + // Don't replace air or water or lava or ignore + if(vmanip.m_data[i].getContent() == CONTENT_IGNORE || + vmanip.m_data[i].getContent() == CONTENT_AIR || vmanip.m_data[i].getContent() == c_water_source || vmanip.m_data[i].getContent() == c_lava_source) continue; From 3e7957512bdb86acb3836c6c1df96e38e05efa3c Mon Sep 17 00:00:00 2001 From: Thomas Lauro Date: Thu, 26 Jul 2012 09:49:00 +0200 Subject: [PATCH 13/28] A bunch of GUIKeyChangeMenu fixes - Display message when associating a key already in use - Fix issue for not beeing able to assign space to an action - Cleaning up - Make it build (by celeron55) --- src/guiKeyChangeMenu.cpp | 657 +++++++++------------------------------ src/guiKeyChangeMenu.h | 47 +-- 2 files changed, 154 insertions(+), 550 deletions(-) diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index db29fb376..a7dbc8c6f 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -30,6 +30,9 @@ #include #include #include "settings.h" +#include + +#define KMaxButtonPerColumns 12 enum { @@ -54,11 +57,14 @@ enum }; GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : - GUIModalMenu(env, parent, id, menumgr) + gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : +GUIModalMenu(env, parent, id, menumgr) { activeKey = -1; + this->key_used_text = NULL; init_keys(); + for(size_t i=0; ikey_used.push_back(key_settings.at(i)->key); } GUIKeyChangeMenu::~GUIKeyChangeMenu() @@ -71,12 +77,12 @@ void GUIKeyChangeMenu::removeChildren() const core::list &children = getChildren(); core::list children_copy; for (core::list::ConstIterator i = children.begin(); i - != children.end(); i++) + != children.end(); i++) { children_copy.push_back(*i); } for (core::list::Iterator i = children_copy.begin(); i - != children_copy.end(); i++) + != children_copy.end(); i++) { (*i)->remove(); } @@ -84,20 +90,12 @@ void GUIKeyChangeMenu::removeChildren() void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) { - /* - Remove stuff - */ removeChildren(); - - /* - Calculate new sizes and positions - */ - v2s32 size(620, 430); - + core::rect < s32 > rect(screensize.X / 2 - size.X / 2, - screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2, - screensize.Y / 2 + size.Y / 2); + screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2, + screensize.Y / 2 + size.Y / 2); DesiredRect = rect; recalculateAbsolutePosition(false); @@ -109,271 +107,48 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) rect += topleft + v2s32(25, 3); //gui::IGUIStaticText *t = Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"), - rect, false, true, this, -1); + rect, false, true, this, -1); //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); } + + // Build buttons + v2s32 offset(25, 60); - // buttons + for(size_t i = 0; i < key_settings.size(); i++) { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Forward"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); + key_setting *k = key_settings.at(i); + { + core::rect < s32 > rect(0, 0, 100, 20); + rect += topleft + v2s32(offset.X, offset.Y); + Environment->addStaticText(k->button_name, rect, false, true, this, -1); + } + + { + core::rect < s32 > rect(0, 0, 100, 30); + rect += topleft + v2s32(offset.X + 105, offset.Y - 5); + k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name())); + } + if(i + 1 == KMaxButtonPerColumns) + offset = v2s32(250, 60); + else + offset += v2s32(0, 25); } - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->forward = Environment->addButton(rect, this, - GUI_ID_KEY_FORWARD_BUTTON, - wgettext(key_forward.name())); - } - - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Backward"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->backward = Environment->addButton(rect, this, - GUI_ID_KEY_BACKWARD_BUTTON, - wgettext(key_backward.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Left"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->left = Environment->addButton(rect, this, GUI_ID_KEY_LEFT_BUTTON, - wgettext(key_left.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Right"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->right = Environment->addButton(rect, this, - GUI_ID_KEY_RIGHT_BUTTON, - wgettext(key_right.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Use"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->use = Environment->addButton(rect, this, GUI_ID_KEY_USE_BUTTON, - wgettext(key_use.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Sneak"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->sneak = Environment->addButton(rect, this, - GUI_ID_KEY_SNEAK_BUTTON, - wgettext(key_sneak.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Jump"), rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->jump = Environment->addButton(rect, this, GUI_ID_KEY_JUMP_BUTTON, - wgettext(key_jump.name())); - } - - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Drop"), rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->dropbtn = Environment->addButton(rect, this, GUI_ID_KEY_DROP_BUTTON, - wgettext(key_drop.name())); - } - - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Inventory"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->inventory = Environment->addButton(rect, this, - GUI_ID_KEY_INVENTORY_BUTTON, - wgettext(key_inventory.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Chat"), rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->chat = Environment->addButton(rect, this, GUI_ID_KEY_CHAT_BUTTON, - wgettext(key_chat.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Command"), rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->cmd = Environment->addButton(rect, this, GUI_ID_KEY_CMD_BUTTON, - wgettext(key_cmd.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Console"), rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->console = Environment->addButton(rect, this, GUI_ID_KEY_CONSOLE_BUTTON, - wgettext(key_console.name())); - } - - //next col - offset = v2s32(250, 60); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Toggle fly"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->fly = Environment->addButton(rect, this, GUI_ID_KEY_FLY_BUTTON, - wgettext(key_fly.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Toggle fast"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->fast = Environment->addButton(rect, this, GUI_ID_KEY_FAST_BUTTON, - wgettext(key_fast.name())); - } - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Range select"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->range = Environment->addButton(rect, this, - GUI_ID_KEY_RANGE_BUTTON, - wgettext(key_range.name())); - } - - offset += v2s32(0, 25); - { - core::rect < s32 > rect(0, 0, 100, 20); - rect += topleft + v2s32(offset.X, offset.Y); - Environment->addStaticText(wgettext("Print stacks"), - rect, false, true, this, -1); - //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - } - - { - core::rect < s32 > rect(0, 0, 100, 30); - rect += topleft + v2s32(offset.X + 105, offset.Y - 5); - this->dump = Environment->addButton(rect, this, GUI_ID_KEY_DUMP_BUTTON, - wgettext(key_dump.name())); - } { core::rect < s32 > rect(0, 0, 100, 30); rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40); Environment->addButton(rect, this, GUI_ID_BACK_BUTTON, - wgettext("Save")); + wgettext("Save")); } { core::rect < s32 > rect(0, 0, 100, 30); rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40); Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON, - wgettext("Cancel")); + wgettext("Cancel")); } changeCtype("C"); + } void GUIKeyChangeMenu::drawMenu() @@ -396,102 +171,27 @@ void GUIKeyChangeMenu::drawMenu() bool GUIKeyChangeMenu::acceptInput() { - g_settings->set("keymap_forward", key_forward.sym()); - g_settings->set("keymap_backward", key_backward.sym()); - g_settings->set("keymap_left", key_left.sym()); - g_settings->set("keymap_right", key_right.sym()); - g_settings->set("keymap_jump", key_jump.sym()); - g_settings->set("keymap_sneak", key_sneak.sym()); - g_settings->set("keymap_drop", key_drop.sym()); - g_settings->set("keymap_inventory", key_inventory.sym()); - g_settings->set("keymap_chat", key_chat.sym()); - g_settings->set("keymap_cmd", key_cmd.sym()); - g_settings->set("keymap_console", key_console.sym()); - g_settings->set("keymap_rangeselect", key_range.sym()); - g_settings->set("keymap_freemove", key_fly.sym()); - g_settings->set("keymap_fastmove", key_fast.sym()); - g_settings->set("keymap_special1", key_use.sym()); - g_settings->set("keymap_print_debug_stacks", key_dump.sym()); + for(size_t i = 0; i < key_settings.size(); i++) + { + key_setting *k = key_settings.at(i); + g_settings->set(k->setting_name, k->key.sym()); + } clearKeyCache(); return true; } -void GUIKeyChangeMenu::init_keys() -{ - key_forward = getKeySetting("keymap_forward"); - key_backward = getKeySetting("keymap_backward"); - key_left = getKeySetting("keymap_left"); - key_right = getKeySetting("keymap_right"); - key_jump = getKeySetting("keymap_jump"); - key_sneak = getKeySetting("keymap_sneak"); - key_drop = getKeySetting("keymap_drop"); - key_inventory = getKeySetting("keymap_inventory"); - key_chat = getKeySetting("keymap_chat"); - key_cmd = getKeySetting("keymap_cmd"); - key_console = getKeySetting("keymap_console"); - key_range = getKeySetting("keymap_rangeselect"); - key_fly = getKeySetting("keymap_freemove"); - key_fast = getKeySetting("keymap_fastmove"); - key_use = getKeySetting("keymap_special1"); - key_dump = getKeySetting("keymap_print_debug_stacks"); -} bool GUIKeyChangeMenu::resetMenu() { if (activeKey >= 0) { - switch (activeKey) + for(size_t i = 0; i < key_settings.size(); i++) { - case GUI_ID_KEY_FORWARD_BUTTON: - this->forward->setText( - wgettext(key_forward.name())); - break; - case GUI_ID_KEY_BACKWARD_BUTTON: - this->backward->setText( - wgettext(key_backward.name())); - break; - case GUI_ID_KEY_LEFT_BUTTON: - this->left->setText(wgettext(key_left.name())); - break; - case GUI_ID_KEY_RIGHT_BUTTON: - this->right->setText(wgettext(key_right.name())); - break; - case GUI_ID_KEY_JUMP_BUTTON: - this->jump->setText(wgettext(key_jump.name())); - break; - case GUI_ID_KEY_SNEAK_BUTTON: - this->sneak->setText(wgettext(key_sneak.name())); - break; - case GUI_ID_KEY_DROP_BUTTON: - this->dropbtn->setText(wgettext(key_drop.name())); - break; - case GUI_ID_KEY_INVENTORY_BUTTON: - this->inventory->setText( - wgettext(key_inventory.name())); - break; - case GUI_ID_KEY_CHAT_BUTTON: - this->chat->setText(wgettext(key_chat.name())); - break; - case GUI_ID_KEY_CMD_BUTTON: - this->cmd->setText(wgettext(key_cmd.name())); - break; - case GUI_ID_KEY_CONSOLE_BUTTON: - this->console->setText(wgettext(key_console.name())); - break; - case GUI_ID_KEY_RANGE_BUTTON: - this->range->setText(wgettext(key_range.name())); - break; - case GUI_ID_KEY_FLY_BUTTON: - this->fly->setText(wgettext(key_fly.name())); - break; - case GUI_ID_KEY_FAST_BUTTON: - this->fast->setText(wgettext(key_fast.name())); - break; - case GUI_ID_KEY_USE_BUTTON: - this->use->setText(wgettext(key_use.name())); - break; - case GUI_ID_KEY_DUMP_BUTTON: - this->dump->setText(wgettext(key_dump.name())); - break; + key_setting *k = key_settings.at(i); + if(k->id == activeKey) + { + k->button->setText(wgettext(k->key.name())); + break; + } } activeKey = -1; return false; @@ -501,104 +201,58 @@ bool GUIKeyChangeMenu::resetMenu() bool GUIKeyChangeMenu::OnEvent(const SEvent& event) { if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0 - && event.KeyInput.PressedDown) + && event.KeyInput.PressedDown) { changeCtype(""); KeyPress kp(event.KeyInput); - if (activeKey == GUI_ID_KEY_FORWARD_BUTTON) + // Remove Key already in use message + if(this->key_used_text) { - this->forward->setText(wgettext(kp.name())); - this->key_forward = kp; + this->key_used_text->remove(); + this->key_used_text = NULL; } - else if (activeKey == GUI_ID_KEY_BACKWARD_BUTTON) + // Display Key already in use message + if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end()) { - this->backward->setText(wgettext(kp.name())); - this->key_backward = kp; + core::rect < s32 > rect(0, 0, 600, 40); + rect += v2s32(0, 0) + v2s32(25, 30); + this->key_used_text = Environment->addStaticText(wgettext("Key already in use"), + rect, false, true, this, -1); + //infostream << "Key already in use" << std::endl; } - else if (activeKey == GUI_ID_KEY_LEFT_BUTTON) + + // But go on { - this->left->setText(wgettext(kp.name())); - this->key_left = kp; + key_setting *k=NULL; + for(size_t i = 0; i < key_settings.size(); i++) + { + if(key_settings.at(i)->id == activeKey) + { + k = key_settings.at(i); + break; + } + } + assert(k); + k->key = kp; + k->button->setText(wgettext(k->key.name())); + + this->key_used.push_back(kp); + + changeCtype("C"); + activeKey = -1; + return true; } - else if (activeKey == GUI_ID_KEY_RIGHT_BUTTON) - { - this->right->setText(wgettext(kp.name())); - this->key_right = kp; - } - else if (activeKey == GUI_ID_KEY_JUMP_BUTTON) - { - this->jump->setText(wgettext(kp.name())); - this->key_jump = kp; - } - else if (activeKey == GUI_ID_KEY_SNEAK_BUTTON) - { - this->sneak->setText(wgettext(kp.name())); - this->key_sneak = kp; - } - else if (activeKey == GUI_ID_KEY_DROP_BUTTON) - { - this->dropbtn->setText(wgettext(kp.name())); - this->key_drop = kp; - } - else if (activeKey == GUI_ID_KEY_INVENTORY_BUTTON) - { - this->inventory->setText(wgettext(kp.name())); - this->key_inventory = kp; - } - else if (activeKey == GUI_ID_KEY_CHAT_BUTTON) - { - this->chat->setText(wgettext(kp.name())); - this->key_chat = kp; - } - else if (activeKey == GUI_ID_KEY_CMD_BUTTON) - { - this->cmd->setText(wgettext(kp.name())); - this->key_cmd = kp; - } - else if (activeKey == GUI_ID_KEY_CONSOLE_BUTTON) - { - this->console->setText(wgettext(kp.name())); - this->key_console = kp; - } - else if (activeKey == GUI_ID_KEY_RANGE_BUTTON) - { - this->range->setText(wgettext(kp.name())); - this->key_range = kp; - } - else if (activeKey == GUI_ID_KEY_FLY_BUTTON) - { - this->fly->setText(wgettext(kp.name())); - this->key_fly = kp; - } - else if (activeKey == GUI_ID_KEY_FAST_BUTTON) - { - this->fast->setText(wgettext(kp.name())); - this->key_fast = kp; - } - else if (activeKey == GUI_ID_KEY_USE_BUTTON) - { - this->use->setText(wgettext(kp.name())); - this->key_use = kp; - } - else if (activeKey == GUI_ID_KEY_DUMP_BUTTON) - { - this->dump->setText(wgettext(kp.name())); - this->key_dump = kp; - } - changeCtype("C"); - activeKey = -1; - return true; } if (event.EventType == EET_GUI_EVENT) { if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST - && isVisible()) + && isVisible()) { if (!canTakeFocus(event.GUIEvent.Element)) { dstream << "GUIMainMenu: Not allowing focus change." - << std::endl; + << std::endl; // Returning true disables focus change return true; } @@ -606,106 +260,73 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event) if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) { if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON && - event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON) + event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON) { changeCtype(""); } switch (event.GUIEvent.Caller->getID()) { - case GUI_ID_BACK_BUTTON: //back - acceptInput(); - quitMenu(); - return true; - case GUI_ID_ABORT_BUTTON: //abort - quitMenu(); - return true; - case GUI_ID_KEY_FORWARD_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->forward->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_BACKWARD_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->backward->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_LEFT_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->left->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_RIGHT_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->right->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_USE_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->use->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_FLY_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->fly->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_FAST_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->fast->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_JUMP_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->jump->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_DROP_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->dropbtn->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_CHAT_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->chat->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_CMD_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->cmd->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_CONSOLE_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->console->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_SNEAK_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->sneak->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_INVENTORY_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->inventory->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_DUMP_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->dump->setText(wgettext("press Key")); - break; - case GUI_ID_KEY_RANGE_BUTTON: - resetMenu(); - activeKey = event.GUIEvent.Caller->getID(); - this->range->setText(wgettext("press Key")); - break; + case GUI_ID_BACK_BUTTON: //back + acceptInput(); + quitMenu(); + return true; + case GUI_ID_ABORT_BUTTON: //abort + quitMenu(); + return true; + default: + key_setting *k = NULL; + for(size_t i = 0; i < key_settings.size(); i++) + { + if(key_settings.at(i)->id == event.GUIEvent.Caller->getID()) + { + k = key_settings.at(i); + break; + } + } + assert(k); + + resetMenu(); + activeKey = event.GUIEvent.Caller->getID(); + k->button->setText(wgettext("press key")); + this->key_used.erase(std::remove(this->key_used.begin(), + this->key_used.end(), k->key), this->key_used.end()); + break; } + Environment->setFocus(this); //Buttons changeCtype("C"); - } } return Parent ? Parent->OnEvent(event) : false; } +void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name) +{ + key_setting *k = new key_setting; + k->id = id; + k->button_name = wgettext(button_name.c_str()); + k->setting_name = setting_name; + k->key = getKeySetting(k->setting_name.c_str()); + key_settings.push_back(k); +} + +void GUIKeyChangeMenu::init_keys() +{ + this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward"); + this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward"); + this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left"); + this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right"); + this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1"); + this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump"); + this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak"); + this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop"); + this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory"); + this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat"); + this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd"); + this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console"); + this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove"); + this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove"); + this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect"); + this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks"); +} diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h index 7c71b297d..b81866983 100644 --- a/src/guiKeyChangeMenu.h +++ b/src/guiKeyChangeMenu.h @@ -28,6 +28,16 @@ #include "gettext.h" #include "keycode.h" #include +#include + +typedef struct { + int id; + wchar_t *button_name; + KeyPress key; + std::string setting_name; + gui::IGUIButton *button; +} key_setting; + class GUIKeyChangeMenu: public GUIModalMenu { @@ -54,40 +64,13 @@ private: bool resetMenu(); - gui::IGUIButton *forward; - gui::IGUIButton *backward; - gui::IGUIButton *left; - gui::IGUIButton *right; - gui::IGUIButton *use; - gui::IGUIButton *sneak; - gui::IGUIButton *jump; - gui::IGUIButton *dropbtn; - gui::IGUIButton *inventory; - gui::IGUIButton *fly; - gui::IGUIButton *fast; - gui::IGUIButton *range; - gui::IGUIButton *dump; - gui::IGUIButton *chat; - gui::IGUIButton *cmd; - gui::IGUIButton *console; + void add_key(int id, std::string setting_name, std::string button_name); s32 activeKey; - KeyPress key_forward; - KeyPress key_backward; - KeyPress key_left; - KeyPress key_right; - KeyPress key_use; - KeyPress key_sneak; - KeyPress key_jump; - KeyPress key_drop; - KeyPress key_inventory; - KeyPress key_fly; - KeyPress key_fast; - KeyPress key_range; - KeyPress key_chat; - KeyPress key_cmd; - KeyPress key_console; - KeyPress key_dump; + + std::vector key_used; + gui::IGUIStaticText *key_used_text; + std::vector key_settings; }; #endif From 5194505407884f6375311f0ab4c5f8783646cb91 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 18:02:29 +0300 Subject: [PATCH 14/28] Fix GUIKeyChangeMenu so that '/' can be inserted on a finnish keyboard --- src/guiKeyChangeMenu.cpp | 22 +++++++++++++++++++--- src/guiKeyChangeMenu.h | 2 ++ src/keycode.cpp | 15 +++++++++++++-- src/keycode.h | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index a7dbc8c6f..49b292df4 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -60,6 +60,7 @@ GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : GUIModalMenu(env, parent, id, menumgr) { + shift_down = false; activeKey = -1; this->key_used_text = NULL; init_keys(); @@ -204,7 +205,15 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event) && event.KeyInput.PressedDown) { changeCtype(""); - KeyPress kp(event.KeyInput); + bool prefer_character = shift_down; + KeyPress kp(event.KeyInput, prefer_character); + + bool shift_went_down = false; + if(!shift_down && + (event.KeyInput.Key == irr::KEY_SHIFT || + event.KeyInput.Key == irr::KEY_LSHIFT || + event.KeyInput.Key == irr::KEY_RSHIFT)) + shift_went_down = true; // Remove Key already in use message if(this->key_used_text) @@ -240,8 +249,14 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event) this->key_used.push_back(kp); changeCtype("C"); - activeKey = -1; - return true; + // Allow characters made with shift + if(shift_went_down){ + shift_down = true; + return false; + }else{ + activeKey = -1; + return true; + } } } if (event.EventType == EET_GUI_EVENT) @@ -287,6 +302,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event) assert(k); resetMenu(); + shift_down = false; activeKey = event.GUIEvent.Caller->getID(); k->button->setText(wgettext("press key")); this->key_used.erase(std::remove(this->key_used.begin(), diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h index b81866983..beb4f0b6f 100644 --- a/src/guiKeyChangeMenu.h +++ b/src/guiKeyChangeMenu.h @@ -66,6 +66,8 @@ private: void add_key(int id, std::string setting_name, std::string button_name); + bool shift_down; + s32 activeKey; std::vector key_used; diff --git a/src/keycode.cpp b/src/keycode.cpp index 5a8df0db4..cdf3c6062 100644 --- a/src/keycode.cpp +++ b/src/keycode.cpp @@ -288,16 +288,27 @@ KeyPress::KeyPress(const char *name) m_name = name[0]; } -KeyPress::KeyPress(const irr::SEvent::SKeyInput &in) +KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character) { Key = in.Key; Char = in.Char; + + if(prefer_character){ + m_name.resize(MB_CUR_MAX+1, '\0'); + int written = wctomb(&m_name[0], Char); + if(written > 0){ + infostream<<"KeyPress: Preferring character for "<= 0){ + if(written < 0){ std::string hexstr = hex_encode((const char*)&Char, sizeof(Char)); errorstream<<"KeyPress: Unexpected multibyte character "< Date: Wed, 25 Jul 2012 15:56:09 +0200 Subject: [PATCH 15/28] Fix issue with openal mac os x compile --- src/sound_openal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index d2f7276a6..c78f6288f 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -30,7 +30,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc., #elif defined(__APPLE__) #include #include - #include + //#include #else #include #include From b6c12d2aa3f7f3f0faca86a0b20904e309c15179 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 1 Sep 2012 18:32:15 +0300 Subject: [PATCH 16/28] Fix github issue #224 --- src/server.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index ba99f4707..2da9cbe24 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -442,9 +442,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, m_nearest_unsent_reset_timer += dtime; if(m_nothing_to_send_pause_timer >= 0) - { return; - } + + Player *player = server->m_env->getPlayer(peer_id); + // This can happen sometimes; clients and players are not in perfect sync. + if(player == NULL) + return; // Won't send anything if already sending if(m_blocks_sending.size() >= g_settings->getU16 @@ -456,10 +459,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, //TimeTaker timer("RemoteClient::GetNextBlocks"); - Player *player = server->m_env->getPlayer(peer_id); - - assert(player != NULL); - v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); v3f playerspeeddir(0,0,0); From d53a6cd8518edd025acdfc7878ede857867e507b Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 2 Sep 2012 19:38:44 +0300 Subject: [PATCH 17/28] 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 18/28] 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 19/28] 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 20/28] 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 21/28] 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 22/28] 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 23/28] 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 24/28] 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 25/28] 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 26/28] 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 27/28] 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 28/28] 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()