Compare commits

...

5 Commits

Author SHA1 Message Date
gsemaj
5671adbd74
Make skill result size check an assertion 2023-07-23 10:46:53 -04:00
gsemaj
550b26db39
Oops 2023-07-23 10:42:07 -04:00
gsemaj
68be9cc381
Change SkillResult size validation
Since leech uses trailing structs of two different sizes, just use the max SkillResult size in validation/zeroing and then check for overflow in a couple extra places
2023-07-23 10:31:40 -04:00
gsemaj
ef33a182d1
Reorder abilities to match client handling 2023-07-23 10:15:00 -04:00
gsemaj
9bd8eabed7
Don't add buff if player dead 2023-07-23 10:13:22 -04:00
3 changed files with 42 additions and 29 deletions

View File

@ -5,8 +5,6 @@
#include "Buffs.hpp" #include "Buffs.hpp"
#include "Nanos.hpp" #include "Nanos.hpp"
#include <assert.h>
using namespace Abilities; using namespace Abilities;
std::map<int32_t, SkillData> Abilities::SkillTable; std::map<int32_t, SkillData> Abilities::SkillTable;
@ -56,6 +54,11 @@ static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombat
return SkillResult(sizeof(sSkillResult_Damage_N_Debuff), &result); return SkillResult(sizeof(sSkillResult_Damage_N_Debuff), &result);
} }
static SkillResult handleSkillLeech(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
// TODO abilities
return SkillResult();
}
static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) { static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
BuffStack passiveBuff = { BuffStack passiveBuff = {
skill->drainType == SkillDrainType::PASSIVE ? 1 : skill->durationTime[power], // ticks skill->drainType == SkillDrainType::PASSIVE ? 1 : skill->durationTime[power], // ticks
@ -142,54 +145,59 @@ static SkillResult handleSkillResurrect(SkillData* skill, int power, ICombatant*
#pragma endregion #pragma endregion
static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombatant* src, std::vector<ICombatant*> targets) { static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombatant* src, std::vector<ICombatant*> targets) {
size_t resultSize = 0;
SkillResult (*skillHandler)(SkillData*, int, ICombatant*, ICombatant*) = nullptr; SkillResult (*skillHandler)(SkillData*, int, ICombatant*, ICombatant*) = nullptr;
std::vector<SkillResult> results; std::vector<SkillResult> results;
switch(skill->skillType) switch(skill->skillType)
{ {
case SkillType::DAMAGE: case SkillType::DAMAGE:
resultSize = sizeof(sSkillResult_Damage);
skillHandler = handleSkillDamage; skillHandler = handleSkillDamage;
break; break;
case SkillType::HEAL_HP: case SkillType::HEAL_HP:
case SkillType::RETURNHOMEHEAL: case SkillType::RETURNHOMEHEAL:
resultSize = sizeof(sSkillResult_Heal_HP);
skillHandler = handleSkillHealHP; skillHandler = handleSkillHealHP;
break; break;
case SkillType::KNOCKDOWN:
case SkillType::SLEEP:
case SkillType::SNARE:
case SkillType::STUN:
skillHandler = handleSkillDamageNDebuff;
break;
case SkillType::JUMP: case SkillType::JUMP:
case SkillType::RUN: case SkillType::RUN:
case SkillType::FREEDOM: case SkillType::STEALTH:
case SkillType::PHOENIX:
case SkillType::INVULNERABLE:
case SkillType::MINIMAPENEMY: case SkillType::MINIMAPENEMY:
case SkillType::MINIMAPTRESURE: case SkillType::MINIMAPTRESURE:
case SkillType::NANOSTIMPAK: case SkillType::PHOENIX:
case SkillType::PROTECTBATTERY: case SkillType::PROTECTBATTERY:
case SkillType::PROTECTINFECTION: case SkillType::PROTECTINFECTION:
case SkillType::REWARDBLOB: case SkillType::REWARDBLOB:
case SkillType::REWARDCASH: case SkillType::REWARDCASH:
// case SkillType::INFECTIONDAMAGE:
case SkillType::FREEDOM:
case SkillType::BOUNDINGBALL:
case SkillType::INVULNERABLE:
case SkillType::STAMINA_SELF: case SkillType::STAMINA_SELF:
case SkillType::STEALTH: case SkillType::NANOSTIMPAK:
resultSize = sizeof(sSkillResult_Buff); case SkillType::BUFFHEAL:
skillHandler = handleSkillBuff; skillHandler = handleSkillBuff;
break; break;
case SkillType::BATTERYDRAIN: case SkillType::BLOODSUCKING:
resultSize = sizeof(sSkillResult_BatteryDrain); skillHandler = handleSkillLeech;
skillHandler = handleSkillBatteryDrain;
break;
case SkillType::RECALL: // still soft lock
case SkillType::RECALL_GROUP: // works for player who uses it
resultSize = sizeof(sSkillResult_Move);
skillHandler = handleSkillMove;
break;
case SkillType::PHOENIX_GROUP: // broken
resultSize = sizeof(sSkillResult_Resurrect);
skillHandler = handleSkillResurrect;
break; break;
case SkillType::RETROROCKET_SELF: case SkillType::RETROROCKET_SELF:
// no-op // no-op
return results; return results;
case SkillType::PHOENIX_GROUP:
skillHandler = handleSkillResurrect;
break;
case SkillType::RECALL:
case SkillType::RECALL_GROUP:
skillHandler = handleSkillMove;
break;
case SkillType::BATTERYDRAIN:
skillHandler = handleSkillBatteryDrain;
break;
default: default:
std::cout << "[WARN] Unhandled skill type " << (int)skill->skillType << std::endl; std::cout << "[WARN] Unhandled skill type " << (int)skill->skillType << std::endl;
return results; return results;
@ -199,7 +207,7 @@ static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombat
assert(target != nullptr); assert(target != nullptr);
SkillResult result = skillHandler(skill, power, src != nullptr ? src : target, target); SkillResult result = skillHandler(skill, power, src != nullptr ? src : target, target);
if(result.size == 0) continue; // skill not applicable if(result.size == 0) continue; // skill not applicable
if(result.size != resultSize) { if(result.size > MAX_SKILLRESULT_SIZE) {
std::cout << "[WARN] bad skill result size for " << (int)skill->skillType << " from " << (void*)handleSkillBuff << std::endl; std::cout << "[WARN] bad skill result size for " << (int)skill->skillType << " from " << (void*)handleSkillBuff << std::endl;
continue; continue;
} }
@ -232,7 +240,7 @@ void Abilities::useNanoSkill(CNSocket* sock, SkillData* skill, sNano& nano, std:
std::vector<SkillResult> results = handleSkill(skill, boost, plr, affected); std::vector<SkillResult> results = handleSkill(skill, boost, plr, affected);
if(results.empty()) return; // no effect; no need for confirmation packets if(results.empty()) return; // no effect; no need for confirmation packets
size_t resultSize = results.back().size; // guaranteed to be the same for every item size_t resultSize = MAX_SKILLRESULT_SIZE; // lazy
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), resultSize)) { if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), resultSize)) {
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n"; std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n";
return; return;

View File

@ -7,18 +7,19 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include <assert.h>
constexpr size_t MAX_SKILLRESULT_SIZE = sizeof(sSkillResult_BatteryDrain); constexpr size_t MAX_SKILLRESULT_SIZE = sizeof(sSkillResult_BatteryDrain);
enum class SkillType { enum class SkillType {
DAMAGE = 1, DAMAGE = 1,
HEAL_HP = 2, HEAL_HP = 2,
KNOCKDOWN = 3, KNOCKDOWN = 3, // dnd
SLEEP = 4, SLEEP = 4, // dnd
SNARE = 5, SNARE = 5, // dnd
HEAL_STAMINA = 6, HEAL_STAMINA = 6,
STAMINA_SELF = 7, STAMINA_SELF = 7,
STUN = 8, STUN = 8, // dnd
WEAPONSLOW = 9, WEAPONSLOW = 9,
JUMP = 10, JUMP = 10,
RUN = 11, RUN = 11,
@ -75,6 +76,7 @@ struct SkillResult {
size_t size; size_t size;
uint8_t payload[MAX_SKILLRESULT_SIZE]; uint8_t payload[MAX_SKILLRESULT_SIZE];
SkillResult(size_t len, void* dat) { SkillResult(size_t len, void* dat) {
assert(len <= MAX_SKILLRESULT_SIZE);
size = len; size = len;
memcpy(payload, dat, len); memcpy(payload, dat, len);
} }

View File

@ -21,6 +21,9 @@ std::map<int32_t, std::map<int8_t, Bullet>> Combat::Bullets;
#pragma region Player #pragma region Player
bool Player::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) { bool Player::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) {
if(!isAlive())
return false;
EntityRef self = PlayerManager::getSockFromID(iID); EntityRef self = PlayerManager::getSockFromID(iID);
if(!hasBuff(buffId)) { if(!hasBuff(buffId)) {