contentpack remove feature WIP

This commit is contained in:
MihailRis 2024-02-29 23:47:07 +03:00
parent a1f313bedc
commit 3862dbda66
18 changed files with 541 additions and 433 deletions

View File

@ -1,4 +1,5 @@
// Example of a GLSL library #define PI 3.1415926535897932384626433832795
#define PI2 (PI*2)
vec4 decompress_light(float compressed_light) { vec4 decompress_light(float compressed_light) {
vec4 result; vec4 result;

View File

@ -1,6 +1,7 @@
# Menu # Menu
menu.missing-content=Missing Content! menu.missing-content=Missing Content!
world.convert-request=Content indices have changed! Convert world files? world.convert-request=Content indices have changed! Convert world files?
pack.remove-confirm=Do you want to erase all pack content from the world forever?
error.pack-not-found=Could not to find pack error.pack-not-found=Could not to find pack
error.dependency-not-found=Dependency pack is not found error.dependency-not-found=Dependency pack is not found
world.delete-confirm=Do you want to delete world forever? world.delete-confirm=Do you want to delete world forever?

View File

@ -10,6 +10,8 @@ Add=Добавить
error.pack-not-found=Не удалось найти пакет error.pack-not-found=Не удалось найти пакет
error.dependency-not-found=Используемая зависимость не найдена error.dependency-not-found=Используемая зависимость не найдена
pack.remove-confirm=Удалить весь поставляемый паком контент из мира (безвозвратно)?
# Меню # Меню
menu.New World=Новый Мир menu.New World=Новый Мир
menu.Quit=Выход menu.Quit=Выход

BIN
res/textures/gui/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -70,6 +70,7 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon");
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/warning.png", "gui/warning"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/warning.png", "gui/warning");
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/error.png", "gui/error"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/error.png", "gui/error");
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/cross.png", "gui/cross");
if (content) { if (content) {
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d"); loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d");
loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background"); loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background");
@ -79,7 +80,8 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/crosshair.png", "gui/crosshair"); loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/crosshair.png", "gui/crosshair");
addLayouts(0, "core", loader.getPaths()->getMainRoot()/fs::path("layouts"), loader); addLayouts(0, "core", loader.getPaths()->getMainRoot()/fs::path("layouts"), loader);
for (auto& pack : content->getPacks()) { for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();
auto& info = pack->getInfo(); auto& info = pack->getInfo();
fs::path folder = info.folder / fs::path("layouts"); fs::path folder = info.folder / fs::path("layouts");
addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader); addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader);

View File

@ -26,7 +26,7 @@ void ContentBuilder::add(ItemDef* def) {
} }
void ContentBuilder::add(ContentPackRuntime* pack) { void ContentBuilder::add(ContentPackRuntime* pack) {
packs.push_back(std::unique_ptr<ContentPackRuntime>(pack)); packs.emplace(pack->getId(), pack);
} }
Block& ContentBuilder::createBlock(std::string id) { Block& ContentBuilder::createBlock(std::string id) {
@ -136,7 +136,7 @@ Content::Content(ContentIndices* indices,
std::unique_ptr<DrawGroups> drawGroups, std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs, std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs, std::unordered_map<std::string, ItemDef*> itemDefs,
std::vector<std::unique_ptr<ContentPackRuntime>> packs) std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs)
: blockDefs(blockDefs), : blockDefs(blockDefs),
itemDefs(itemDefs), itemDefs(itemDefs),
indices(indices), indices(indices),
@ -179,6 +179,14 @@ ItemDef& Content::requireItem(std::string id) const {
return *found->second; return *found->second;
} }
const std::vector<std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const { const ContentPackRuntime* Content::getPackRuntime(std::string id) const {
auto found = packs.find(id);
if (found == packs.end()) {
return nullptr;
}
return found->second.get();
}
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const {
return packs; return packs;
} }

View File

@ -48,7 +48,7 @@ class ContentBuilder {
std::unordered_map<std::string, ItemDef*> itemDefs; std::unordered_map<std::string, ItemDef*> itemDefs;
std::vector<std::string> itemIds; std::vector<std::string> itemIds;
std::vector<std::unique_ptr<ContentPackRuntime>> packs; std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
public: public:
~ContentBuilder(); ~ContentBuilder();
@ -108,7 +108,7 @@ class Content {
std::unordered_map<std::string, Block*> blockDefs; std::unordered_map<std::string, Block*> blockDefs;
std::unordered_map<std::string, ItemDef*> itemDefs; std::unordered_map<std::string, ItemDef*> itemDefs;
std::unique_ptr<ContentIndices> indices; std::unique_ptr<ContentIndices> indices;
std::vector<std::unique_ptr<ContentPackRuntime>> packs; std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
public: public:
std::unique_ptr<DrawGroups> const drawGroups; std::unique_ptr<DrawGroups> const drawGroups;
@ -116,7 +116,7 @@ public:
std::unique_ptr<DrawGroups> drawGroups, std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs, std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs, std::unordered_map<std::string, ItemDef*> itemDefs,
std::vector<std::unique_ptr<ContentPackRuntime>> packs); std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs);
~Content(); ~Content();
inline ContentIndices* getIndices() const { inline ContentIndices* getIndices() const {
@ -129,7 +129,9 @@ public:
ItemDef* findItem(std::string id) const; ItemDef* findItem(std::string id) const;
ItemDef& requireItem(std::string id) const; ItemDef& requireItem(std::string id) const;
const std::vector<std::unique_ptr<ContentPackRuntime>>& getPacks() const; const ContentPackRuntime* getPackRuntime(std::string id) const;
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const;
}; };
#endif // CONTENT_CONTENT_H_ #endif // CONTENT_CONTENT_H_

View File

@ -294,6 +294,7 @@ void ContentLoader::load(ContentBuilder& builder) {
auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack)); auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack));
builder.add(runtime); builder.add(runtime);
env = runtime->getEnvironment()->getId(); env = runtime->getEnvironment()->getId();
ContentPackStats& stats = runtime->getStatsWriteable();
fixPackIndices(); fixPackIndices();
@ -306,6 +307,7 @@ void ContentLoader::load(ContentBuilder& builder) {
if (!fs::is_regular_file(pack->getContentFile())) if (!fs::is_regular_file(pack->getContentFile()))
return; return;
auto root = files::read_json(pack->getContentFile()); auto root = files::read_json(pack->getContentFile());
auto blocksarr = root->list("blocks"); auto blocksarr = root->list("blocks");
if (blocksarr) { if (blocksarr) {
@ -314,6 +316,7 @@ void ContentLoader::load(ContentBuilder& builder) {
std::string full = pack->id+":"+name; std::string full = pack->id+":"+name;
auto& def = builder.createBlock(full); auto& def = builder.createBlock(full);
loadBlock(def, full, name); loadBlock(def, full, name);
stats.totalBlocks++;
if (!def.hidden) { if (!def.hidden) {
auto& item = builder.createItem(full+BLOCK_ITEM_SUFFIX); auto& item = builder.createItem(full+BLOCK_ITEM_SUFFIX);
item.generated = true; item.generated = true;
@ -324,6 +327,7 @@ void ContentLoader::load(ContentBuilder& builder) {
for (uint j = 0; j < 4; j++) { for (uint j = 0; j < 4; j++) {
item.emission[j] = def.emission[j]; item.emission[j] = def.emission[j];
} }
stats.totalItems++;
} }
} }
} }
@ -334,6 +338,7 @@ void ContentLoader::load(ContentBuilder& builder) {
std::string name = itemsarr->str(i); std::string name = itemsarr->str(i);
std::string full = pack->id+":"+name; std::string full = pack->id+":"+name;
loadItem(builder.createItem(full), full, name); loadItem(builder.createItem(full), full, name);
stats.totalItems++;
} }
} }
} }

View File

@ -77,8 +77,18 @@ struct ContentPack {
); );
}; };
struct ContentPackStats {
size_t totalBlocks;
size_t totalItems;
inline bool hasSavingContent() const {
return totalBlocks + totalItems > 0;
}
};
class ContentPackRuntime { class ContentPackRuntime {
ContentPack info; ContentPack info;
ContentPackStats stats {};
std::unique_ptr<scripting::Environment> env; std::unique_ptr<scripting::Environment> env;
public: public:
ContentPackRuntime( ContentPackRuntime(
@ -86,6 +96,14 @@ public:
std::unique_ptr<scripting::Environment> env std::unique_ptr<scripting::Environment> env
); );
inline const ContentPackStats& getStats() const {
return stats;
}
inline ContentPackStats& getStatsWriteable() {
return stats;
}
inline const std::string& getId() { inline const std::string& getId() {
return info.id; return info.id;
} }

View File

@ -49,91 +49,91 @@ regfile::regfile(fs::path filename) : file(filename) {
} }
WorldRegion::WorldRegion() { WorldRegion::WorldRegion() {
chunksData = new ubyte*[REGION_CHUNKS_COUNT]{}; chunksData = new ubyte*[REGION_CHUNKS_COUNT]{};
sizes = new uint32_t[REGION_CHUNKS_COUNT]{}; sizes = new uint32_t[REGION_CHUNKS_COUNT]{};
} }
WorldRegion::~WorldRegion() { WorldRegion::~WorldRegion() {
for (uint i = 0; i < REGION_CHUNKS_COUNT; i++) { for (uint i = 0; i < REGION_CHUNKS_COUNT; i++) {
delete[] chunksData[i]; delete[] chunksData[i];
} }
delete[] sizes; delete[] sizes;
delete[] chunksData; delete[] chunksData;
} }
void WorldRegion::setUnsaved(bool unsaved) { void WorldRegion::setUnsaved(bool unsaved) {
this->unsaved = unsaved; this->unsaved = unsaved;
} }
bool WorldRegion::isUnsaved() const { bool WorldRegion::isUnsaved() const {
return unsaved; return unsaved;
} }
ubyte** WorldRegion::getChunks() const { ubyte** WorldRegion::getChunks() const {
return chunksData; return chunksData;
} }
uint32_t* WorldRegion::getSizes() const { uint32_t* WorldRegion::getSizes() const {
return sizes; return sizes;
} }
void WorldRegion::put(uint x, uint z, ubyte* data, uint32_t size) { void WorldRegion::put(uint x, uint z, ubyte* data, uint32_t size) {
size_t chunk_index = z * REGION_SIZE + x; size_t chunk_index = z * REGION_SIZE + x;
delete[] chunksData[chunk_index]; delete[] chunksData[chunk_index];
chunksData[chunk_index] = data; chunksData[chunk_index] = data;
sizes[chunk_index] = size; sizes[chunk_index] = size;
} }
ubyte* WorldRegion::getChunkData(uint x, uint z) { ubyte* WorldRegion::getChunkData(uint x, uint z) {
return chunksData[z * REGION_SIZE + x]; return chunksData[z * REGION_SIZE + x];
} }
uint WorldRegion::getChunkDataSize(uint x, uint z) { uint WorldRegion::getChunkDataSize(uint x, uint z) {
return sizes[z * REGION_SIZE + x]; return sizes[z * REGION_SIZE + x];
} }
const char* WorldFiles::WORLD_FILE = "world.json"; const char* WorldFiles::WORLD_FILE = "world.json";
WorldFiles::WorldFiles(fs::path directory, const DebugSettings& settings) WorldFiles::WorldFiles(fs::path directory, const DebugSettings& settings)
: directory(directory), : directory(directory),
generatorTestMode(settings.generatorTestMode), generatorTestMode(settings.generatorTestMode),
doWriteLights(settings.doWriteLights) { doWriteLights(settings.doWriteLights) {
compressionBuffer.reset(new ubyte[CHUNK_DATA_LEN * 2]); compressionBuffer.reset(new ubyte[CHUNK_DATA_LEN * 2]);
} }
WorldFiles::~WorldFiles(){ WorldFiles::~WorldFiles(){
} }
WorldRegion* WorldFiles::getRegion(regionsmap& regions, int x, int z) { WorldRegion* WorldFiles::getRegion(regionsmap& regions, int x, int z) {
auto found = regions.find(glm::ivec2(x, z)); auto found = regions.find(glm::ivec2(x, z));
if (found == regions.end()) if (found == regions.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
WorldRegion* WorldFiles::getOrCreateRegion(regionsmap& regions, int x, int z) { WorldRegion* WorldFiles::getOrCreateRegion(regionsmap& regions, int x, int z) {
WorldRegion* region = getRegion(regions, x, z); WorldRegion* region = getRegion(regions, x, z);
if (region == nullptr) { if (region == nullptr) {
region = new WorldRegion(); region = new WorldRegion();
regions[glm::ivec2(x, z)].reset(region); regions[glm::ivec2(x, z)].reset(region);
} }
return region; return region;
} }
ubyte* WorldFiles::compress(const ubyte* src, size_t srclen, size_t& len) { ubyte* WorldFiles::compress(const ubyte* src, size_t srclen, size_t& len) {
ubyte* buffer = this->compressionBuffer.get(); ubyte* buffer = this->compressionBuffer.get();
len = extrle::encode(src, srclen, buffer); len = extrle::encode(src, srclen, buffer);
ubyte* data = new ubyte[len]; ubyte* data = new ubyte[len];
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
data[i] = buffer[i]; data[i] = buffer[i];
} }
return data; return data;
} }
ubyte* WorldFiles::decompress(const ubyte* src, size_t srclen, size_t dstlen) { ubyte* WorldFiles::decompress(const ubyte* src, size_t srclen, size_t dstlen) {
ubyte* decompressed = new ubyte[dstlen]; ubyte* decompressed = new ubyte[dstlen];
extrle::decode(src, srclen, decompressed); extrle::decode(src, srclen, decompressed);
return decompressed; return decompressed;
} }
int WorldFiles::getVoxelRegionVersion(int x, int z) { int WorldFiles::getVoxelRegionVersion(int x, int z) {
@ -168,49 +168,49 @@ int WorldFiles::getVoxelRegionsVersion() {
*/ */
void WorldFiles::put(int x, int z, const ubyte* voxelData) { void WorldFiles::put(int x, int z, const ubyte* voxelData) {
int regionX = floordiv(x, REGION_SIZE); int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE); int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE); int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE); int localZ = z - (regionZ * REGION_SIZE);
/* Writing Voxels */ { /* Writing Voxels */ {
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
region->setUnsaved(true); region->setUnsaved(true);
size_t compressedSize; size_t compressedSize;
ubyte* data = compress(voxelData, CHUNK_DATA_LEN, compressedSize); ubyte* data = compress(voxelData, CHUNK_DATA_LEN, compressedSize);
region->put(localX, localZ, data, compressedSize); region->put(localX, localZ, data, compressedSize);
} }
} }
/* /*
* Store chunk (voxels and lights) in region (existing or new) * Store chunk (voxels and lights) in region (existing or new)
*/ */
void WorldFiles::put(Chunk* chunk){ void WorldFiles::put(Chunk* chunk){
assert(chunk != nullptr); assert(chunk != nullptr);
int regionX = floordiv(chunk->x, REGION_SIZE); int regionX = floordiv(chunk->x, REGION_SIZE);
int regionZ = floordiv(chunk->z, REGION_SIZE); int regionZ = floordiv(chunk->z, REGION_SIZE);
int localX = chunk->x - (regionX * REGION_SIZE); int localX = chunk->x - (regionX * REGION_SIZE);
int localZ = chunk->z - (regionZ * REGION_SIZE); int localZ = chunk->z - (regionZ * REGION_SIZE);
/* Writing voxels */ { /* Writing voxels */ {
size_t compressedSize; size_t compressedSize;
std::unique_ptr<ubyte[]> chunk_data (chunk->encode()); std::unique_ptr<ubyte[]> chunk_data (chunk->encode());
ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize); ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize);
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
region->setUnsaved(true); region->setUnsaved(true);
region->put(localX, localZ, data, compressedSize); region->put(localX, localZ, data, compressedSize);
} }
/* Writing lights cache */ /* Writing lights cache */
if (doWriteLights && chunk->isLighted()) { if (doWriteLights && chunk->isLighted()) {
size_t compressedSize; size_t compressedSize;
std::unique_ptr<ubyte[]> light_data (chunk->lightmap.encode()); std::unique_ptr<ubyte[]> light_data (chunk->lightmap.encode());
ubyte* data = compress(light_data.get(), LIGHTMAP_DATA_LEN, compressedSize); ubyte* data = compress(light_data.get(), LIGHTMAP_DATA_LEN, compressedSize);
WorldRegion* region = getOrCreateRegion(lights, regionX, regionZ); WorldRegion* region = getOrCreateRegion(lights, regionX, regionZ);
region->setUnsaved(true); region->setUnsaved(true);
region->put(localX, localZ, data, compressedSize); region->put(localX, localZ, data, compressedSize);
} }
/* Writing block inventories */ /* Writing block inventories */
if (!chunk->inventories.empty()){ if (!chunk->inventories.empty()){
auto& inventories = chunk->inventories; auto& inventories = chunk->inventories;
@ -237,19 +237,19 @@ void WorldFiles::put(Chunk* chunk){
} }
fs::path WorldFiles::getRegionsFolder() const { fs::path WorldFiles::getRegionsFolder() const {
return directory/fs::path("regions"); return directory/fs::path("regions");
} }
fs::path WorldFiles::getLightsFolder() const { fs::path WorldFiles::getLightsFolder() const {
return directory/fs::path("lights"); return directory/fs::path("lights");
} }
fs::path WorldFiles::getInventoriesFolder() const { fs::path WorldFiles::getInventoriesFolder() const {
return directory/fs::path("inventories"); return directory/fs::path("inventories");
} }
fs::path WorldFiles::getRegionFilename(int x, int z) const { fs::path WorldFiles::getRegionFilename(int x, int z) const {
return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin"); return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin");
} }
/* /*
@ -275,78 +275,78 @@ bool WorldFiles::parseRegionFilename(const std::string& name, int& x, int& z) {
} }
fs::path WorldFiles::getPlayerFile() const { fs::path WorldFiles::getPlayerFile() const {
return directory/fs::path("player.json"); return directory/fs::path("player.json");
} }
fs::path WorldFiles::getWorldFile() const { fs::path WorldFiles::getWorldFile() const {
return directory/fs::path(WORLD_FILE); return directory/fs::path(WORLD_FILE);
} }
fs::path WorldFiles::getIndicesFile() const { fs::path WorldFiles::getIndicesFile() const {
return directory/fs::path("indices.json"); return directory/fs::path("indices.json");
} }
fs::path WorldFiles::getPacksFile() const { fs::path WorldFiles::getPacksFile() const {
return directory/fs::path("packs.list"); return directory/fs::path("packs.list");
} }
ubyte* WorldFiles::getChunk(int x, int z){ ubyte* WorldFiles::getChunk(int x, int z){
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true); return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true);
} }
/* Get cached lights for chunk at x,z /* Get cached lights for chunk at x,z
* @return lights data or nullptr */ * @return lights data or nullptr */
light_t* WorldFiles::getLights(int x, int z) { light_t* WorldFiles::getLights(int x, int z) {
std::unique_ptr<ubyte[]> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true)); std::unique_ptr<ubyte[]> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true));
if (data == nullptr) if (data == nullptr)
return nullptr; return nullptr;
return Lightmap::decode(data.get()); return Lightmap::decode(data.get());
} }
chunk_inventories_map WorldFiles::fetchInventories(int x, int z) { chunk_inventories_map WorldFiles::fetchInventories(int x, int z) {
chunk_inventories_map inventories; chunk_inventories_map inventories;
const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false); const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false);
if (data == nullptr) if (data == nullptr)
return inventories; return inventories;
ByteReader reader(data, BUFFER_SIZE_UNKNOWN); ByteReader reader(data, BUFFER_SIZE_UNKNOWN);
int count = reader.getInt32(); int count = reader.getInt32();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
uint index = reader.getInt32(); uint index = reader.getInt32();
uint size = reader.getInt32(); uint size = reader.getInt32();
auto map = json::from_binary(reader.pointer(), size); auto map = json::from_binary(reader.pointer(), size);
reader.skip(size); reader.skip(size);
auto inv = std::make_shared<Inventory>(0, 0); auto inv = std::make_shared<Inventory>(0, 0);
inv->deserialize(map.get()); inv->deserialize(map.get());
inventories[index] = inv; inventories[index] = inv;
} }
return inventories; return inventories;
} }
ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder, ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
int x, int z, int layer, bool compression) { int x, int z, int layer, bool compression) {
int regionX = floordiv(x, REGION_SIZE); int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE); int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE); int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE); int localZ = z - (regionZ * REGION_SIZE);
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
ubyte* data = region->getChunkData(localX, localZ); ubyte* data = region->getChunkData(localX, localZ);
if (data == nullptr) { if (data == nullptr) {
uint32_t size; uint32_t size;
data = readChunkData(x, z, size, folder, layer); data = readChunkData(x, z, size, folder, layer);
if (data != nullptr) { if (data != nullptr) {
region->put(localX, localZ, data, size); region->put(localX, localZ, data, size);
} }
} }
if (data != nullptr) { if (data != nullptr) {
size_t size = region->getChunkDataSize(localX, localZ); size_t size = region->getChunkDataSize(localX, localZ);
if (compression) { if (compression) {
return decompress(data, size, CHUNK_DATA_LEN); return decompress(data, size, CHUNK_DATA_LEN);
} }
return data; return data;
} }
return nullptr; return nullptr;
} }
@ -360,7 +360,7 @@ regfile* WorldFiles::getRegFile(glm::ivec3 coord, const fs::path& folder) {
auto iter = std::next(openRegFiles.begin(), rand() % openRegFiles.size()); auto iter = std::next(openRegFiles.begin(), rand() % openRegFiles.size());
openRegFiles.erase(iter); openRegFiles.erase(iter);
} }
fs::path filename = folder / getRegionFilename(coord[0], coord[1]); fs::path filename = folder / getRegionFilename(coord[0], coord[1]);
if (!fs::is_regular_file(filename)) { if (!fs::is_regular_file(filename)) {
return nullptr; return nullptr;
} }
@ -373,14 +373,14 @@ ubyte* WorldFiles::readChunkData(int x,
uint32_t& length, uint32_t& length,
fs::path folder, fs::path folder,
int layer){ int layer){
if (generatorTestMode) if (generatorTestMode)
return nullptr; return nullptr;
int regionX = floordiv(x, REGION_SIZE); int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE); int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE); int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE); int localZ = z - (regionZ * REGION_SIZE);
int chunkIndex = localZ * REGION_SIZE + localX; int chunkIndex = localZ * REGION_SIZE + localX;
glm::ivec3 coord(regionX, regionZ, layer); glm::ivec3 coord(regionX, regionZ, layer);
regfile* rfile = WorldFiles::getRegFile(coord, folder); regfile* rfile = WorldFiles::getRegFile(coord, folder);
@ -389,24 +389,24 @@ ubyte* WorldFiles::readChunkData(int x,
} }
files::rafile& file = rfile->file; files::rafile& file = rfile->file;
size_t file_size = file.length(); size_t file_size = file.length();
size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4; size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4;
uint32_t offset; uint32_t offset;
file.seekg(table_offset + chunkIndex * 4); file.seekg(table_offset + chunkIndex * 4);
file.read((char*)(&offset), 4); file.read((char*)(&offset), 4);
offset = dataio::read_int32_big((const ubyte*)(&offset), 0); offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
if (offset == 0){ if (offset == 0){
return nullptr; return nullptr;
} }
file.seekg(offset); file.seekg(offset);
file.read((char*)(&offset), 4); file.read((char*)(&offset), 4);
length = dataio::read_int32_big((const ubyte*)(&offset), 0); length = dataio::read_int32_big((const ubyte*)(&offset), 0);
ubyte* data = new ubyte[length]{}; ubyte* data = new ubyte[length]{};
file.read((char*)data, length); file.read((char*)data, length);
return data; return data;
} }
/* Read missing chunks data (null pointers) from region file /* Read missing chunks data (null pointers) from region file
@ -415,7 +415,7 @@ ubyte* WorldFiles::readChunkData(int x,
*/ */
void WorldFiles::fetchChunks(WorldRegion* region, int x, int z, fs::path folder, int layer) { void WorldFiles::fetchChunks(WorldRegion* region, int x, int z, fs::path folder, int layer) {
ubyte** chunks = region->getChunks(); ubyte** chunks = region->getChunks();
uint32_t* sizes = region->getSizes(); uint32_t* sizes = region->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE; int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE;
@ -441,70 +441,70 @@ void WorldFiles::writeRegion(int x, int z, WorldRegion* entry, fs::path folder,
openRegFiles.erase(regcoord); openRegFiles.erase(regcoord);
} }
char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC;
header[8] = REGION_FORMAT_VERSION; header[8] = REGION_FORMAT_VERSION;
header[9] = 0; // flags header[9] = 0; // flags
std::ofstream file(filename, std::ios::out | std::ios::binary); std::ofstream file(filename, std::ios::out | std::ios::binary);
file.write(header, REGION_HEADER_SIZE); file.write(header, REGION_HEADER_SIZE);
size_t offset = REGION_HEADER_SIZE; size_t offset = REGION_HEADER_SIZE;
char intbuf[4]{}; char intbuf[4]{};
uint offsets[REGION_CHUNKS_COUNT]{}; uint offsets[REGION_CHUNKS_COUNT]{};
ubyte** region = entry->getChunks(); ubyte** region = entry->getChunks();
uint32_t* sizes = entry->getSizes(); uint32_t* sizes = entry->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
ubyte* chunk = region[i]; ubyte* chunk = region[i];
if (chunk == nullptr){ if (chunk == nullptr){
offsets[i] = 0; offsets[i] = 0;
} else { } else {
offsets[i] = offset; offsets[i] = offset;
size_t compressedSize = sizes[i]; size_t compressedSize = sizes[i];
dataio::write_int32_big(compressedSize, (ubyte*)intbuf, 0); dataio::write_int32_big(compressedSize, (ubyte*)intbuf, 0);
offset += 4 + compressedSize; offset += 4 + compressedSize;
file.write(intbuf, 4); file.write(intbuf, 4);
file.write((const char*)chunk, compressedSize); file.write((const char*)chunk, compressedSize);
} }
} }
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
dataio::write_int32_big(offsets[i], (ubyte*)intbuf, 0); dataio::write_int32_big(offsets[i], (ubyte*)intbuf, 0);
file.write(intbuf, 4); file.write(intbuf, 4);
} }
} }
void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int layer) { void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int layer) {
for (auto& it : regions){ for (auto& it : regions){
WorldRegion* region = it.second.get(); WorldRegion* region = it.second.get();
if (region->getChunks() == nullptr || !region->isUnsaved()) if (region->getChunks() == nullptr || !region->isUnsaved())
continue; continue;
glm::ivec2 key = it.first; glm::ivec2 key = it.first;
writeRegion(key[0], key[1], region, folder, layer); writeRegion(key[0], key[1], region, folder, layer);
} }
} }
void WorldFiles::write(const World* world, const Content* content) { void WorldFiles::write(const World* world, const Content* content) {
fs::path regionsFolder = getRegionsFolder(); fs::path regionsFolder = getRegionsFolder();
fs::path lightsFolder = getLightsFolder(); fs::path lightsFolder = getLightsFolder();
fs::path inventoriesFolder = getInventoriesFolder(); fs::path inventoriesFolder = getInventoriesFolder();
fs::create_directories(regionsFolder); fs::create_directories(regionsFolder);
fs::create_directories(inventoriesFolder); fs::create_directories(inventoriesFolder);
fs::create_directories(lightsFolder); fs::create_directories(lightsFolder);
if (world) { if (world) {
writeWorldInfo(world); writeWorldInfo(world);
writePacks(world); writePacks(world);
} }
if (generatorTestMode) { if (generatorTestMode) {
return; return;
} }
writeIndices(content->getIndices()); writeIndices(content->getIndices());
writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS); writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS);
writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS); writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS);
writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES); writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES);
} }
@ -514,64 +514,64 @@ void WorldFiles::writePacks(const World* world) {
return; return;
} }
const auto& packs = world->getPacks(); const auto& packs = world->getPacks();
std::stringstream ss; std::stringstream ss;
ss << "# autogenerated; do not modify\n"; ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) { for (const auto& pack : packs) {
ss << pack.id << "\n"; ss << pack.id << "\n";
} }
files::write_string(packsFile, ss.str()); files::write_string(packsFile, ss.str());
} }
void WorldFiles::writeIndices(const ContentIndices* indices) { void WorldFiles::writeIndices(const ContentIndices* indices) {
dynamic::Map root; dynamic::Map root;
uint count; uint count;
auto& blocks = root.putList("blocks"); auto& blocks = root.putList("blocks");
count = indices->countBlockDefs(); count = indices->countBlockDefs();
for (uint i = 0; i < count; i++) { for (uint i = 0; i < count; i++) {
const Block* def = indices->getBlockDef(i); const Block* def = indices->getBlockDef(i);
blocks.put(def->name); blocks.put(def->name);
} }
auto& items = root.putList("items"); auto& items = root.putList("items");
count = indices->countItemDefs(); count = indices->countItemDefs();
for (uint i = 0; i < count; i++) { for (uint i = 0; i < count; i++) {
const ItemDef* def = indices->getItemDef(i); const ItemDef* def = indices->getItemDef(i);
items.put(def->name); items.put(def->name);
} }
files::write_json(getIndicesFile(), &root); files::write_json(getIndicesFile(), &root);
} }
void WorldFiles::writeWorldInfo(const World* world) { void WorldFiles::writeWorldInfo(const World* world) {
files::write_json(getWorldFile(), world->serialize().get()); files::write_json(getWorldFile(), world->serialize().get());
} }
bool WorldFiles::readWorldInfo(World* world) { bool WorldFiles::readWorldInfo(World* world) {
fs::path file = getWorldFile(); fs::path file = getWorldFile();
if (!fs::is_regular_file(file)) { if (!fs::is_regular_file(file)) {
std::cerr << "warning: world.json does not exists" << std::endl; std::cerr << "warning: world.json does not exists" << std::endl;
return false; return false;
} }
auto root = files::read_json(file); auto root = files::read_json(file);
world->deserialize(root.get()); world->deserialize(root.get());
return true; return true;
} }
void WorldFiles::writePlayer(std::shared_ptr<Player> player) { void WorldFiles::writePlayer(std::shared_ptr<Player> player) {
files::write_json(getPlayerFile(), player->serialize().release()); files::write_json(getPlayerFile(), player->serialize().release());
} }
bool WorldFiles::readPlayer(std::shared_ptr<Player> player) { bool WorldFiles::readPlayer(std::shared_ptr<Player> player) {
fs::path file = getPlayerFile(); fs::path file = getPlayerFile();
if (!fs::is_regular_file(file)) { if (!fs::is_regular_file(file)) {
std::cerr << "warning: player.json does not exists" << std::endl; std::cerr << "warning: player.json does not exists" << std::endl;
return false; return false;
} }
player->deserialize(files::read_json(file).get()); player->deserialize(files::read_json(file).get());
return true; return true;
} }
void WorldFiles::addPack(const World* world, const std::string& id) { void WorldFiles::addPack(const World* world, const std::string& id) {
@ -585,10 +585,32 @@ void WorldFiles::addPack(const World* world, const std::string& id) {
auto packs = files::read_list(file); auto packs = files::read_list(file);
packs.push_back(id); packs.push_back(id);
std::stringstream ss; std::stringstream ss;
ss << "# autogenerated; do not modify\n"; ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) { for (const auto& pack : packs) {
ss << pack << "\n"; ss << pack << "\n";
} }
files::write_string(file, ss.str()); files::write_string(file, ss.str());
}
void WorldFiles::removePack(const World* world, const std::string& id) {
fs::path file = getPacksFile();
if (!fs::is_regular_file(file)) {
if (!fs::is_directory(directory)) {
fs::create_directories(directory);
}
writePacks(world);
}
auto packs = files::read_list(file);
auto found = std::find(packs.begin(), packs.end(), id);
if (found != packs.end()) {
packs.erase(found);
}
std::stringstream ss;
ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) {
ss << pack << "\n";
}
files::write_string(file, ss.str());
} }

View File

@ -47,22 +47,22 @@ public:
}; };
class WorldRegion { class WorldRegion {
ubyte** chunksData; ubyte** chunksData;
uint32_t* sizes; uint32_t* sizes;
bool unsaved = false; bool unsaved = false;
public: public:
WorldRegion(); WorldRegion();
~WorldRegion(); ~WorldRegion();
void put(uint x, uint z, ubyte* data, uint32_t size); void put(uint x, uint z, ubyte* data, uint32_t size);
ubyte* getChunkData(uint x, uint z); ubyte* getChunkData(uint x, uint z);
uint getChunkDataSize(uint x, uint z); uint getChunkDataSize(uint x, uint z);
void setUnsaved(bool unsaved); void setUnsaved(bool unsaved);
bool isUnsaved() const; bool isUnsaved() const;
ubyte** getChunks() const; ubyte** getChunks() const;
uint32_t* getSizes() const; uint32_t* getSizes() const;
}; };
struct regfile { struct regfile {
@ -77,86 +77,95 @@ typedef std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>> regionsmap;
class WorldFiles { class WorldFiles {
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles; std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
void writeWorldInfo(const World* world); void writeWorldInfo(const World* world);
fs::path getRegionFilename(int x, int y) const; fs::path getRegionFilename(int x, int y) const;
fs::path getWorldFile() const; fs::path getWorldFile() const;
fs::path getIndicesFile() const; fs::path getIndicesFile() const;
fs::path getPacksFile() const; fs::path getPacksFile() const;
WorldRegion* getRegion(regionsmap& regions, int x, int z); WorldRegion* getRegion(regionsmap& regions, int x, int z);
WorldRegion* getOrCreateRegion(regionsmap& regions, int x, int z); WorldRegion* getOrCreateRegion(regionsmap& regions, int x, int z);
/* Compress buffer with extrle /// @brief Compress buffer with extrle
@param src source buffer /// @param src source buffer
@param srclen length of source buffer /// @param srclen length of the source buffer
@param len (out argument) length of result buffer */ /// @param len (out argument) length of result buffer
ubyte* compress(const ubyte* src, size_t srclen, size_t& len); /// @return compressed bytes array
ubyte* compress(const ubyte* src, size_t srclen, size_t& len);
/* Decompress buffer with extrle /// @brief Decompress buffer with extrle
@param src compressed buffer /// @param src compressed buffer
@param srclen length of compressed buffer /// @param srclen length of compressed buffer
@param dstlen max expected length of source buffer */ /// @param dstlen max expected length of source buffer
ubyte* decompress(const ubyte* src, size_t srclen, size_t dstlen); /// @return decompressed bytes array
ubyte* decompress(const ubyte* src, size_t srclen, size_t dstlen);
ubyte* readChunkData(int x, int y, ubyte* readChunkData(int x, int y, uint32_t& length, fs::path folder, int layer);
uint32_t& length,
fs::path folder,
int layer);
void fetchChunks(WorldRegion* region, int x, int y,
fs::path folder, int layer);
void writeRegions(regionsmap& regions, void fetchChunks(WorldRegion* region, int x, int y, fs::path folder, int layer);
const fs::path& folder, int layer);
ubyte* getData(regionsmap& regions, void writeRegions(regionsmap& regions, const fs::path& folder, int layer);
const fs::path& folder,
int x, int z, int layer, bool compression); ubyte* getData(regionsmap& regions, const fs::path& folder, int x, int z, int layer, bool compression);
regfile* getRegFile(glm::ivec3 coord, const fs::path& folder); regfile* getRegFile(glm::ivec3 coord, const fs::path& folder);
fs::path getLightsFolder() const; fs::path getLightsFolder() const;
fs::path getInventoriesFolder() const; fs::path getInventoriesFolder() const;
public: public:
static bool parseRegionFilename(const std::string& name, int& x, int& y); static bool parseRegionFilename(const std::string& name, int& x, int& y);
fs::path getRegionsFolder() const; fs::path getRegionsFolder() const;
fs::path getPlayerFile() const; fs::path getPlayerFile() const;
regionsmap regions; regionsmap regions;
regionsmap storages; regionsmap storages;
regionsmap lights; regionsmap lights;
fs::path directory; fs::path directory;
std::unique_ptr<ubyte[]> compressionBuffer; std::unique_ptr<ubyte[]> compressionBuffer;
bool generatorTestMode; bool generatorTestMode;
bool doWriteLights; bool doWriteLights;
WorldFiles(fs::path directory, const DebugSettings& settings); WorldFiles(fs::path directory, const DebugSettings& settings);
~WorldFiles(); ~WorldFiles();
void put(Chunk* chunk); void put(Chunk* chunk);
void put(int x, int z, const ubyte* voxelData); void put(int x, int z, const ubyte* voxelData);
int getVoxelRegionVersion(int x, int z); int getVoxelRegionVersion(int x, int z);
int getVoxelRegionsVersion(); int getVoxelRegionsVersion();
ubyte* getChunk(int x, int z); ubyte* getChunk(int x, int z);
light_t* getLights(int x, int z); light_t* getLights(int x, int z);
chunk_inventories_map fetchInventories(int x, int z); chunk_inventories_map fetchInventories(int x, int z);
bool readWorldInfo(World* world); bool readWorldInfo(World* world);
bool readPlayer(std::shared_ptr<Player> player); bool readPlayer(std::shared_ptr<Player> player);
void writeRegion(int x, int y, void writeRegion(int x, int y, WorldRegion* entry, fs::path file, int layer);
WorldRegion* entry,
fs::path file, /// @brief Write player data to world files
int layer); /// @param player target player
void writePlayer(std::shared_ptr<Player> player); void writePlayer(std::shared_ptr<Player> player);
/* @param world world info to save (nullable) */
void write(const World* world, const Content* content); /// @brief Write all unsaved data to world files
void writePacks(const World* world); /// @param world target world
void writeIndices(const ContentIndices* indices); /// @param content world content
/* Append pack to packs.list without duplicate check */ void write(const World* world, const Content* content);
void writePacks(const World* world);
void writeIndices(const ContentIndices* indices);
/// @brief Append pack to the packs list without duplicate check and
/// dependencies resolve
/// @param world target world
/// @param id pack id
void addPack(const World* world, const std::string& id); void addPack(const World* world, const std::string& id);
/// @brief Remove pack from the list (does not remove indices)
/// @param world target world
/// @param id pack id
void removePack(const World* world, const std::string& id);
static const char* WORLD_FILE; static const char* WORLD_FILE;
}; };

View File

@ -170,6 +170,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool hudVisible
shader->uniform1f("u_gamma", settings.graphics.gamma); shader->uniform1f("u_gamma", settings.graphics.gamma);
shader->uniform1f("u_fogFactor", fogFactor); shader->uniform1f("u_fogFactor", fogFactor);
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve); shader->uniform1f("u_fogCurve", settings.graphics.fogCurve);
shader->uniform1f("u_dayTime", level->world->daytime);
shader->uniform3f("u_cameraPos", camera->position); shader->uniform3f("u_cameraPos", camera->position);
shader->uniform1i("u_cubemap", 1); shader->uniform1i("u_cubemap", 1);
{ {

View File

@ -342,7 +342,8 @@ std::shared_ptr<Panel> create_packs_panel(
const std::vector<ContentPack>& packs, const std::vector<ContentPack>& packs,
Engine* engine, Engine* engine,
bool backbutton, bool backbutton,
packconsumer callback packconsumer callback,
packconsumer remover
){ ){
auto assets = engine->getAssets(); auto assets = engine->getAssets();
auto panel = std::make_shared<Panel>(vec2(550, 200), vec4(5.0f)); auto panel = std::make_shared<Panel>(vec2(550, 200), vec4(5.0f));
@ -358,7 +359,12 @@ std::shared_ptr<Panel> create_packs_panel(
callback(pack); callback(pack);
}); });
} }
auto idlabel = std::make_shared<Label>("["+pack.id+"]"); auto runtime = engine->getContent()->getPackRuntime(pack.id);
auto idlabel = std::make_shared<Label>(
(runtime && runtime->getStats().hasSavingContent())
? "*["+pack.id+"]"
: "["+pack.id+"]"
);
idlabel->setColor(vec4(1, 1, 1, 0.5f)); idlabel->setColor(vec4(1, 1, 1, 0.5f));
idlabel->setSize(vec2(300, 25)); idlabel->setSize(vec2(300, 25));
idlabel->setAlign(Align::right); idlabel->setAlign(Align::right);
@ -390,6 +396,19 @@ std::shared_ptr<Panel> create_packs_panel(
packpanel->add(descriptionlabel, vec2(80, 28)); packpanel->add(descriptionlabel, vec2(80, 28));
packpanel->add(std::make_shared<Image>(icon, vec2(64)), vec2(8)); packpanel->add(std::make_shared<Image>(icon, vec2(64)), vec2(8));
if (remover && pack.id != "base") {
auto remimg = std::make_shared<Image>("gui/cross", vec2(32));
remimg->setColor(vec4(1.f, 1.f, 1.f, 0.5f));
auto rembtn = std::make_shared<Button>(remimg, vec4(2));
rembtn->setColor(vec4(0.0f));
rembtn->setHoverColor(vec4(1.0f, 1.0f, 1.0f, 0.17f));
rembtn->listenAction([=](GUI* gui) {
remover(pack);
});
packpanel->add(rembtn, vec2(470, 22));
}
panel->add(packpanel); panel->add(packpanel);
} }
if (backbutton) { if (backbutton) {
@ -398,8 +417,15 @@ std::shared_ptr<Panel> create_packs_panel(
return panel; return panel;
} }
static void reopen_world(Engine* engine, World* world) {
std::string wname = world->getName();
engine->setScreen(nullptr);
engine->setScreen(std::make_shared<MenuScreen>(engine));
open_world(wname, engine);
}
// TODO: refactor // TODO: refactor
void create_content_panel(Engine* engine) { void create_content_panel(Engine* engine, Level* level) {
auto menu = engine->getGUI()->getMenu(); auto menu = engine->getGUI()->getMenu();
auto paths = engine->getPaths(); auto paths = engine->getPaths();
auto mainPanel = create_page(engine, "content", 550, 0.0f, 5); auto mainPanel = create_page(engine, "content", 550, 0.0f, 5);
@ -415,16 +441,29 @@ void create_content_panel(Engine* engine) {
} }
} }
auto panel = create_packs_panel(engine->getContentPacks(), engine, false, nullptr); auto panel = create_packs_panel(
engine->getContentPacks(), engine, false, nullptr,
[=](const ContentPack& pack) {
auto runtime = engine->getContent()->getPackRuntime(pack.id);
if (runtime->getStats().hasSavingContent()) {
guiutil::confirm(engine->getGUI(), langs::get(L"remove-confirm", L"pack")+
L" ("+util::str2wstr_utf8(pack.id)+L")", [=]() {
// FIXME: work in progress
});
} else {
auto world = level->getWorld();
world->wfile->removePack(world, pack.id);
reopen_world(engine, world);
}
}
);
mainPanel->add(panel); mainPanel->add(panel);
mainPanel->add(create_button( mainPanel->add(create_button(
langs::get(L"Add", L"content"), vec4(10.0f), vec4(1), [=](GUI* gui) { langs::get(L"Add", L"content"), vec4(10.0f), vec4(1), [=](GUI* gui) {
auto panel = create_packs_panel(scanned, engine, true, auto panel = create_packs_panel(scanned, engine, true,
[=](const ContentPack& pack) { [=](const ContentPack& pack) {
auto screen = dynamic_cast<LevelScreen*>(engine->getScreen().get());
auto level = screen->getLevel();
auto world = level->getWorld(); auto world = level->getWorld();
auto worldFolder = paths->getWorldFolder(); auto worldFolder = paths->getWorldFolder();
for (const auto& dependency : pack.dependencies) { for (const auto& dependency : pack.dependencies) {
fs::path folder = ContentPack::findPack(paths, worldFolder, dependency); fs::path folder = ContentPack::findPack(paths, worldFolder, dependency);
@ -437,14 +476,9 @@ void create_content_panel(Engine* engine) {
world->wfile->addPack(world, dependency); world->wfile->addPack(world, dependency);
} }
} }
world->wfile->addPack(world, pack.id); world->wfile->addPack(world, pack.id);
reopen_world(engine, world);
std::string wname = world->getName(); }, nullptr);
engine->setScreen(nullptr);
engine->setScreen(std::make_shared<MenuScreen>(engine));
open_world(wname, engine);
});
menu->addPage("content-packs", panel); menu->addPage("content-packs", panel);
menu->setPage("content-packs"); menu->setPage("content-packs");
})); }));
@ -691,7 +725,7 @@ void create_settings_panel(Engine* engine) {
panel->add(guiutil::backButton(menu)); panel->add(guiutil::backButton(menu));
} }
void create_pause_panel(Engine* engine) { void menus::create_pause_panel(Engine* engine, Level* level) {
auto menu = engine->getGUI()->getMenu(); auto menu = engine->getGUI()->getMenu();
auto panel = create_page(engine, "pause", 400, 0.0f, 1); auto panel = create_page(engine, "pause", 400, 0.0f, 1);
@ -699,7 +733,7 @@ void create_pause_panel(Engine* engine) {
menu->reset(); menu->reset();
})); }));
panel->add(create_button(L"Content", vec4(10.0f), vec4(1), [=](GUI*) { panel->add(create_button(L"Content", vec4(10.0f), vec4(1), [=](GUI*) {
create_content_panel(engine); create_content_panel(engine, level);
menu->setPage("content"); menu->setPage("content");
})); }));
panel->add(guiutil::gotoButton(L"Settings", "settings", menu)); panel->add(guiutil::gotoButton(L"Settings", "settings", menu));
@ -717,10 +751,9 @@ void menus::create_menus(Engine* engine) {
create_new_world_panel(engine); create_new_world_panel(engine);
create_settings_panel(engine); create_settings_panel(engine);
create_controls_panel(engine); create_controls_panel(engine);
create_pause_panel(engine);
create_languages_panel(engine); create_languages_panel(engine);
create_world_generators_panel(engine);
create_main_menu_panel(engine); create_main_menu_panel(engine);
create_world_generators_panel(engine);
} }
void menus::refresh_menus(Engine* engine) { void menus::refresh_menus(Engine* engine) {

View File

@ -2,9 +2,11 @@
#define FRONTEND_MENU_H_ #define FRONTEND_MENU_H_
class Engine; class Engine;
class Level;
namespace menus { namespace menus {
void create_version_label(Engine* engine); void create_version_label(Engine* engine);
void create_pause_panel(Engine* engine, Level* level);
void create_menus(Engine* engine); void create_menus(Engine* engine);
void refresh_menus(Engine* engine); void refresh_menus(Engine* engine);
} }

View File

@ -87,22 +87,25 @@ void MenuScreen::draw(float delta) {
static bool backlight; static bool backlight;
LevelScreen::LevelScreen(Engine* engine, Level* level) LevelScreen::LevelScreen(Engine* engine, Level* level) : Screen(engine), level(level){
: Screen(engine), menus::create_pause_panel(engine, level);
level(level),
frontend(std::make_unique<LevelFrontend>(level, engine->getAssets())),
hud(std::make_unique<Hud>(engine, frontend.get())),
worldRenderer(std::make_unique<WorldRenderer>(engine, frontend.get())),
controller(std::make_unique<LevelController>(engine->getSettings(), level)) {
auto& settings = engine->getSettings(); auto& settings = engine->getSettings();
auto assets = engine->getAssets();
controller = std::make_unique<LevelController>(settings, level);
frontend = std::make_unique<LevelFrontend>(level, assets);
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get());
hud = std::make_unique<Hud>(engine, frontend.get());
backlight = settings.graphics.backlight; backlight = settings.graphics.backlight;
animator.reset(new TextureAnimator()); animator = std::make_unique<TextureAnimator>();
animator->addAnimations(engine->getAssets()->getAnimations()); animator->addAnimations(assets->getAnimations());
auto content = level->content; auto content = level->content;
for (auto& pack : content->getPacks()) { for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();
const ContentPack& info = pack->getInfo(); const ContentPack& info = pack->getInfo();
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
if (fs::is_regular_file(scriptFile)) { if (fs::is_regular_file(scriptFile)) {

View File

@ -4,9 +4,11 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <string.h> #include <string.h>
bool Events::_keys[KEYS_BUFFER_SIZE] = {}; inline constexpr short _MOUSE_KEYS_OFFSET = 1024;
uint Events::_frames[KEYS_BUFFER_SIZE] = {};
uint Events::_current = 0; bool Events::keys[KEYS_BUFFER_SIZE] = {};
uint Events::frames[KEYS_BUFFER_SIZE] = {};
uint Events::currentFrame = 0;
int Events::scroll = 0; int Events::scroll = 0;
glm::vec2 Events::delta = {}; glm::vec2 Events::delta = {};
glm::vec2 Events::cursor = {}; glm::vec2 Events::cursor = {};
@ -17,125 +19,124 @@ std::vector<keycode> Events::pressedKeys;
std::unordered_map<std::string, Binding> Events::bindings; std::unordered_map<std::string, Binding> Events::bindings;
bool Events::pressed(keycode keycode) { bool Events::pressed(keycode keycode) {
return pressed(static_cast<int>(keycode)); return pressed(static_cast<int>(keycode));
} }
bool Events::pressed(int keycode) { bool Events::pressed(int keycode) {
if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) { if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) {
fprintf(stderr, "pressed %i\n", keycode); //FIXME: unreasonable cstdio usage return false;
return false; }
} return keys[keycode];
return _keys[keycode];
} }
bool Events::jpressed(keycode keycode) { bool Events::jpressed(keycode keycode) {
return jpressed(static_cast<int>(keycode)); return jpressed(static_cast<int>(keycode));
} }
bool Events::jpressed(int keycode) { bool Events::jpressed(int keycode) {
return Events::pressed(keycode) && _frames[keycode] == _current; return Events::pressed(keycode) && frames[keycode] == currentFrame;
} }
bool Events::clicked(mousecode button) { bool Events::clicked(mousecode button) {
return clicked(static_cast<int>(button)); return clicked(static_cast<int>(button));
} }
bool Events::clicked(int button) { bool Events::clicked(int button) {
return Events::pressed(_MOUSE_KEYS_OFFSET + button); return Events::pressed(_MOUSE_KEYS_OFFSET + button);
} }
bool Events::jclicked(mousecode button) { bool Events::jclicked(mousecode button) {
return jclicked(static_cast<int>(button)); return jclicked(static_cast<int>(button));
} }
bool Events::jclicked(int button) { bool Events::jclicked(int button) {
return Events::jpressed(_MOUSE_KEYS_OFFSET + button); return Events::jpressed(_MOUSE_KEYS_OFFSET + button);
} }
void Events::toggleCursor() { void Events::toggleCursor() {
_cursor_locked = !_cursor_locked; _cursor_locked = !_cursor_locked;
Window::setCursorMode(_cursor_locked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); Window::setCursorMode(_cursor_locked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
} }
void Events::pollEvents() { void Events::pollEvents() {
_current++; currentFrame++;
delta.x = 0.f; delta.x = 0.f;
delta.y = 0.f; delta.y = 0.f;
scroll = 0; scroll = 0;
codepoints.clear(); codepoints.clear();
pressedKeys.clear(); pressedKeys.clear();
glfwPollEvents(); glfwPollEvents();
for (auto& entry : bindings) { for (auto& entry : bindings) {
auto& binding = entry.second; auto& binding = entry.second;
binding.justChange = false; binding.justChange = false;
bool newstate = false; bool newstate = false;
switch (binding.type) { switch (binding.type) {
case inputtype::keyboard: newstate = pressed(binding.code); break; case inputtype::keyboard: newstate = pressed(binding.code); break;
case inputtype::mouse: newstate = clicked(binding.code); break; case inputtype::mouse: newstate = clicked(binding.code); break;
} }
if (newstate) { if (newstate) {
if (!binding.state) { if (!binding.state) {
binding.state = true; binding.state = true;
binding.justChange = true; binding.justChange = true;
} }
} }
else { else {
if (binding.state) { if (binding.state) {
binding.state = false; binding.state = false;
binding.justChange = true; binding.justChange = true;
} }
} }
} }
} }
void Events::bind(std::string name, inputtype type, keycode code) { void Events::bind(std::string name, inputtype type, keycode code) {
bind(name, type, static_cast<int>(code)); bind(name, type, static_cast<int>(code));
} }
void Events::bind(std::string name, inputtype type, mousecode code) { void Events::bind(std::string name, inputtype type, mousecode code) {
bind(name, type, static_cast<int>(code)); bind(name, type, static_cast<int>(code));
} }
void Events::bind(std::string name, inputtype type, int code) { void Events::bind(std::string name, inputtype type, int code) {
bindings[name] = { type, code, false, false }; bindings[name] = { type, code, false, false };
} }
bool Events::active(std::string name) { bool Events::active(std::string name) {
const auto& found = bindings.find(name); const auto& found = bindings.find(name);
if (found == bindings.end()) { if (found == bindings.end()) {
return false; return false;
} }
return found->second.active(); return found->second.active();
} }
bool Events::jactive(std::string name) { bool Events::jactive(std::string name) {
const auto& found = bindings.find(name); const auto& found = bindings.find(name);
if (found == bindings.end()) { if (found == bindings.end()) {
return false; return false;
} }
return found->second.jactive(); return found->second.jactive();
} }
void Events::setKey(int key, bool b) { void Events::setKey(int key, bool b) {
Events::_keys[key] = b; Events::keys[key] = b;
Events::_frames[key] = Events::_current; Events::frames[key] = currentFrame;
} }
void Events::setButton(int button, bool b) { void Events::setButton(int button, bool b) {
setKey(_MOUSE_KEYS_OFFSET + button, b); setKey(_MOUSE_KEYS_OFFSET + button, b);
} }
void Events::setPosition(float xpos, float ypos) { void Events::setPosition(float xpos, float ypos) {
if (Events::cursor_drag) { if (Events::cursor_drag) {
Events::delta.x += xpos - Events::cursor.x; Events::delta.x += xpos - Events::cursor.x;
Events::delta.y += ypos - Events::cursor.y; Events::delta.y += ypos - Events::cursor.y;
} }
else { else {
Events::cursor_drag = true; Events::cursor_drag = true;
} }
Events::cursor.x = xpos; Events::cursor.x = xpos;
Events::cursor.y = ypos; Events::cursor.y = ypos;
} }

View File

@ -7,45 +7,43 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include "../typedefs.h"
typedef unsigned int uint; inline constexpr short KEYS_BUFFER_SIZE = 1036;
const short KEYS_BUFFER_SIZE = 1036;
const short _MOUSE_KEYS_OFFSET = 1024;
class Events { class Events {
static bool keys[KEYS_BUFFER_SIZE];
static uint frames[KEYS_BUFFER_SIZE];
static uint currentFrame;
static bool cursor_drag;
public: public:
static bool _keys[KEYS_BUFFER_SIZE];
static uint _frames[KEYS_BUFFER_SIZE];
static uint _current;
static int scroll; static int scroll;
static glm::vec2 delta; static glm::vec2 delta;
static glm::vec2 cursor; static glm::vec2 cursor;
static bool cursor_drag; static bool _cursor_locked;
static bool _cursor_locked; static std::vector<uint> codepoints;
static std::vector<uint> codepoints; static std::vector<keycode> pressedKeys;
static std::vector<keycode> pressedKeys; static std::unordered_map<std::string, Binding> bindings;
static std::unordered_map<std::string, Binding> bindings;
static void pollEvents(); static void pollEvents();
static bool pressed(keycode keycode); static bool pressed(keycode keycode);
static bool pressed(int keycode); static bool pressed(int keycode);
static bool jpressed(keycode keycode); static bool jpressed(keycode keycode);
static bool jpressed(int keycode); static bool jpressed(int keycode);
static bool clicked(mousecode button); static bool clicked(mousecode button);
static bool clicked(int button); static bool clicked(int button);
static bool jclicked(mousecode button); static bool jclicked(mousecode button);
static bool jclicked(int button); static bool jclicked(int button);
static void toggleCursor(); static void toggleCursor();
static void bind(std::string name, inputtype type, keycode code); static void bind(std::string name, inputtype type, keycode code);
static void bind(std::string name, inputtype type, mousecode code); static void bind(std::string name, inputtype type, mousecode code);
static void bind(std::string name, inputtype type, int code); static void bind(std::string name, inputtype type, int code);
static bool active(std::string name); static bool active(std::string name);
static bool jactive(std::string name); static bool jactive(std::string name);
static void setKey(int key, bool b); static void setKey(int key, bool b);
static void setButton(int button, bool b); static void setButton(int button, bool b);