hyperrogue/savemem.cpp

311 lines
8.7 KiB
C++
Raw Permalink Normal View History

// Hyperbolic Rogue -- memory saving
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
/** \file memory.cpp
* \brief memory saving: memory saving mode, memory warnings, etc.
*/
#include "hyper.h"
namespace hr {
2019-08-09 19:18:13 +00:00
#if HDR
static constexpr int PSEUDOKEY_MEMORY = 16397;
2019-08-09 19:18:13 +00:00
#endif
EX bool memory_saving_mode = true;
2018-01-26 01:10:40 +00:00
EX bool show_memory_warning = true;
EX bool ignored_memory_warning;
2019-06-06 17:37:17 +00:00
static constexpr int LIM = 150;
2018-01-26 01:10:40 +00:00
EX heptagon *last_cleared;
2018-01-26 01:10:40 +00:00
EX void destroycellcontents(cell *c) {
2018-01-26 01:10:40 +00:00
c->land = laMemory;
c->wall = waChasm;
c->item = itNone;
if(!isMultitile(c->monst) && c->monst != moPair)
c->monst = moNone;
}
void degrade(cell *c) {
c->mpdist++;
forCellEx(c2, c)
if(c2->mpdist < c->mpdist - 1)
degrade(c2);
destroycellcontents(c);
}
EX vector<cell*> removed_cells;
2018-01-26 01:10:40 +00:00
void slow_delete_cell(cell *c) {
while(c->mpdist < BARLEV)
degrade(c);
for(int i=0; i<c->type; i++)
if(c->move(i))
c->move(i)->move(c->c.spin(i)) = NULL;
2018-01-26 01:10:40 +00:00
removed_cells.push_back(c);
destroy_cell(c);
2018-01-26 01:10:40 +00:00
}
void delete_heptagon(heptagon *h2) {
cell *c = h2->c7;
if(BITRUNCATED) {
2018-01-26 01:10:40 +00:00
for(int i=0; i<c->type; i++)
if(c->move(i))
slow_delete_cell(c->move(i));
2018-01-26 01:10:40 +00:00
}
slow_delete_cell(c);
for(int i=0; i<S7; i++)
if(h2->move(i))
h2->move(i)->move(h2->c.spin(i)) = NULL;
tailored_delete(h2);
2018-01-26 01:10:40 +00:00
}
void recursive_delete(heptagon *h, int i) {
heptagon *h2 = h->move(i);
2018-02-01 02:06:45 +00:00
{ for(int i=1; i<S7; i++)
if(h2->move(i) && h2->move(i)->move(0) == h2)
2018-02-01 02:06:45 +00:00
recursive_delete(h2, i); }
2018-01-26 01:10:40 +00:00
if(h2->alt && h2->alt->alt == h2->alt) {
2019-05-12 23:57:40 +00:00
DEBB(DF_MEMORY, ("destroying alternate map ", h2->alt));
2018-01-26 01:10:40 +00:00
for(hrmap *& hm: allmaps) {
if(hm->getOrigin() == h2->alt) {
2018-01-26 01:10:40 +00:00
delete hm;
hm = allmaps.back();
allmaps.pop_back();
2019-05-12 23:57:40 +00:00
DEBB(DF_MEMORY, ("map found (", isize(allmaps), " altmaps total)"));
2018-01-26 01:10:40 +00:00
break;
}
}
}
if(h2->alt) {
h2->alt->cdata = NULL;
}
delete_heptagon(h2);
h->move(i) = NULL;
2018-01-26 01:10:40 +00:00
}
bool unsafeLand(cell *c) {
return
isCyclic(c->land) || isGravityLand(c->land) || isHaunted(c->land) ||
among(c->land, laCaribbean, laOcean, laGraveyard, laPrincessQuest);
}
EX void save_memory() {
if(quotient || !hyperbolic || NONSTDVAR) return;
2018-01-26 01:10:40 +00:00
if(!memory_saving_mode) return;
if(unsafeLand(cwt.at)) return;
int d = celldist(cwt.at);
2018-01-26 01:10:40 +00:00
if(d < LIM+10) return;
heptagon *at = cwt.at->master;
2018-01-26 01:10:40 +00:00
heptagon *orig = currentmap->gamestart()->master;
if(recallCell.at) {
if(unsafeLand(recallCell.at)) return;
heptagon *at2 = recallCell.at->master;
2018-01-26 01:10:40 +00:00
int t = 0;
while(at != at2) {
t++; if(t > 10000) return;
if(celldist(at->c7) > celldist(at2->c7))
at = at->move(0);
2018-01-26 01:10:40 +00:00
else
at2 = at2->move(0);
2018-01-26 01:10:40 +00:00
}
}
while(celldist(at->c7) > d-LIM && at != orig) at = at->move(0);
// make sure it is not the same altmap
auto atalt = at->alt; if(atalt) atalt = atalt->alt;
while(atalt && at != orig && at->alt && at->alt->alt == atalt) at = at->move(0);
2018-01-26 01:10:40 +00:00
// go back to such a point X that all the heptagons adjacent to the current 'at'
// are the children of X. This X becomes the new 'at'
if(true) {
2024-01-11 16:22:20 +00:00
heptagon *allh[FULL_EDGE+1];
2018-01-26 01:10:40 +00:00
int hcount = 0;
allh[hcount++] = at;
for(int j=0; j<S7; j++)
if(allh[0]->move(j))
allh[hcount++] = at->move(j);
2018-01-26 01:10:40 +00:00
int deuniq_steps = 0;
int i = 1;
while(i < hcount) {
if(allh[i] == allh[0])
allh[i] = allh[hcount-1], hcount--;
else if(celldist(allh[i]->c7) > celldist(allh[0]->c7))
allh[i] = allh[i]->move(0);
2018-01-26 01:10:40 +00:00
else {
if(allh[0] == orig) return;
allh[0] = allh[0]->move(0);
2018-01-26 01:10:40 +00:00
i = 1;
deuniq_steps++;
if(deuniq_steps == 10) return;
}
}
at = allh[0];
}
if(last_cleared && celldist(at->c7) < celldist(last_cleared->c7))
return;
2019-05-12 23:57:40 +00:00
DEBB(DF_MEMORY, ("celldist = ", make_pair(celldist(cwt.at), celldist(at->c7))));
2018-01-26 01:10:40 +00:00
heptagon *at1 = at;
while(at != last_cleared && at != orig) {
heptagon *atn = at;
at = at->move(0);
2018-01-26 01:10:40 +00:00
for(int i=1; i<S7; i++)
if(at->move(i) && at->move(i) != atn)
2018-01-26 01:10:40 +00:00
recursive_delete(at, i);
}
last_cleared = at1;
2019-05-12 23:57:40 +00:00
DEBB(DF_MEMORY, ("current cellcount = ", cellcount));
2018-01-26 01:10:40 +00:00
sort(removed_cells.begin(), removed_cells.end());
callhooks(hooks_removecells);
removed_cells.clear();
}
2019-08-09 20:07:03 +00:00
EX purehookset hooks_removecells;
2018-01-26 01:10:40 +00:00
EX bool is_cell_removed(cell *c) {
2018-01-26 01:10:40 +00:00
return binary_search(removed_cells.begin(), removed_cells.end(), c);
}
EX void set_if_removed(cell*& c, cell *val) {
2018-01-26 01:10:40 +00:00
if(is_cell_removed(c)) c = val;
}
2019-06-06 17:37:17 +00:00
typedef array<char, 1048576> reserve_block;
EX int reserve_count = 0;
EX int reserve_limit = 128;
2019-06-06 17:37:17 +00:00
const int max_reserve = 4096;
array<reserve_block*, max_reserve> reserve;
std::new_handler default_handler;
EX purehookset hooks_clear_cache;
2019-06-06 17:37:17 +00:00
void reserve_handler() {
if(reserve_count) {
reserve_count--;
delete reserve[reserve_count];
}
if(reserve_count < 32) callhooks(hooks_clear_cache);
if(!reserve_count) std::set_new_handler(default_handler);
}
EX void apply_memory_reserve() {
#if CAP_MEMORY_RESERVE
2019-06-06 17:37:17 +00:00
if(reserve_count > 0) std::set_new_handler(default_handler);
if(reserve_limit > max_reserve) reserve_limit = max_reserve;
if(reserve_limit < 0) reserve_limit = 0;
while(reserve_count > reserve_limit) { reserve_count--; delete reserve[reserve_count]; }
try {
while(reserve_count < reserve_limit) {
reserve[reserve_count] = new reserve_block;
/* only if successful */
reserve_count++;
}
}
catch(std::bad_alloc&) {}
#if ISWINDOWS
// no get_new_handler on this compiler...
default_handler = [] { throw std::bad_alloc(); };
#else
2019-06-06 17:37:17 +00:00
default_handler = std::get_new_handler();
#endif
2019-06-06 17:37:17 +00:00
if(reserve_count > 0) std::set_new_handler(reserve_handler);
#endif
2019-06-06 17:37:17 +00:00
}
EX void memory_for_lib() {
2019-06-06 17:37:17 +00:00
if(reserve_count) { reserve_count--; delete reserve[reserve_count]; }
}
EX void show_memory_menu() {
gamescreen();
2019-06-06 17:37:17 +00:00
dialog::init(XLAT("memory"));
dialog::addHelp(XLAT(
"HyperRogue takes place in a world that is larger than anything Euclidean. "
"Unfortunately, in some cases running it on an Euclidean computer might be "
"a problem -- the computer could simply run out of memory. Some lands (such as the Ocean or the Brown Islands) "
"may use up memory very fast!\n\n"
));
if(sizeof(void*) <= 4)
dialog::addHelp(XLAT(
"You are playing a 32-bit HyperRogue executable, which can only use 4GB of memory.\n\n"));
dialog::addHelp(XLAT(
"Although you are extremely unlikely to return to a place you have already been to, "
"the game never forgets these areas, unless you start a new game, use an Orb of "
2019-06-17 05:22:41 +00:00
"Safety (found in Land of Eternal Motion, the Prairie, and the Ocean), or activate the memory "
2019-06-06 17:37:17 +00:00
"saving mode, which tries to intelligently predict which cells you will never find "
"again and can be safely forgotten.\n\n")
);
if(cheater) dialog::addSelItem(XLAT("cells in memory"), its(cellcount) + "+" + its(heptacount), 0);
dialog::addBoolItem(XLAT("memory saving mode"), memory_saving_mode, 'f');
dialog::add_action([] { memory_saving_mode = !memory_saving_mode; if(memory_saving_mode) save_memory(), apply_memory_reserve(); });
dialog::addBoolItem_action(XLAT("show memory warnings"), show_memory_warning, 'w');
#if CAP_MEMORY_RESERVE
2019-06-06 17:37:17 +00:00
if(reserve_limit > 0 && reserve_count < reserve_limit) {
dialog::addItem(XLAT("just let me find Orb of Safety or finish the game"), 'l');
dialog::add_action([] { ignored_memory_warning = true; popScreen(); });
}
dialog::addSelItem("memory reserve", its(reserve_count) + "/" + its(reserve_limit) + " MB", 'r');
dialog::add_action([] {
dialog::editNumber(reserve_limit, 0, max_reserve, 16, 128, XLAT("memory reserve"),
XLAT("When to show a memory warning.")
);
dialog::bound_low(0);
dialog::bound_up(max_reserve);
2023-08-09 12:01:24 +00:00
dialog::get_di().reaction = apply_memory_reserve;
2019-06-06 17:37:17 +00:00
});
#endif
2019-06-06 17:37:17 +00:00
2019-06-17 05:22:41 +00:00
dialog::addItem(XLAT("clear caches"), 'c');
2019-06-06 17:37:17 +00:00
dialog::add_action([] { callhooks(hooks_clear_cache); });
dialog::addBack();
dialog::display();
}
EX bool protect_memory() {
#if CAP_MEMORY_RESERVE
2019-07-31 15:58:16 +00:00
apply_memory_reserve();
2019-06-06 17:37:17 +00:00
if(reserve_limit && reserve_count < reserve_limit && !ignored_memory_warning) {
pushScreen(show_memory_menu);
return true;
}
if(reserve_limit && reserve_count < 8) {
pushScreen(show_memory_menu);
return true;
}
#endif
2019-06-06 17:37:17 +00:00
return false;
}
EX bool memory_issues() {
2019-06-06 17:37:17 +00:00
return reserve_limit && reserve_count < 16;
}
}