Stop storing players as bare pointers - use unique_ptr instead

Should fix some potential memory leaks that were caught by static analysis
This commit is contained in:
2023-01-12 20:19:25 -05:00
parent 1a82f8ff8b
commit 8872f1aa25
12 changed files with 57 additions and 63 deletions

View File

@@ -299,8 +299,6 @@ bool load_party_v2(fs::path file_to_load, cUniverse& real_univ){
return false; return false;
} }
univ.party[i].readFrom(fin); univ.party[i].readFrom(fin);
// Do this to make sure the PC's internal party reference is correct
univ.party[i].join_party(univ.party);
} }
// Including stored PCs // Including stored PCs
@@ -309,9 +307,8 @@ bool load_party_v2(fs::path file_to_load, cUniverse& real_univ){
long next_uid; long next_uid;
while(fin >> next_uid) { while(fin >> next_uid) {
std::string fname = "save/pc~" + std::to_string(next_uid) + ".txt"; std::string fname = "save/pc~" + std::to_string(next_uid) + ".txt";
cPlayer* stored_pc = new cPlayer(univ.party); cPlayer* stored_pc = new cPlayer(no_party);
stored_pc->readFrom(partyIn.getFile(fname)); stored_pc->readFrom(partyIn.getFile(fname));
univ.stored_pcs[next_uid] = stored_pc->leave_party();
} }
} }
@@ -427,7 +424,7 @@ bool save_party(fs::path dest_file, const cUniverse& univ) {
// And stored PCs // And stored PCs
if(univ.stored_pcs.size()) { if(univ.stored_pcs.size()) {
std::ostream& fout = partyOut.newFile("save/stored_pcs.txt"); std::ostream& fout = partyOut.newFile("save/stored_pcs.txt");
for(auto p : univ.stored_pcs) { for(const auto& p : univ.stored_pcs) {
std::string fname = "save/pc~" + std::to_string(p.first) + ".txt"; std::string fname = "save/pc~" + std::to_string(p.first) + ".txt";
p.second->writeTo(partyOut.newFile(fname)); p.second->writeTo(partyOut.newFile(fname));
fout << p.first << '\n'; fout << p.first << '\n';

View File

@@ -581,7 +581,7 @@ static void readShopFromXml(ticpp::Element& data, cShop& shop) {
entry->GetText(&num); entry->GetText(&num);
shop.addItem(num, dummy_item, amount, chance); shop.addItem(num, dummy_item, amount, chance);
} else if(type == "special") { } else if(type == "special") {
int amount, node, cost = 0, icon; int amount = 0, node = 0, cost = 0, icon = 0;
std::string title, descr; std::string title, descr;
std::set<std::string> reqs = {"quantity", "node", "icon", "name", "description"}; std::set<std::string> reqs = {"quantity", "node", "icon", "name", "description"};
Iterator<Element> attr; Iterator<Element> attr;
@@ -609,7 +609,7 @@ static void readShopFromXml(ticpp::Element& data, cShop& shop) {
throw xMissingElem("special", *reqs.begin(), entry->Row(), entry->Column(), fname); throw xMissingElem("special", *reqs.begin(), entry->Row(), entry->Column(), fname);
shop.addSpecial(title, descr, icon, node, cost, amount); shop.addSpecial(title, descr, icon, node, cost, amount);
} else { } else {
eShopItemType itype; eShopItemType itype{};
int n = 0; int n = 0;
if(type == "mage-spell") { if(type == "mage-spell") {
itype = eShopItemType::MAGE_SPELL; itype = eShopItemType::MAGE_SPELL;

View File

@@ -2705,7 +2705,7 @@ void affect_spec(const runtime_state& ctx) {
for(auto& p : univ.stored_pcs) { for(auto& p : univ.stored_pcs) {
if(p.first == pc && p.second->unique_id == pc) { if(p.first == pc && p.second->unique_id == pc) {
can_pick = true; can_pick = true;
found = p.second; found = p.second.get();
break; break;
} }
} }
@@ -3174,7 +3174,7 @@ void affect_spec(const runtime_state& ctx) {
univ.party.stuff_done[spec.sd1][spec.sd2] = univ.party[pc_num].unique_id - 1000; univ.party.stuff_done[spec.sd1][spec.sd2] = univ.party[pc_num].unique_id - 1000;
if(spec.ex1a == 1) break; if(spec.ex1a == 1) break;
who->main_status += eMainStatus::SPLIT; who->main_status += eMainStatus::SPLIT;
univ.stored_pcs[who->unique_id] = who->leave_party(); univ.stored_pcs[who->unique_id] = univ.party.remove_pc(pc_num);
univ.party.new_pc(pc_num); univ.party.new_pc(pc_num);
} }
break; break;
@@ -3191,7 +3191,7 @@ void affect_spec(const runtime_state& ctx) {
check_mess = false; check_mess = false;
break; break;
} }
univ.party.replace_pc(pc_num, univ.stored_pcs[spec.ex1a]); univ.party.replace_pc(pc_num, std::move(univ.stored_pcs[spec.ex1a]));
ctx.cur_target = &univ.get_target(pc_num); ctx.cur_target = &univ.get_target(pc_num);
univ.party[pc_num].main_status -= eMainStatus::SPLIT; univ.party[pc_num].main_status -= eMainStatus::SPLIT;
univ.stored_pcs.erase(spec.ex1a); univ.stored_pcs.erase(spec.ex1a);

View File

@@ -31,7 +31,7 @@ enum eFont {
struct TextStyle { struct TextStyle {
bool italic = false, underline = false; bool italic = false, underline = false;
eFont font = FONT_BOLD; eFont font = FONT_BOLD;
int pointSize = 10, lineHeight; int pointSize = 10, lineHeight = 10;
sf::Color colour; sf::Color colour;
TextStyle() : colour(sf::Color::Black) {} TextStyle() : colour(sf::Color::Black) {}
void applyTo(sf::Text& text); void applyTo(sf::Text& text);

View File

@@ -166,6 +166,7 @@ static bool pick_race_select_led(cDialog& me, std::string item_hit, bool, const
case 1: race = eRace::NEPHIL; break; case 1: race = eRace::NEPHIL; break;
case 2: race = eRace::SLITH; break; case 2: race = eRace::SLITH; break;
case 3: race = eRace::VAHNATAI; break; case 3: race = eRace::VAHNATAI; break;
default: return false;
} }
if(store_trait_mode == 0) if(store_trait_mode == 0)
pc->race = race; pc->race = race;

View File

@@ -40,18 +40,11 @@ cParty::cParty(ePartyPreset party_preset) {
for(int i = 0; i < 10; i++) for(int i = 0; i < 10; i++)
out_c[i].exists = false; out_c[i].exists = false;
for(int i = 0; i < 6; i++) for(int i = 0; i < 6; i++)
adven[i] = new cPlayer(*this, party_preset, i); adven[i].reset(new cPlayer(*this, party_preset, i));
for(auto& monst : imprisoned_monst) for(auto& monst : imprisoned_monst)
monst = 0; monst = 0;
} }
cParty::~cParty() {
for(int i = 0; i < 6; i++) {
delete adven[i];
adven[i] = nullptr;
}
}
cParty::cParty(const cParty& other) cParty::cParty(const cParty& other)
: iLiving(other) : iLiving(other)
, next_pc_id(other.next_pc_id) , next_pc_id(other.next_pc_id)
@@ -109,8 +102,7 @@ cParty::cParty(const cParty& other)
memcpy(stuff_done, other.stuff_done, sizeof(stuff_done)); memcpy(stuff_done, other.stuff_done, sizeof(stuff_done));
memcpy(setup, other.setup, sizeof(setup)); memcpy(setup, other.setup, sizeof(setup));
for(int i = 0; i < 6; i++) { for(int i = 0; i < 6; i++) {
adven[i] = new cPlayer(*other.adven[i]); adven[i].reset(new cPlayer(*this, *other.adven[i]));
adven[i]->join_party(*this);
} }
} }
@@ -184,12 +176,8 @@ void cParty::swap(cParty& other) {
memcpy(temp_setup, setup, sizeof(setup)); memcpy(temp_setup, setup, sizeof(setup));
memcpy(setup, other.setup, sizeof(setup)); memcpy(setup, other.setup, sizeof(setup));
memcpy(other.setup, temp_setup, sizeof(setup)); memcpy(other.setup, temp_setup, sizeof(setup));
// Fixup the references of PCs to their party
for(size_t i = 0; i < adven.size(); i++) { for(size_t i = 0; i < adven.size(); i++) {
adven[i]->join_party(*this); std::swap(adven[i], other.adven[i]);
}
for(size_t i = 0; i < other.adven.size(); i++) {
other.adven[i]->join_party(other);
} }
} }
@@ -351,22 +339,27 @@ void cParty::cEncNote::import_legacy(int16_t(& old)[2], const cScenario& scenari
void cParty::import_legacy(legacy::pc_record_type(& old)[6]) { void cParty::import_legacy(legacy::pc_record_type(& old)[6]) {
for(int i = 0; i < 6; i++) { for(int i = 0; i < 6; i++) {
delete adven[i]; adven[i].reset(new cPlayer(*this));
adven[i] = new cPlayer(*this);
adven[i]->import_legacy(old[i]); adven[i]->import_legacy(old[i]);
} }
} }
std::unique_ptr<cPlayer> cParty::remove_pc(size_t spot) {
if(spot >= 6) return nullptr;
adven[spot]->party = nullptr;
return std::move(adven[spot]);
}
void cParty::new_pc(size_t spot) { void cParty::new_pc(size_t spot) {
replace_pc(spot, new cPlayer(*this)); std::unique_ptr<cPlayer> new_pc{new cPlayer(*this)};
replace_pc(spot, std::move(new_pc));
adven[spot]->main_status = eMainStatus::ALIVE; adven[spot]->main_status = eMainStatus::ALIVE;
} }
void cParty::replace_pc(size_t spot, cPlayer* with) { void cParty::replace_pc(size_t spot, std::unique_ptr<cPlayer> with) {
if(spot < 6 && with != nullptr) { if(spot < 6 && with != nullptr) {
with->join_party(*this); with->party = this;
delete adven[spot]; adven[spot] = std::move(with);
adven[spot] = with;
} }
} }

View File

@@ -123,7 +123,7 @@ public:
long long total_m_killed, total_dam_done, total_xp_gained, total_dam_taken; long long total_m_killed, total_dam_done, total_xp_gained, total_dam_taken;
std::string scen_name; std::string scen_name;
private: private:
std::array<cPlayer*,6> adven; std::array<std::unique_ptr<cPlayer>,6> adven;
public: public:
unsigned short setup[4][64][64]; // formerly setup_save_type unsigned short setup[4][64][64]; // formerly setup_save_type
std::array<std::vector<cItem>,3> stored_items; // formerly stored_items_list_type std::array<std::vector<cItem>,3> stored_items; // formerly stored_items_list_type
@@ -178,8 +178,9 @@ public:
int get_level() const; int get_level() const;
int calc_day() const; int calc_day() const;
std::unique_ptr<cPlayer> remove_pc(size_t spot);
void new_pc(size_t spot); void new_pc(size_t spot);
void replace_pc(size_t spot, cPlayer* with); void replace_pc(size_t spot, std::unique_ptr<cPlayer> with);
size_t free_space(); size_t free_space();
size_t count(eMainStatus type = eMainStatus::ALIVE); size_t count(eMainStatus type = eMainStatus::ALIVE);
void void_pcs(); void void_pcs();
@@ -219,7 +220,6 @@ public:
typedef std::vector<cJournal>::iterator journalIter; typedef std::vector<cJournal>::iterator journalIter;
typedef std::vector<cConvers>::iterator talkIter; typedef std::vector<cConvers>::iterator talkIter;
cParty(ePartyPreset party_preset = PARTY_DEFAULT); cParty(ePartyPreset party_preset = PARTY_DEFAULT);
~cParty();
// Copy-and-swap // Copy-and-swap
void swap(cParty& other); void swap(cParty& other);
cParty(const cParty& other); cParty(const cParty& other);

View File

@@ -943,7 +943,12 @@ void cPlayer::finish_create() {
} }
} }
cPlayer::cPlayer(cParty& party) : party(&party), weap_poisoned(*this) { cPlayer::cPlayer(cParty& party) : cPlayer(no_party) {
this->party = &party;
unique_id = party.next_pc_id++;
}
cPlayer::cPlayer(no_party_t) : weap_poisoned(*this) {
main_status = eMainStatus::ABSENT; main_status = eMainStatus::ABSENT;
name = "\n"; name = "\n";
@@ -967,8 +972,6 @@ cPlayer::cPlayer(cParty& party) : party(&party), weap_poisoned(*this) {
race = eRace::HUMAN; race = eRace::HUMAN;
direction = DIR_N; direction = DIR_N;
combat_pos = {-1,-1}; combat_pos = {-1,-1};
unique_id = party.next_pc_id++;
} }
cPlayer::cPlayer(cParty& party,ePartyPreset key,short slot) : cPlayer(party) { cPlayer::cPlayer(cParty& party,ePartyPreset key,short slot) : cPlayer(party) {
@@ -1115,9 +1118,13 @@ cPlayer::cPlayer(cParty& party,ePartyPreset key,short slot) : cPlayer(party) {
} }
} }
cPlayer::cPlayer(const cPlayer& other) cPlayer::cPlayer(cParty& party, const cPlayer& other) : cPlayer(no_party, other) {
this->party = &party;
unique_id = party.next_pc_id++;
}
cPlayer::cPlayer(no_party_t, const cPlayer& other)
: iLiving(other) : iLiving(other)
, party(other.party)
, main_status(other.main_status) , main_status(other.main_status)
, name(other.name) , name(other.name)
, skills(other.skills) , skills(other.skills)
@@ -1136,7 +1143,6 @@ cPlayer::cPlayer(const cPlayer& other)
, weap_poisoned(*this, other.weap_poisoned.slot) , weap_poisoned(*this, other.weap_poisoned.slot)
, traits(other.traits) , traits(other.traits)
, race(other.race) , race(other.race)
, unique_id(party->next_pc_id++)
, last_cast(other.last_cast) , last_cast(other.last_cast)
, combat_pos(other.combat_pos) , combat_pos(other.combat_pos)
, parry(other.parry) , parry(other.parry)
@@ -1148,7 +1154,7 @@ cPlayer::cPlayer(cPlayer&& other) : weap_poisoned(*this, other.weap_poisoned.slo
} }
void cPlayer::swap(cPlayer& other) { void cPlayer::swap(cPlayer& other) {
std::swap(party, other.party); // Don't swap the party reference!
std::swap(main_status, other.main_status); std::swap(main_status, other.main_status);
std::swap(name, other.name); std::swap(name, other.name);
std::swap(skills, other.skills); std::swap(skills, other.skills);

View File

@@ -26,6 +26,8 @@
namespace legacy { struct pc_record_type; }; namespace legacy { struct pc_record_type; };
static struct no_party_t {} no_party;
enum class eBuyStatus {OK, NO_SPACE, NEED_GOLD, TOO_HEAVY, HAVE_LOTS}; enum class eBuyStatus {OK, NO_SPACE, NEED_GOLD, TOO_HEAVY, HAVE_LOTS};
enum ePartyPreset {PARTY_BLANK, PARTY_DEFAULT, PARTY_DEBUG}; enum ePartyPreset {PARTY_BLANK, PARTY_DEFAULT, PARTY_DEBUG};
@@ -67,9 +69,13 @@ inline bool operator!=(const cInvenSlot& a, const cInvenSlot& b) {
} }
class cPlayer : public iLiving { class cPlayer : public iLiving {
cParty* party; cParty* party = nullptr;
template<typename Fcn> template<typename Fcn>
cInvenSlot find_item_matching(Fcn fcn); cInvenSlot find_item_matching(Fcn fcn);
// Allow the party to overwrite the PC's party reference
// It only does this (and should ever do this) in remove_pc and replace_pc!
// Sadly there is no good way to friend just those two functions.
friend class cParty;
static const int INVENTORY_SIZE = 24; static const int INVENTORY_SIZE = 24;
public: public:
// A nice convenient bitset with just the low 30 bits set, for initializing spells // A nice convenient bitset with just the low 30 bits set, for initializing spells
@@ -166,19 +172,19 @@ public:
short stat_adj(eSkill skill) const; short stat_adj(eSkill skill) const;
eBuyStatus ok_to_buy(short cost,cItem item) const; eBuyStatus ok_to_buy(short cost,cItem item) const;
void join_party(cParty& p) {party = &p;}
cPlayer* leave_party() {party = nullptr; return this;}
void import_legacy(legacy::pc_record_type old); void import_legacy(legacy::pc_record_type old);
cPlayer(no_party_t);
cPlayer(cParty& party); cPlayer(cParty& party);
cPlayer(cParty& party,ePartyPreset key,short slot); cPlayer(cParty& party,ePartyPreset key,short slot);
cPlayer(no_party_t, const cPlayer& other);
cPlayer(cParty& party, const cPlayer& other);
short get_tnl(); short get_tnl();
void writeTo(std::ostream& file) const; void writeTo(std::ostream& file) const;
void readFrom(std::istream& file); void readFrom(std::istream& file);
virtual ~cPlayer() = default; virtual ~cPlayer() = default;
// Copy-and-swap // Copy-and-swap
void swap(cPlayer& other); void swap(cPlayer& other);
cPlayer(const cPlayer& other); cPlayer(const cPlayer& other) = delete;
cPlayer(cPlayer&& other); cPlayer(cPlayer&& other);
// For now, not assignable because of an issue of how to handle the unique_id // For now, not assignable because of an issue of how to handle the unique_id
cPlayer& operator=(cPlayer other) = delete; cPlayer& operator=(cPlayer other) = delete;

View File

@@ -912,7 +912,6 @@ cUniverse::cUniverse(const cUniverse& other)
, cur_pc(other.cur_pc) , cur_pc(other.cur_pc)
, scenario(other.scenario) , scenario(other.scenario)
, party(other.party) , party(other.party)
, stored_pcs(other.stored_pcs)
, town(*this) , town(*this)
, out(*this) , out(*this)
, file(other.file) , file(other.file)
@@ -920,11 +919,12 @@ cUniverse::cUniverse(const cUniverse& other)
, ghost_mode(other.ghost_mode) , ghost_mode(other.ghost_mode)
, node_step_through(other.node_step_through) , node_step_through(other.node_step_through)
{ {
for(auto& p : stored_pcs) {
p.second = new cPlayer(*p.second);
}
town.copy(other.town); town.copy(other.town);
out.copy(other.out); out.copy(other.out);
for(const auto& pc : other.stored_pcs) {
std::unique_ptr<cPlayer> clone(new cPlayer(no_party, *pc.second));
stored_pcs.emplace(pc.first, std::move(clone));
}
} }
cUniverse::cUniverse(cUniverse&& other) : town(*this), out(*this) { cUniverse::cUniverse(cUniverse&& other) : town(*this), out(*this) {
@@ -1305,13 +1305,7 @@ short cUniverse::difficulty_adjust() const {
return adj; return adj;
} }
cUniverse::~cUniverse() {
clear_stored_pcs();
}
void cUniverse::clear_stored_pcs() { void cUniverse::clear_stored_pcs() {
for(auto& p : stored_pcs)
delete p.second;
stored_pcs.clear(); stored_pcs.clear();
} }

View File

@@ -198,7 +198,7 @@ public:
cScenario scenario; cScenario scenario;
cParty party; cParty party;
std::map<long,cPlayer*> stored_pcs; std::map<long, std::unique_ptr<cPlayer>> stored_pcs;
cCurTown town; cCurTown town;
cCurOut out; cCurOut out;
fs::path file; fs::path file;
@@ -214,7 +214,6 @@ public:
void generate_job_bank(int which, job_bank_t& bank); void generate_job_bank(int which, job_bank_t& bank);
short difficulty_adjust() const; short difficulty_adjust() const;
explicit cUniverse(ePartyPreset party_type = PARTY_DEFAULT); explicit cUniverse(ePartyPreset party_type = PARTY_DEFAULT);
~cUniverse();
// Copy-and-swap // Copy-and-swap
void swap(cUniverse& other); void swap(cUniverse& other);
cUniverse(const cUniverse& other); cUniverse(const cUniverse& other);

View File

@@ -16,9 +16,7 @@ using namespace std;
TEST_CASE("Loading player character from file") { TEST_CASE("Loading player character from file") {
ifstream fin; ifstream fin;
fin.exceptions(ios::badbit); fin.exceptions(ios::badbit);
cParty party; cPlayer pc(no_party);
cPlayer pc(party);
pc.leave_party();
// Fill in some junk data // Fill in some junk data
pc.cur_sp = 27; pc.cur_sp = 27;
pc.max_sp = 38; pc.max_sp = 38;