1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-11 17:51:04 +00:00

Fix number of tool uses being off by 1..32767 (#11110)

This commit is contained in:
Wuzzy 2021-10-31 22:33:33 +00:00 committed by GitHub
parent 38ba813c55
commit 6910c8d920
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 229 additions and 71 deletions

View file

@ -1870,7 +1870,8 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
m_armor_groups,
toolcap,
punchitem,
time_from_last_punch);
time_from_last_punch,
punchitem->wear);
if(result.did_punch && result.damage != 0)
{

View file

@ -3619,7 +3619,8 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
// cheat detection.
// Get digging parameters
DigParams params = getDigParams(nodedef_manager->get(n).groups,
&selected_item.getToolCapabilities(itemdef_manager));
&selected_item.getToolCapabilities(itemdef_manager),
selected_item.wear);
// If can't dig, try hand
if (!params.diggable) {

View file

@ -1119,8 +1119,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
float time_from_last_punch =
playersao->resetTimeFromLastPunch();
u16 wear = pointed_object->punch(dir, &toolcap, playersao,
time_from_last_punch);
u32 wear = pointed_object->punch(dir, &toolcap, playersao,
time_from_last_punch, tool_item.wear);
// Callback may have changed item, so get it again
playersao->getWieldedItem(&selected_item);
@ -1173,7 +1173,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Get diggability and expected digging time
DigParams params = getDigParams(m_nodedef->get(n).groups,
&selected_item.getToolCapabilities(m_itemdef));
&selected_item.getToolCapabilities(m_itemdef),
selected_item.wear);
// If can't dig, try hand
if (!params.diggable) {
params = getDigParams(m_nodedef->get(n).groups,

View file

@ -174,7 +174,7 @@ int ObjectRef::l_punch(lua_State *L)
v3f dir = readParam<v3f>(L, 5, sao->getBasePosition() - puncher->getBasePosition());
dir.normalize();
u16 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch);
u32 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch);
lua_pushnumber(L, wear);
return 1;

View file

@ -160,28 +160,33 @@ int ModApiUtil::l_write_json(lua_State *L)
return 1;
}
// get_dig_params(groups, tool_capabilities)
// get_dig_params(groups, tool_capabilities[, wear])
int ModApiUtil::l_get_dig_params(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ItemGroupList groups;
read_groups(L, 1, groups);
ToolCapabilities tp = read_tool_capabilities(L, 2);
push_dig_params(L, getDigParams(groups, &tp));
if (lua_isnoneornil(L, 3)) {
push_dig_params(L, getDigParams(groups, &tp));
} else {
u16 wear = readParam<int>(L, 3);
push_dig_params(L, getDigParams(groups, &tp, wear));
}
return 1;
}
// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
// get_hit_params(groups, tool_capabilities[, time_from_last_punch, [, wear]])
int ModApiUtil::l_get_hit_params(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
std::unordered_map<std::string, int> groups;
read_groups(L, 1, groups);
ToolCapabilities tp = read_tool_capabilities(L, 2);
if(lua_isnoneornil(L, 3))
push_hit_params(L, getHitParams(groups, &tp));
else
push_hit_params(L, getHitParams(groups, &tp, readParam<float>(L, 3)));
float time_from_last_punch = readParam<float>(L, 3, 1000000);
int wear = readParam<int>(L, 4, 0);
push_hit_params(L, getHitParams(groups, &tp,
time_from_last_punch, wear));
return 1;
}

View file

@ -50,10 +50,10 @@ private:
// write_json(data[, styled])
static int l_write_json(lua_State *L);
// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
// get_dig_params(groups, tool_capabilities[, wear])
static int l_get_dig_params(lua_State *L);
// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
// get_hit_params(groups, tool_capabilities[, time_from_last_punch[, wear]])
static int l_get_hit_params(lua_State *L);
// check_password_entry(name, entry, password)

View file

@ -305,10 +305,11 @@ void LuaEntitySAO::getStaticData(std::string *result) const
*result = os.str();
}
u16 LuaEntitySAO::punch(v3f dir,
u32 LuaEntitySAO::punch(v3f dir,
const ToolCapabilities *toolcap,
ServerActiveObject *puncher,
float time_from_last_punch)
float time_from_last_punch,
u16 initial_wear)
{
if (!m_registered) {
// Delete unknown LuaEntities when punched
@ -326,7 +327,8 @@ u16 LuaEntitySAO::punch(v3f dir,
m_armor_groups,
toolcap,
&tool_item,
time_from_last_punch);
time_from_last_punch,
initial_wear);
bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);

View file

@ -44,9 +44,10 @@ public:
bool isStaticAllowed() const { return m_prop.static_save; }
bool shouldUnload() const { return true; }
void getStaticData(std::string *result) const;
u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr,
u32 punch(v3f dir, const ToolCapabilities *toolcap = nullptr,
ServerActiveObject *puncher = nullptr,
float time_from_last_punch = 1000000.0f);
float time_from_last_punch = 1000000.0f,
u16 initial_wear = 0);
void rightClick(ServerActiveObject *clicker);
void setPos(const v3f &pos);
void moveTo(v3f pos, bool continuous);

View file

@ -409,10 +409,11 @@ void PlayerSAO::setLookPitchAndSend(const float pitch)
m_env->getGameDef()->SendMovePlayer(m_peer_id);
}
u16 PlayerSAO::punch(v3f dir,
u32 PlayerSAO::punch(v3f dir,
const ToolCapabilities *toolcap,
ServerActiveObject *puncher,
float time_from_last_punch)
float time_from_last_punch,
u16 initial_wear)
{
if (!toolcap)
return 0;
@ -430,7 +431,7 @@ u16 PlayerSAO::punch(v3f dir,
s32 old_hp = getHP();
HitParams hitparams = getHitParams(m_armor_groups, toolcap,
time_from_last_punch);
time_from_last_punch, initial_wear);
PlayerSAO *playersao = m_player->getPlayerSAO();

View file

@ -109,8 +109,8 @@ public:
Interaction interface
*/
u16 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher,
float time_from_last_punch);
u32 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher,
float time_from_last_punch, u16 initial_wear = 0);
void rightClick(ServerActiveObject *clicker);
void setHP(s32 hp, const PlayerHPChangeReason &reason) override
{

View file

@ -145,11 +145,12 @@ public:
virtual bool shouldUnload() const
{ return true; }
// Returns tool wear
virtual u16 punch(v3f dir,
// Returns added tool wear
virtual u32 punch(v3f dir,
const ToolCapabilities *toolcap = nullptr,
ServerActiveObject *puncher = nullptr,
float time_from_last_punch = 1000000.0f)
float time_from_last_punch = 1000000.0f,
u16 initial_wear = 0)
{ return 0; }
virtual void rightClick(ServerActiveObject *clicker)
{}

View file

@ -183,9 +183,74 @@ void ToolCapabilities::deserializeJson(std::istream &is)
}
}
DigParams getDigParams(const ItemGroupList &groups,
const ToolCapabilities *tp)
static u32 calculateResultWear(const u32 uses, const u16 initial_wear)
{
if (uses == 0) {
// Trivial case: Infinite uses
return 0;
}
/* Finite uses. This is not trivial,
as the maximum wear is not neatly evenly divisible by
most possible uses numbers. For example, for 128
uses, the calculation of wear is trivial, as
65536 / 128 uses = 512 wear,
so the tool will get 512 wear 128 times in its lifetime.
But for a number like 130, this does not work:
65536 / 130 uses = 504.123... wear.
Since wear must be an integer, we will get
504*130 = 65520, which would lead to the wrong number
of uses.
Instead, we partition the "wear range" into blocks:
A block represents a single use and can be
of two possible sizes: normal and oversized.
A normal block is equal to floor(65536 / uses).
An oversized block is a normal block plus 1.
Then we determine how many oversized and normal
blocks we need and finally, whether we add
the normal wear or the oversized wear.
Example for 130 uses:
* Normal wear = 504
* Number of normal blocks = 114
* Oversized wear = 505
* Number of oversized blocks = 16
If we add everything together, we get:
114*504 + 16*505 = 65536
*/
u32 result_wear;
u32 wear_normal = ((U16_MAX+1) / uses);
// Will be non-zero if its not evenly divisible
u16 blocks_oversize = (U16_MAX+1) % uses;
// Whether to add one extra wear point in case
// of oversized wear.
u16 wear_extra = 0;
if (blocks_oversize > 0) {
u16 blocks_normal = uses - blocks_oversize;
/* When the wear has reached this value, we
know that wear_normal has been applied
for blocks_normal times, therefore,
only oversized blocks remain.
This also implies the raw tool wear number
increases a bit faster after this point,
but this should be barely noticable by the
player.
*/
u16 wear_extra_at = blocks_normal * wear_normal;
if (initial_wear >= wear_extra_at) {
wear_extra = 1;
}
}
result_wear = wear_normal + wear_extra;
return result_wear;
}
DigParams getDigParams(const ItemGroupList &groups,
const ToolCapabilities *tp,
const u16 initial_wear)
{
// Group dig_immediate defaults to fixed time and no wear
if (tp->groupcaps.find("dig_immediate") == tp->groupcaps.cend()) {
switch (itemgroup_get(groups, "dig_immediate")) {
@ -201,7 +266,7 @@ DigParams getDigParams(const ItemGroupList &groups,
// Values to be returned (with a bit of conversion)
bool result_diggable = false;
float result_time = 0.0;
float result_wear = 0.0;
u32 result_wear = 0;
std::string result_main_group;
int level = itemgroup_get(groups, "level");
@ -224,20 +289,22 @@ DigParams getDigParams(const ItemGroupList &groups,
if (!result_diggable || time < result_time) {
result_time = time;
result_diggable = true;
if (cap.uses != 0)
result_wear = 1.0 / cap.uses / pow(3.0, leveldiff);
else
result_wear = 0;
// The actual number of uses increases
// exponentially with leveldiff.
// If the levels are equal, real_uses equals cap.uses.
u32 real_uses = cap.uses * pow(3.0, leveldiff);
real_uses = MYMIN(real_uses, U16_MAX);
result_wear = calculateResultWear(real_uses, initial_wear);
result_main_group = groupname;
}
}
u16 wear_i = U16_MAX * result_wear;
return DigParams(result_diggable, result_time, wear_i, result_main_group);
return DigParams(result_diggable, result_time, result_wear, result_main_group);
}
HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp, float time_from_last_punch)
const ToolCapabilities *tp, float time_from_last_punch,
u16 initial_wear)
{
s16 damage = 0;
float result_wear = 0.0f;
@ -249,10 +316,12 @@ HitParams getHitParams(const ItemGroupList &armor_groups,
damage += damageGroup.second * punch_interval_multiplier * armor / 100.0;
}
if (tp->punch_attack_uses > 0)
result_wear = 1.0f / tp->punch_attack_uses * punch_interval_multiplier;
if (tp->punch_attack_uses > 0) {
result_wear = calculateResultWear(tp->punch_attack_uses, initial_wear);
result_wear *= punch_interval_multiplier;
}
u16 wear_i = U16_MAX * result_wear;
u32 wear_i = (u32) result_wear;
return {damage, wear_i};
}
@ -266,7 +335,8 @@ PunchDamageResult getPunchDamage(
const ItemGroupList &armor_groups,
const ToolCapabilities *toolcap,
const ItemStack *punchitem,
float time_from_last_punch
float time_from_last_punch,
u16 initial_wear
){
bool do_hit = true;
{
@ -286,7 +356,8 @@ PunchDamageResult getPunchDamage(
if(do_hit)
{
HitParams hitparams = getHitParams(armor_groups, toolcap,
time_from_last_punch);
time_from_last_punch,
punchitem->wear);
result.did_punch = true;
result.wear = hitparams.wear;
result.damage = hitparams.hp;

View file

@ -88,10 +88,10 @@ struct DigParams
// Digging time in seconds
float time;
// Caused wear
u16 wear;
u32 wear; // u32 because wear could be 65536 (single-use tool)
std::string main_group;
DigParams(bool a_diggable = false, float a_time = 0.0f, u16 a_wear = 0,
DigParams(bool a_diggable = false, float a_time = 0.0f, u32 a_wear = 0,
const std::string &a_main_group = ""):
diggable(a_diggable),
time(a_time),
@ -101,21 +101,24 @@ struct DigParams
};
DigParams getDigParams(const ItemGroupList &groups,
const ToolCapabilities *tp);
const ToolCapabilities *tp,
const u16 initial_wear = 0);
struct HitParams
{
s16 hp;
u16 wear;
// Caused wear
u32 wear; // u32 because wear could be 65536 (single-use weapon)
HitParams(s16 hp_ = 0, u16 wear_ = 0):
HitParams(s16 hp_ = 0, u32 wear_ = 0):
hp(hp_),
wear(wear_)
{}
};
HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp, float time_from_last_punch);
const ToolCapabilities *tp, float time_from_last_punch,
u16 initial_wear = 0);
HitParams getHitParams(const ItemGroupList &armor_groups,
const ToolCapabilities *tp);
@ -135,7 +138,8 @@ PunchDamageResult getPunchDamage(
const ItemGroupList &armor_groups,
const ToolCapabilities *toolcap,
const ItemStack *punchitem,
float time_from_last_punch
float time_from_last_punch,
u16 initial_wear = 0
);
f32 getToolRange(const ItemDefinition &def_selected, const ItemDefinition &def_hand);