#include "Generator.hpp"
#include "WorldCollision.hpp"
+#include "WorldSave.hpp"
#include <algorithm>
-#include <iostream>
#include <limits>
+#include <ostream>
#include <queue>
, light{0}
, model()
, position(0, 0, 0)
-, dirty(false) {
+, dirty_model(false)
+, dirty_save(false) {
}
: types(other.types)
, model(std::move(other.model))
, position(other.position)
-, dirty(other.dirty) {
+, dirty_model(other.dirty_model)
+, dirty_save(other.dirty_save) {
std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
std::copy(other.light, other.light + sizeof(light), light);
std::copy(other.light, other.light + sizeof(light), light);
model = std::move(other.model);
position = other.position;
- dirty = other.dirty;
+ dirty_model = other.dirty_save;
+ dirty_save = other.dirty_save;
return *this;
}
const BlockType &new_type = Type(block);
blocks[index] = block;
+ Invalidate();
if (&old_type == &new_type) return;
void Chunk::Draw() noexcept {
- if (dirty) {
+ if (ShouldUpdateModel()) {
Update();
}
model.Draw();
}
void Chunk::CheckUpdate() noexcept {
- if (dirty) {
+ if (ShouldUpdateModel()) {
Update();
}
}
}
model.Update(buf);
- dirty = false;
+ ClearModel();
}
Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
}
-ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry ®, const Generator &gen) noexcept
+ChunkLoader::ChunkLoader(
+ const Config &config,
+ const BlockTypeRegistry ®,
+ const Generator &gen,
+ const WorldSave &save
+) noexcept
: base(0, 0, 0)
, reg(reg)
, gen(gen)
+, save(save)
, loaded()
-, to_generate()
+, to_load()
, to_free()
, gen_timer(config.gen_limit)
, load_dist(config.load_dist)
}
-void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
+void ChunkLoader::Queue(const Chunk::Pos &from, const Chunk::Pos &to) {
for (int z = from.z; z < to.z; ++z) {
for (int y = from.y; y < to.y; ++y) {
for (int x = from.x; x < to.x; ++x) {
if (Known(pos)) {
continue;
} else if (pos == base) {
- Generate(pos);
+ Load(pos);
// light testing
// for (int i = 0; i < 16; ++i) {
// loaded.back().Invalidate();
// loaded.back().CheckUpdate();
} else {
- to_generate.emplace_back(pos);
+ to_load.emplace_back(pos);
}
}
}
}
- to_generate.sort(ChunkLess(base));
+ to_load.sort(ChunkLess(base));
}
-Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
+Chunk &ChunkLoader::Load(const Chunk::Pos &pos) {
loaded.emplace_back(reg);
Chunk &chunk = loaded.back();
chunk.Position(pos);
- gen(chunk);
+ if (save.Exists(pos)) {
+ save.Read(chunk);
+ } else {
+ gen(chunk);
+ }
Insert(chunk);
return chunk;
}
++next;
// unlink neighbors so they won't reference a dead chunk
chunk->ClearNeighbors();
+ // if it should be saved, do it now
+ if (chunk->ShouldUpdateSave()) {
+ save.Write(*chunk);
+ }
// and move it from loaded to free list
to_free.splice(to_free.end(), loaded, chunk);
return next;
}
bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept {
- for (const Chunk::Pos &chunk : to_generate) {
+ for (const Chunk::Pos &chunk : to_load) {
if (chunk == pos) {
return true;
}
return *chunk;
}
- for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) {
+ for (auto iter(to_load.begin()), end(to_load.end()); iter != end; ++iter) {
if (*iter == pos) {
- to_generate.erase(iter);
+ to_load.erase(iter);
break;
}
}
- return Generate(pos);
+ return Load(pos);
}
bool ChunkLoader::OutOfRange(const Chunk::Pos &pos) const noexcept {
}
}
// abort far away queued chunks
- for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
+ for (auto iter(to_load.begin()), end(to_load.end()); iter != end;) {
if (OutOfRange(*iter)) {
- iter = to_generate.erase(iter);
+ iter = to_load.erase(iter);
} else {
++iter;
}
}
// add missing new chunks
- GenerateSurrounding(base);
+ QueueSurrounding(base);
}
-void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
+void ChunkLoader::QueueSurrounding(const Chunk::Pos &pos) {
const Chunk::Pos offset(load_dist, load_dist, load_dist);
- Generate(pos - offset, pos + offset);
+ Queue(pos - offset, pos + offset);
}
void ChunkLoader::Update(int dt) {
- // check if a chunk generation is scheduled for this frame
- // and if there's a chunk waiting to be generated
+ // check if a chunk load is scheduled for this frame
+ // and if there's chunks waiting to be loaded
gen_timer.Update(dt);
if (gen_timer.Hit()) {
- LoadOne();
+ // we may
+ // load until one of load or generation limits was hit
+ constexpr int max_load = 10;
+ constexpr int max_gen = 1;
+ int loaded = 0;
+ int generated = 0;
+ while (!to_load.empty() && loaded < max_load && generated < max_gen) {
+ if (LoadOne()) {
+ ++generated;
+ } else {
+ ++loaded;
+ }
+ }
+ }
+
+ constexpr int max_save = 10;
+ int saved = 0;
+ for (Chunk &chunk : loaded) {
+ if (chunk.ShouldUpdateSave()) {
+ save.Write(chunk);
+ ++saved;
+ if (saved >= max_save) {
+ break;
+ }
+ }
}
}
}
}
-void ChunkLoader::LoadOne() {
- if (to_generate.empty()) return;
+bool ChunkLoader::LoadOne() {
+ if (to_load.empty()) return false;
// take position of next chunk in queue
- Chunk::Pos pos(to_generate.front());
- to_generate.pop_front();
+ Chunk::Pos pos(to_load.front());
+ to_load.pop_front();
// look if the same chunk was already generated and still lingering
for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
if (iter->Position() == pos) {
loaded.splice(loaded.end(), to_free, iter);
Insert(loaded.back());
- return;
+ return false;
}
}
loaded.splice(loaded.end(), to_free, to_free.begin());
}
+ bool generated = false;
Chunk &chunk = loaded.back();
chunk.Position(pos);
- gen(chunk);
+ if (save.Exists(pos)) {
+ save.Read(chunk);
+ } else {
+ gen(chunk);
+ generated = true;
+ }
Insert(chunk);
+ return generated;
}
}