Compare commits

..

12 Commits
0.4.0 ... 0.4.1

Author SHA1 Message Date
Perttu Ahola
e3ddbe8c6b Version 0.4.1 2012-07-21 23:14:23 +03:00
Andreas Zwinkau
d085139057 Fix signedness warning in base64.cpp 2012-07-21 22:10:29 +03:00
Andreas Zwinkau
28e7443f9b Fix wctomb use
wctomb(NULL, _) returns "nonzero if the encoding has nontrivial shift state, or zero if the encoding is stateless."

I assume the intentation was to get the size of the target buffer.
Use MB_CUR_MAX for this.
2012-07-21 22:09:17 +03:00
Andreas Zwinkau
e79ad21aeb Remove mbtowc warnings
As mbtowc(_, _, 1) reads at most one char, everything other than a
return value of 1 is an error. Since the input strings are static,
an assert protects against future changes.

Likewise, wctomb should currently never encounter a character, which
actually needs a multibyte representation.
2012-07-21 22:08:20 +03:00
Perttu Ahola
0b61253931 Actually fix facedir-rotated nodes placed using minetest.env:place_node() 2012-07-21 21:23:15 +03:00
Matthew I
a2738dec59 Fix hovering after mining a block underneath you while sneaking 2012-07-21 20:56:56 +03:00
Perttu Ahola
1788709e2d Rotate facedir-rotated top and bottom textures too, and re-implement nodebox side rotation 2012-07-21 20:23:32 +03:00
Perttu Ahola
47d30d12cb Facedir rotation of nodebox textures 2012-07-21 18:59:12 +03:00
Perttu Ahola
43df78102c Check whether node is known before reading definition in __builtin:item:on_step() 2012-07-21 16:36:14 +03:00
Bad-Command
cc10eec6c6 Fix signed overflow in getPointedThing 2012-07-21 16:13:51 +03:00
Perttu Ahola
15bf9a7026 Fix typo in scriptapi.cpp in minetest.get_craft_recipe() 2012-07-21 15:32:46 +03:00
Perttu Ahola
2795f44f03 Server-side checking of digging; disable_anticheat setting 2012-07-21 14:38:49 +03:00
17 changed files with 224 additions and 39 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
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 0)
set(VERSION_PATCH 1)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()

View File

@@ -148,11 +148,13 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
newnode.param2 = minetest.dir_to_wallmounted(dir)
-- Calculate the direction for furnaces and chests and stuff
elseif def.paramtype2 == 'facedir' and placer then
local playerpos = placer:getpos()
local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z}
newnode.param2 = minetest.dir_to_facedir(dir)
minetest.log("action", "facedir: " .. newnode.param2)
elseif def.paramtype2 == 'facedir' then
local placer_pos = placer:getpos()
if placer_pos then
local dir = {x = pos.x - placer_pos.x, y = pos.y - placer_pos.y, z = pos.z - placer_pos.z}
newnode.param2 = minetest.dir_to_facedir(dir)
minetest.log("action", "facedir: " .. newnode.param2)
end
end
-- Add node and update

View File

@@ -72,7 +72,8 @@ minetest.register_entity("__builtin:item", {
local p = self.object:getpos()
p.y = p.y - 0.3
local nn = minetest.env:get_node(p).name
if minetest.registered_nodes[nn].walkable then
-- If node is not registered or node is walkably solid
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable then
if self.physical_state then
self.object:setvelocity({x=0,y=0,z=0})
self.object:setacceleration({x=0, y=0, z=0})

View File

@@ -447,6 +447,9 @@ minetest.register_node("experimental:tester_node_1", {
tile_images = {"wieldhand.png"},
groups = {oddly_breakable_by_hand=2},
sounds = default.node_sound_wood_defaults(),
-- This was known to cause a bug in minetest.item_place_node() when used
-- via minetest.env:place_node(), causing a placer with no position
paramtype2 = "facedir",
on_construct = function(pos)
experimental.print_to_everything("experimental:tester_node_1:on_construct("..minetest.pos_to_string(pos)..")")

View File

@@ -158,6 +158,8 @@
#static_spawnpoint = 0, 10, 0
# If true, new players cannot join with an empty password
#disallow_empty_password = false
# If true, disable cheat prevention in multiplayer
#disable_anticheat = false
# Profiler data print interval. #0 = disable.
#profiler_print_interval = 0

View File

@@ -40,7 +40,7 @@ static inline bool is_base64(unsigned char c) {
bool base64_is_valid(std::string const& s)
{
for(int i=0; i<s.size(); i++)
for(size_t i=0; i<s.size(); i++)
if(!is_base64(s[i])) return false;
return true;
}

View File

@@ -1029,10 +1029,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
case NDT_NODEBOX:
{
static const v3s16 tile_dirs[6] = {
v3s16(0, 1, 0),
v3s16(0, -1, 0),
v3s16(1, 0, 0),
v3s16(-1, 0, 0),
v3s16(0, 0, 1),
v3s16(0, 0, -1)
};
TileSpec tiles[6];
for(int i = 0; i < 6; i++)
{
tiles[i] = getNodeTileN(n, p, i, data);
// Handles facedir rotation for textures
tiles[i] = getNodeTile(n, p, tile_dirs[i], data);
}
u16 l = getInteriorLight(n, 0, data);

View File

@@ -764,6 +764,8 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_last_good_position(0,0,0),
m_last_good_position_age(0),
m_time_from_last_punch(0),
m_nocheat_dig_pos(32767, 32767, 32767),
m_nocheat_dig_time(0),
m_wield_index(0),
m_position_not_sent(false),
m_armor_groups_sent(false),
@@ -874,8 +876,9 @@ void PlayerSAO::step(float dtime, bool send_recommended)
}
m_time_from_last_punch += dtime;
m_nocheat_dig_time += dtime;
if(m_is_singleplayer)
if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
{
m_last_good_position = m_player->getPosition();
m_last_good_position_age = 0;
@@ -888,7 +891,8 @@ void PlayerSAO::step(float dtime, bool send_recommended)
NOTE: Actually the server should handle player physics like the
client does and compare player's position to what is calculated
on our side. This is required when eg. players fly due to an
explosion.
explosion. Altough a node-based alternative might be possible
too, and much more lightweight.
*/
float player_max_speed = 0;

View File

@@ -173,6 +173,9 @@ class PlayerSAO : public ServerActiveObject
{
return m_peer_id;
}
// Cheat prevention
v3f getLastGoodPosition() const
{
return m_last_good_position;
@@ -183,6 +186,26 @@ class PlayerSAO : public ServerActiveObject
m_time_from_last_punch = 0.0;
return r;
}
void noCheatDigStart(v3s16 p)
{
m_nocheat_dig_pos = p;
m_nocheat_dig_time = 0;
}
v3s16 getNoCheatDigPos()
{
return m_nocheat_dig_pos;
}
float getNoCheatDigTime()
{
return m_nocheat_dig_time;
}
void noCheatDigEnd()
{
m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
}
// Other
void updatePrivileges(const std::set<std::string> &privs,
bool is_singleplayer)
{
@@ -196,9 +219,14 @@ class PlayerSAO : public ServerActiveObject
Player *m_player;
u16 m_peer_id;
Inventory *m_inventory;
// Cheat prevention
v3f m_last_good_position;
float m_last_good_position_age;
float m_time_from_last_punch;
v3s16 m_nocheat_dig_pos;
float m_nocheat_dig_time;
int m_wield_index;
bool m_position_not_sent;
ItemGroupList m_armor_groups;

View File

@@ -121,6 +121,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("unlimited_player_transfer_distance", "true");
settings->setDefault("enable_pvp", "true");
settings->setDefault("disallow_empty_password", "false");
settings->setDefault("disable_anticheat", "false");
settings->setDefault("profiler_print_interval", "0");
settings->setDefault("enable_mapgen_debug_info", "false");

View File

@@ -367,6 +367,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
// Prevent signed number overflow
if(yend==32767)
yend=32766;
if(zend==32767)
zend=32766;
if(xend==32767)
xend=32766;
for(s16 y = ystart; y <= yend; y++)
for(s16 z = zstart; z <= zend; z++)
for(s16 x = xstart; x <= xend; x++)
@@ -2112,7 +2120,9 @@ void the_game(
ldown_for_dig = true;
}
MapNode n = client.getEnv().getClientMap().getNode(nodepos);
// NOTE: Similar piece of code exists on the server side for
// cheat detection.
// Get digging parameters
DigParams params = getDigParams(nodedef->get(n).groups,
&playeritem_toolcap);
@@ -2160,7 +2170,7 @@ void the_game(
{
dig_index = crack_animation_length;
}
// Don't show cracks if not diggable
if(dig_time_complete >= 100000.0)
{

View File

@@ -258,9 +258,10 @@ KeyPress::KeyPress(const char *name)
try {
Key = keyname_to_keycode(name);
m_name = name;
if (strlen(name) > 8)
mbtowc(&Char, name + 8, 1);
else
if (strlen(name) > 8) {
int chars_read = mbtowc(&Char, name + 8, 1);
assert (chars_read == 1 && "unexpected multibyte character");
} else
Char = L'\0';
return;
} catch (UnknownKeycode &e) {};
@@ -270,7 +271,8 @@ KeyPress::KeyPress(const char *name)
m_name += name;
try {
Key = keyname_to_keycode(m_name.c_str());
mbtowc(&Char, name, 1);
int chars_read = mbtowc(&Char, name, 1);
assert (chars_read == 1 && "unexpected multibyte character");
return;
} catch (UnknownKeycode &e) {};
}
@@ -279,7 +281,8 @@ KeyPress::KeyPress(const char *name)
Key = irr::KEY_KEY_CODES_COUNT;
mbtowc(&Char, name, 1);
int mbtowc_ret = mbtowc(&Char, name, 1);
assert (mbtowc_ret == 1 && "unexpected multibyte character");
m_name = name[0];
}
@@ -290,9 +293,9 @@ KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
if (valid_kcode(Key)) {
m_name = KeyNames[Key];
} else {
size_t maxlen = wctomb(NULL, Char);
m_name.resize(maxlen+1, '\0');
wctomb(&m_name[0], Char);
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
assert (written >= 0 && "unexpected multibyte character");
}
}

View File

@@ -35,7 +35,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
LocalPlayer::LocalPlayer(IGameDef *gamedef):
Player(gamedef),
m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false)
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)
{
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
@@ -189,8 +192,26 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
/*
Check the nodes under the player to see from which node the
player is sneaking from, if any.
player is sneaking from, if any. If the node from under
the player has been removed, the player falls.
*/
v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
if(m_sneak_node_exists &&
nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
m_old_node_below_type != "air")
{
// Old node appears to have been removed; that is,
// it wasn't air before but now it is
m_need_to_get_new_sneak_node = false;
m_sneak_node_exists = false;
}
else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
{
// We are on something, so make sure to recalculate the sneak
// node.
m_need_to_get_new_sneak_node = true;
}
if(m_need_to_get_new_sneak_node)
{
v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
v2f player_p2df(position.X, position.Z);
@@ -240,17 +261,9 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
}
bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
if(control.sneak && m_sneak_node_exists)
{
if(sneak_node_found)
m_sneak_node = new_sneak_node;
}
else
{
m_sneak_node = new_sneak_node;
m_sneak_node_exists = sneak_node_found;
}
m_sneak_node = new_sneak_node;
m_sneak_node_exists = sneak_node_found;
/*
If sneaking, the player's collision box can be in air, so
@@ -295,6 +308,12 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
}
}
}
/*
Update the node last under the player
*/
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;
}
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)

View File

@@ -95,6 +95,12 @@ class LocalPlayer : public Player
v3s16 m_sneak_node;
// Whether the player is allowed to sneak
bool m_sneak_node_exists;
// Node below player, used to determine whether it has been removed,
// and its old type
v3s16 m_old_node_below;
std::string m_old_node_below_type;
// Whether recalculation of the sneak node is needed
bool m_need_to_get_new_sneak_node;
};
#endif

View File

@@ -605,7 +605,46 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
};
u8 tileindex = dir_to_tile[facedir*8 + dir_i];
return getNodeTileN(mn, p, tileindex, data);
// If not rotated or is side tile, we're done
if(facedir == 0 || (tileindex != 0 && tileindex != 1))
return getNodeTileN(mn, p, tileindex, data);
// This is the top or bottom tile, and it shall be rotated; thus rotate it
TileSpec spec = getNodeTileN(mn, p, tileindex, data);
if(tileindex == 0){
if(facedir == 1){ // -90
std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
name += "^[transformR270";
spec.texture = data->m_gamedef->tsrc()->getTexture(name);
}
else if(facedir == 2){ // 180
spec.texture.pos += spec.texture.size;
spec.texture.size *= -1;
}
else if(facedir == 3){ // 90
std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
name += "^[transformR90";
spec.texture = data->m_gamedef->tsrc()->getTexture(name);
}
}
else if(tileindex == 1){
if(facedir == 1){ // -90
std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
name += "^[transformR90";
spec.texture = data->m_gamedef->tsrc()->getTexture(name);
}
else if(facedir == 2){ // 180
spec.texture.pos += spec.texture.size;
spec.texture.size *= -1;
}
else if(facedir == 3){ // 90
std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
name += "^[transformR270";
spec.texture = data->m_gamedef->tsrc()->getTexture(name);
}
}
return spec;
}
static void getTileInfo(

View File

@@ -4593,7 +4593,7 @@ static int l_get_craft_recipe(lua_State *L)
setintfield(L, -1, "width", input.width);
switch (input.method) {
case CRAFT_METHOD_NORMAL:
lua_pushstring(L,"noraml");
lua_pushstring(L,"normal");
break;
case CRAFT_METHOD_COOKING:
lua_pushstring(L,"cooking");

View File

@@ -3014,6 +3014,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
if(n.getContent() != CONTENT_IGNORE)
scriptapi_node_on_punch(m_lua, p_under, n, playersao);
// Cheat prevention
playersao->noCheatDigStart(p_under);
}
else if(pointed.type == POINTEDTHING_OBJECT)
{
@@ -3051,7 +3053,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/
else if(action == 2)
{
// Only complete digging of nodes
// Only digging of nodes
if(pointed.type == POINTEDTHING_NODE)
{
MapNode n(CONTENT_IGNORE);
@@ -3067,10 +3069,65 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
m_emerge_queue.addBlock(peer_id,
getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
}
if(n.getContent() != CONTENT_IGNORE)
/* Cheat prevention */
bool is_valid_dig = true;
if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
{
v3s16 nocheat_p = playersao->getNoCheatDigPos();
float nocheat_t = playersao->getNoCheatDigTime();
playersao->noCheatDigEnd();
// If player didn't start digging this, ignore dig
if(nocheat_p != p_under){
infostream<<"Server: NoCheat: "<<player->getName()
<<" started digging "
<<PP(nocheat_p)<<" and completed digging "
<<PP(p_under)<<"; not digging."<<std::endl;
is_valid_dig = false;
}
// Get player's wielded item
ItemStack playeritem;
InventoryList *mlist = playersao->getInventory()->getList("main");
if(mlist != NULL)
playeritem = mlist->getItem(playersao->getWieldIndex());
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(m_itemdef);
// Get diggability and expected digging time
DigParams params = getDigParams(m_nodedef->get(n).groups,
&playeritem_toolcap);
// If can't dig, try hand
if(!params.diggable){
const ItemDefinition &hand = m_itemdef->get("");
const ToolCapabilities *tp = hand.tool_capabilities;
if(tp)
params = getDigParams(m_nodedef->get(n).groups, tp);
}
// If can't dig, ignore dig
if(!params.diggable){
infostream<<"Server: NoCheat: "<<player->getName()
<<" completed digging "<<PP(p_under)
<<", which is not diggable with tool. not digging."
<<std::endl;
is_valid_dig = false;
}
// If time is considerably too short, ignore dig
// Check time only for medium and slow timed digs
if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
infostream<<"Server: NoCheat: "<<player->getName()
<<" completed digging "
<<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
<<params.time<<"s; not digging."<<std::endl;
is_valid_dig = false;
}
}
/* Actually dig node */
if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
scriptapi_node_on_dig(m_lua, p_under, n, playersao);
if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
// Send unusual result (that is, node not being removed)
if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
{
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);