mirror of https://github.com/minetest/minetest.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
b01ed545a5
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
17
src/game.cpp
17
src/game.cpp
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue