Merge remote-tracking branch 'upstream/master'

This commit is contained in:
sfan5 2012-09-07 18:15:36 +02:00
commit b01ed545a5
21 changed files with 488 additions and 131 deletions

View File

@ -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 # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 4) set(VERSION_MINOR 4)
set(VERSION_PATCH 2-rc1) set(VERSION_PATCH 3)
if(VERSION_EXTRA) if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif() endif()

View File

@ -260,6 +260,16 @@ function minetest.node_punch(pos, node, puncher)
end end
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) function minetest.node_dig(pos, node, digger)
minetest.debug("node_dig") minetest.debug("node_dig")
@ -284,14 +294,9 @@ function minetest.node_dig(pos, node, digger)
local dp = minetest.get_dig_params(def.groups, tp) local dp = minetest.get_dig_params(def.groups, tp)
wielded:add_wear(dp.wear) wielded:add_wear(dp.wear)
digger:set_wielded_item(wielded) digger:set_wielded_item(wielded)
-- Add dropped items to object's inventory -- Handle drops
if digger:get_inventory() then minetest.handle_node_drops(pos, drops, digger)
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
local oldmetadata = nil local oldmetadata = nil
if def.after_dig_node then if def.after_dig_node then

View File

@ -891,6 +891,12 @@ minetest.get_craft_recipe(output) -> input
^ input.items = for example { stack 1, stack 2, stack 3, stack 4, ^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
stack 5, stack 6, stack 7, stack 8, stack 9 } stack 5, stack 6, stack 7, stack 8, stack 9 }
^ input.items = nil if no recipe found ^ 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: Rollbacks:
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds

View File

@ -157,43 +157,21 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
return false; 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(); INodeDefManager *nodemgr = m_gamedef->ndef();
//m_dout<<DTIME<<"Rendering map..."<<std::endl; for(core::map<v3s16, MapBlock*>::Iterator
DSTACK(__FUNCTION_NAME); i = m_drawlist.getIterator();
i.atEnd() == false; i++)
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(); MapBlock *block = i.getNode()->getValue();
block->refDrop();
} }
m_drawlist.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(); m_camera_mutex.Lock();
v3f camera_position = m_camera_position; v3f camera_position = m_camera_position;
@ -201,17 +179,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
f32 camera_fov = m_camera_fov; f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock(); m_camera_mutex.Unlock();
/* // Use a higher fov to accomodate faster camera movements.
Get all blocks and draw all visible ones // 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 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); 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_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = 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 // Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed. // Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min( 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.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / 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 // Number of blocks in rendering range
u32 blocks_in_range = 0; u32 blocks_in_range = 0;
// Number of blocks occlusion culled // 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 // Blocks that were drawn and had a mesh
u32 blocks_drawn = 0; u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass // 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 // Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0; //u32 blocks_without_stuff = 0;
/*
Collect a set of blocks for drawing
*/
core::map<v3s16, MapBlock*> drawset;
{
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
for(core::map<v2s16, MapSector*>::Iterator for(core::map<v2s16, MapSector*>::Iterator
si = m_sectors.getIterator(); si = m_sectors.getIterator();
@ -380,36 +340,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
&& d > m_control.wanted_min_range * BS) && d > m_control.wanted_min_range * BS)
continue; 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 // Add to set
drawset[block->getPos()] = block; block->refGrab();
m_drawlist[block->getPos()] = block;
sector_blocks_drawn++; sector_blocks_drawn++;
blocks_drawn++; blocks_drawn++;
@ -418,8 +352,127 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
if(sector_blocks_drawn != 0) if(sector_blocks_drawn != 0)
m_last_drawn_sectors[sp] = true; 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<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
core::list<MeshBufList> lists;
void clear()
{
lists.clear();
}
void add(scene::IMeshBuffer *buf)
{
for(core::list<MeshBufList>::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 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); ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
int timecheck_counter = 0; MeshBufListList drawbufs;
for(core::map<v3s16, MapBlock*>::Iterator for(core::map<v3s16, MapBlock*>::Iterator
i = drawset.getIterator(); i = m_drawlist.getIterator();
i.atEnd() == false; i++) 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; i<c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(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 ["<<analyze_block(block)
<<"] contains an empty meshbuf"<<std::endl;
drawbufs.add(buf);
}
}
}
}
core::list<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++)
{ {
{ {
timecheck_counter++; 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<scene::IMeshBuffer*>::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 Draw the faces of the block
*/ */
@ -494,17 +638,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
else else
blocks_without_stuff++; blocks_without_stuff++;
} }
#endif
} }
} // ScopeProfiler } // ScopeProfiler
// Log only on solid pass because values are the same // Log only on solid pass because values are the same
if(pass == scene::ESNRP_SOLID){ 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", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
} }

View File

@ -113,7 +113,8 @@ public:
{ {
return m_box; return m_box;
} }
void updateDrawList(video::IVideoDriver* driver);
void renderMap(video::IVideoDriver* driver, s32 pass); void renderMap(video::IVideoDriver* driver, s32 pass);
int getBackgroundBrightness(float max_d, u32 daylight_factor, int getBackgroundBrightness(float max_d, u32 daylight_factor,
@ -141,6 +142,8 @@ private:
v3f m_camera_direction; v3f m_camera_direction;
f32 m_camera_fov; f32 m_camera_fov;
JMutex m_camera_mutex; JMutex m_camera_mutex;
core::map<v3s16, MapBlock*> m_drawlist;
core::map<v2s16, bool> m_last_drawn_sectors; core::map<v2s16, bool> m_last_drawn_sectors;
}; };

View File

@ -65,9 +65,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
TOSERVER_INVENTORY_FIELDS TOSERVER_INVENTORY_FIELDS
16-bit node ids 16-bit node ids
TOCLIENT_DETACHED_INVENTORY 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 #define PROTOCOL_ID 0x4f457403

View File

@ -1235,6 +1235,9 @@ void the_game(
float object_hit_delay_timer = 0.0; float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10; 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 invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false; bool respawn_menu_active = false;
@ -2697,7 +2700,19 @@ void the_game(
item = mlist->getItem(client.getPlayerItem()); item = mlist->getItem(client.getPlayerItem());
camera.wield(item); 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 Drawing begins
*/ */

View File

@ -733,6 +733,54 @@ void GUIFormSpecMenu::drawMenu()
void GUIFormSpecMenu::updateSelectedItem() 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; i<m_inventorylists.size() && !found; i++){
const ListDrawSpec &s = m_inventorylists[i];
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv)
continue;
InventoryList *list = inv->getList(s.listname);
if(!list)
continue;
for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
u32 item_i = i + s.start_item_i;
if(item_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 "
<<s.inventoryloc.dump()<<" "<<s.listname
<<" "<<item_i<<std::endl;
m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
m_selected_amount = stack.count;
break;
}
}
}
if(!found){
infostream<<"Client: Discarding selected content guess: "
<<m_selected_content_guess.getItemString()<<std::endl;
m_selected_content_guess.name = "";
}
}
// If the selected stack has become empty for some reason, deselect it. // If the selected stack has become empty for some reason, deselect it.
// If the selected stack has become smaller, adjust m_selected_amount. // If the selected stack has become smaller, adjust m_selected_amount.
if(m_selected_item) if(m_selected_item)
@ -1054,21 +1102,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Check how many items can be moved // Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->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;
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()) else if(leftover.empty())
{ {
// Item fits
m_selected_amount -= move_amount; m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
} }
// Source stack goes partly into destination stack
else else
{ {
// Item only fits partially
move_amount -= leftover.count; move_amount -= leftover.count;
m_selected_amount -= move_amount; m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
} }
infostream<<"Handing IACTION_MOVE to manager"<<std::endl; infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
@ -1084,6 +1139,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
else if(drop_amount > 0) else if(drop_amount > 0)
{ {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_DROP // Send IACTION_DROP
assert(m_selected_item && m_selected_item->isValid()); assert(m_selected_item && m_selected_item->isValid());
@ -1107,6 +1164,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
else if(craft_amount > 0) else if(craft_amount > 0)
{ {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT // Send IACTION_CRAFT
assert(s.isValid()); assert(s.isValid());
@ -1126,6 +1185,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_item = NULL; m_selected_item = NULL;
m_selected_amount = 0; m_selected_amount = 0;
m_selected_dragging = false; m_selected_dragging = false;
m_selected_content_guess = ItemStack();
} }
} }
if(event.EventType==EET_GUI_EVENT) if(event.EventType==EET_GUI_EVENT)

View File

@ -212,6 +212,12 @@ protected:
ItemSpec *m_selected_item; ItemSpec *m_selected_item;
u32 m_selected_amount; u32 m_selected_amount;
bool m_selected_dragging; 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; v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element; gui::IGUIStaticText *m_tooltip_element;

View File

@ -37,7 +37,7 @@
enum enum
{ {
GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR, GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
//buttons // buttons
GUI_ID_KEY_FORWARD_BUTTON, GUI_ID_KEY_FORWARD_BUTTON,
GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_BACKWARD_BUTTON,
GUI_ID_KEY_LEFT_BUTTON, GUI_ID_KEY_LEFT_BUTTON,
@ -53,7 +53,9 @@ enum
GUI_ID_KEY_DROP_BUTTON, GUI_ID_KEY_DROP_BUTTON,
GUI_ID_KEY_INVENTORY_BUTTON, GUI_ID_KEY_INVENTORY_BUTTON,
GUI_ID_KEY_DUMP_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, GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
@ -135,6 +137,18 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
else else
offset += v2s32(0, 25); offset += v2s32(0, 25);
} }
{
s32 option_x = offset.X + 10;
s32 option_y = offset.Y;
u32 option_w = 180;
{
core::rect<s32> 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); core::rect < s32 > rect(0, 0, 100, 30);
@ -177,6 +191,11 @@ bool GUIKeyChangeMenu::acceptInput()
key_setting *k = key_settings.at(i); key_setting *k = key_settings.at(i);
g_settings->set(k->setting_name, k->key.sym()); 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(); clearKeyCache();
return true; return true;
} }

View File

@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h" // getTexturePath #include "tile.h" // getTexturePath
#include "filesys.h" #include "filesys.h"
#include "util/string.h" #include "util/string.h"
#include "subgame.h"
struct CreateWorldDestMainMenu : public CreateWorldDest struct CreateWorldDestMainMenu : public CreateWorldDest
{ {
@ -47,6 +48,22 @@ struct CreateWorldDestMainMenu : public CreateWorldDest
{} {}
void accepted(std::wstring name, std::string gameid) void accepted(std::wstring name, std::string gameid)
{ {
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"));
return;
}
std::vector<WorldSpec> worlds = getAvailableWorlds();
for(std::vector<WorldSpec>::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); m_menu->createNewWorld(name, gameid);
} }
GUIMainMenu *m_menu; GUIMainMenu *m_menu;
@ -929,3 +946,7 @@ int GUIMainMenu::getTab()
return TAB_SINGLEPLAYER; // Default return TAB_SINGLEPLAYER; // Default
} }
void GUIMainMenu::displayMessageMenu(std::wstring msg)
{
(new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
}

View File

@ -92,6 +92,7 @@ public:
void createNewWorld(std::wstring name, std::string gameid); void createNewWorld(std::wstring name, std::string gameid);
void deleteWorld(const std::vector<std::string> &paths); void deleteWorld(const std::vector<std::string> &paths);
int getTab(); int getTab();
void displayMessageMenu(std::wstring msg);
private: private:
MainMenuData *m_data; MainMenuData *m_data;

View File

@ -542,10 +542,6 @@ void InventoryList::deSerialize(std::istream &is)
throw SerializationError("too many items"); throw SerializationError("too many items");
m_items[item_i++].clear(); 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); m_lists.push_back(list);
} }
else
{
throw SerializationError("Unknown inventory identifier");
}
} }
} }

View File

@ -332,6 +332,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
// If source is infinite, reset it's stack // If source is infinite, reset it's stack
if(src_can_take_count == -1){ 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; i<list_to->getSize(); i++){
if(list_to->getItem(i).empty()){
list_to->changeItem(i, to_stack_was);
break;
}
}
}
list_from->deleteItem(from_i); list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was); list_from->addItem(from_i, from_stack_was);
} }
@ -339,6 +351,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
if(dst_can_put_count == -1){ if(dst_can_put_count == -1){
list_to->deleteItem(to_i); list_to->deleteItem(to_i);
list_to->addItem(to_i, to_stack_was); 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); list_from->takeItem(from_i, count);
} }

View File

@ -66,6 +66,29 @@ struct InventoryLocation
name = name_; 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_) void applyCurrentPlayer(const std::string &name_)
{ {
if(type == CURRENT_PLAYER) if(type == CURRENT_PLAYER)

View File

@ -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.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); position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
f32 min_y = lwn_f.Y + 0.5*BS; if(!is_climbing)
if(position.Y < min_y)
{ {
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) if(m_speed.Y < 0)
m_speed.Y = 0; m_speed.Y = 0;
}
} }
} }

View File

@ -1468,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout,
MapBlock *block = (*i); MapBlock *block = (*i);
block->incrementUsageTimer(dtime); block->incrementUsageTimer(dtime);
if(block->getUsageTimer() > unload_timeout) if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
{ {
v3s16 p = block->getPos(); v3s16 p = block->getPos();

View File

@ -56,7 +56,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_generated(false), m_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0) m_usage_timer(0),
m_refcount(0)
{ {
data = NULL; data = NULL;
if(dummy == false) if(dummy == false)

View File

@ -430,6 +430,22 @@ public:
{ {
return m_usage_timer; return m_usage_timer;
} }
/*
See m_refcount
*/
void refGrab()
{
m_refcount++;
}
void refDrop()
{
m_refcount--;
}
int refGet()
{
return m_refcount;
}
/* /*
Node Timers Node Timers
@ -566,6 +582,12 @@ private:
Map will unload the block when this reaches a timeout. Map will unload the block when this reaches a timeout.
*/ */
float m_usage_timer; 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) inline bool blockpos_over_limit(v3s16 p)

View File

@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <set> #include <set>
#include <vector> #include <vector>
#define WORLDNAME_BLACKLISTED_CHARS "/\\"
struct SubgameSpec struct SubgameSpec
{ {
std::string id; // "" = game does not exist std::string id; // "" = game does not exist

View File

@ -242,6 +242,29 @@ inline bool string_allowed(const std::string &s, const std::string &allowed_char
return true; 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 Forcefully wraps string into rows using \n
(no word wrap, used for showing paths in gui) (no word wrap, used for showing paths in gui)