summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruntodesu <kirill@untode.su>2025-09-11 14:13:39 +0500
committeruntodesu <kirill@untode.su>2025-09-11 14:13:39 +0500
commit8784cbfebcb8a0220fb947a6070032e20b80fc2f (patch)
tree2e03a2c013ed7b19a5dafaba1ddfb05c1878449a
parentf0cc06c7388acb32b86301965c5b2547e4e3b919 (diff)
downloadvoxelius-8784cbfebcb8a0220fb947a6070032e20b80fc2f.tar.bz2
voxelius-8784cbfebcb8a0220fb947a6070032e20b80fc2f.zip
Another qfengine graft: resource management
DECOPYPASTA DECOPYPASTA DECOPYPASTA DECOPYPASTA
-rw-r--r--core/pch.hh1
-rw-r--r--core/resource/CMakeLists.txt1
-rw-r--r--core/resource/binfile.cc68
-rw-r--r--core/resource/binfile.hh2
-rw-r--r--core/resource/image.cc96
-rw-r--r--core/resource/image.hh2
-rw-r--r--core/resource/resource.cc129
-rw-r--r--core/resource/resource.hh43
-rw-r--r--core/utils/CMakeLists.txt2
-rw-r--r--core/utils/physfs.cc76
-rw-r--r--core/utils/physfs.hh18
-rw-r--r--game/client/main.cc17
-rw-r--r--game/client/resource/sound_effect.cc64
-rw-r--r--game/client/resource/sound_effect.hh2
-rw-r--r--game/client/resource/texture_gui.cc56
-rw-r--r--game/client/resource/texture_gui.hh4
-rw-r--r--game/server/main.cc9
17 files changed, 374 insertions, 216 deletions
diff --git a/core/pch.hh b/core/pch.hh
index 3a013bd..dedd310 100644
--- a/core/pch.hh
+++ b/core/pch.hh
@@ -21,6 +21,7 @@
#include <stdexcept>
#include <thread>
#include <type_traits>
+#include <typeindex>
#include <unordered_map>
#include <unordered_set>
#include <vector>
diff --git a/core/resource/CMakeLists.txt b/core/resource/CMakeLists.txt
index ef2ffae..7df4306 100644
--- a/core/resource/CMakeLists.txt
+++ b/core/resource/CMakeLists.txt
@@ -3,4 +3,5 @@ target_sources(core PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/binfile.hh"
"${CMAKE_CURRENT_LIST_DIR}/image.cc"
"${CMAKE_CURRENT_LIST_DIR}/image.hh"
+ "${CMAKE_CURRENT_LIST_DIR}/resource.cc"
"${CMAKE_CURRENT_LIST_DIR}/resource.hh")
diff --git a/core/resource/binfile.cc b/core/resource/binfile.cc
index 726ef9b..0e6efa8 100644
--- a/core/resource/binfile.cc
+++ b/core/resource/binfile.cc
@@ -4,68 +4,48 @@
#include "core/resource/resource.hh"
-static emhash8::HashMap<std::string, resource_ptr<BinFile>> resource_map;
+#include "core/utils/physfs.hh"
-template<>
-resource_ptr<BinFile> resource::load<BinFile>(std::string_view name, unsigned int flags)
+static const void* binfile_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
+ assert(name);
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
+ auto file = PHYSFS_openRead(name);
+
+ if(file == nullptr) {
+ spdlog::error("binfile: {}: {}", name, utils::physfs_error());
+ return nullptr;
}
- auto file = PHYSFS_openRead(std::string(name).c_str());
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
- if(file == nullptr) {
- spdlog::warn("resource: {}: {}", name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ if(file_size < 0) {
+ spdlog::error("binfile: {}: {}", name, utils::physfs_error());
+ PHYSFS_close(file);
return nullptr;
}
- auto new_resource = std::make_shared<BinFile>();
- new_resource->size = PHYSFS_fileLength(file);
- new_resource->buffer = new std::byte[new_resource->size];
+ auto binfile = new BinFile();
+ binfile->size = static_cast<std::size_t>(file_size);
+ binfile->buffer = new std::byte[binfile->size];
- PHYSFS_readBytes(file, new_resource->buffer, new_resource->size);
+ PHYSFS_readBytes(file, binfile->buffer, file_size);
PHYSFS_close(file);
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
+ return binfile;
}
-template<>
-void resource::hard_cleanup<BinFile>(void)
+static void binfile_free_func(const void* resource)
{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [BinFile] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [BinFile] {}", it.first);
- }
+ assert(resource);
- delete[] it.second->buffer;
- }
+ auto binfile = reinterpret_cast<const BinFile*>(resource);
+ delete[] binfile->buffer;
- resource_map.clear();
+ delete binfile;
}
-template<>
-void resource::soft_cleanup<BinFile>(void)
+void BinFile::register_resource(void)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [BinFile] {}", iter->first);
-
- delete[] iter->second->buffer;
-
- iter = resource_map.erase(iter);
-
- continue;
- }
-
- iter = std::next(iter);
- }
+ resource::register_loader<BinFile>(&binfile_load_func, &binfile_free_func);
}
diff --git a/core/resource/binfile.hh b/core/resource/binfile.hh
index 6bb5e3d..5f24d77 100644
--- a/core/resource/binfile.hh
+++ b/core/resource/binfile.hh
@@ -3,6 +3,8 @@
#pragma once
struct BinFile final {
+ static void register_resource(void);
+
std::byte* buffer;
std::size_t size;
};
diff --git a/core/resource/image.cc b/core/resource/image.cc
index 8b570e4..d5e256d 100644
--- a/core/resource/image.cc
+++ b/core/resource/image.cc
@@ -4,7 +4,7 @@
#include "core/resource/resource.hh"
-static emhash8::HashMap<std::string, resource_ptr<Image>> resource_map;
+#include "core/utils/physfs.hh"
static int stbi_physfs_read(void* context, char* data, int size)
{
@@ -22,94 +22,60 @@ static int stbi_physfs_eof(void* context)
return PHYSFS_eof(reinterpret_cast<PHYSFS_File*>(context));
}
-template<>
-resource_ptr<Image> resource::load<Image>(std::string_view name, unsigned int flags)
+static const void* image_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
+ assert(name);
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
- }
+ stbi_io_callbacks callbacks;
+ callbacks.read = &stbi_physfs_read;
+ callbacks.skip = &stbi_physfs_skip;
+ callbacks.eof = &stbi_physfs_eof;
+
+ stbi_set_flip_vertically_on_load(bool(flags & IMAGE_LOAD_FLIP));
- auto file = PHYSFS_openRead(std::string(name).c_str());
+ auto file = PHYSFS_openRead(name);
if(file == nullptr) {
- spdlog::warn("resource: {}: {}", name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ spdlog::error("image: {}: {}", name, utils::physfs_error());
return nullptr;
}
- if(flags & IMAGE_LOAD_FLIP) {
- stbi_set_flip_vertically_on_load(true);
- }
- else {
- stbi_set_flip_vertically_on_load(false);
- }
-
- stbi_io_callbacks callbacks;
- callbacks.read = &stbi_physfs_read;
- callbacks.skip = &stbi_physfs_skip;
- callbacks.eof = &stbi_physfs_eof;
-
- auto new_resource = std::make_shared<Image>();
+ int desired_channels;
if(flags & IMAGE_LOAD_GRAY) {
- new_resource->pixels = stbi_load_from_callbacks(&callbacks, file, &new_resource->size.x, &new_resource->size.y, nullptr, STBI_grey);
+ desired_channels = STBI_grey;
}
else {
- new_resource->pixels = stbi_load_from_callbacks(
- &callbacks, file, &new_resource->size.x, &new_resource->size.y, nullptr, STBI_rgb_alpha);
+ desired_channels = STBI_rgb_alpha;
}
- PHYSFS_close(file);
+ int width, height, channels;
+ auto pixels = stbi_load_from_callbacks(&callbacks, file, &width, &height, &channels, desired_channels);
- if(new_resource->pixels == nullptr) {
- spdlog::warn("resource: {}: {}", name, stbi_failure_reason());
- return nullptr;
- }
+ PHYSFS_close(file);
- if(new_resource->size.x <= 0 || new_resource->size.y <= 0) {
- spdlog::warn("resource {}: invalid dimensions", name);
- stbi_image_free(new_resource->pixels);
+ if(pixels == nullptr) {
+ spdlog::error("image: {}: {}", name, stbi_failure_reason());
return nullptr;
}
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
+ auto image = new Image;
+ image->pixels = pixels;
+ image->size = glm::ivec2(width, height);
+ return image;
}
-template<>
-void resource::hard_cleanup<Image>(void)
+static void image_free_func(const void* resource)
{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [Image] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [Image] {}", it.first);
- }
-
- stbi_image_free(it.second->pixels);
- }
+ assert(resource);
+
+ auto image = reinterpret_cast<const Image*>(resource);
+ stbi_image_free(image->pixels);
- resource_map.clear();
+ delete image;
}
-template<>
-void resource::soft_cleanup<Image>(void)
+void Image::register_resource(void)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [Image] {}", iter->first);
-
- stbi_image_free(iter->second->pixels);
-
- iter = resource_map.erase(iter);
-
- continue;
- }
-
- iter = std::next(iter);
- }
+ resource::register_loader<Image>(&image_load_func, &image_free_func);
}
diff --git a/core/resource/image.hh b/core/resource/image.hh
index 13e912e..b62fcbb 100644
--- a/core/resource/image.hh
+++ b/core/resource/image.hh
@@ -6,6 +6,8 @@ constexpr static unsigned int IMAGE_LOAD_GRAY = 0x0001U;
constexpr static unsigned int IMAGE_LOAD_FLIP = 0x0002U;
struct Image final {
+ static void register_resource(void);
+
stbi_uc* pixels;
glm::ivec2 size;
};
diff --git a/core/resource/resource.cc b/core/resource/resource.cc
new file mode 100644
index 0000000..600843f
--- /dev/null
+++ b/core/resource/resource.cc
@@ -0,0 +1,129 @@
+#include "core/pch.hh"
+
+#include "core/resource/resource.hh"
+
+struct ResourceLoader final {
+ ResourceLoadFunc load_func;
+ ResourceFreeFunc free_func;
+ emhash8::HashMap<std::string, std::shared_ptr<const void>> resources;
+ std::vector<std::shared_ptr<const void>> cache;
+ std::string class_name;
+};
+
+namespace
+{
+emhash8::HashMap<std::type_index, std::unique_ptr<ResourceLoader>> loaders;
+} // namespace
+
+void resource::detail::register_loader(const std::type_info& type, ResourceLoadFunc load_func, ResourceFreeFunc free_func)
+{
+ assert(load_func);
+ assert(free_func);
+
+ auto type_index = std::type_index(type);
+ auto loader = std::make_unique<ResourceLoader>();
+ loader->class_name = type.name();
+ loader->load_func = load_func;
+ loader->free_func = free_func;
+
+ assert(!loaders.contains(type_index));
+
+ loaders.insert_or_assign(type_index, std::move(loader));
+}
+
+std::shared_ptr<const void> resource::detail::load_resource(const std::type_info& type, std::string_view name, std::uint32_t flags)
+{
+ auto name_str = std::string(name);
+ auto type_index = std::type_index(type);
+ auto loader = loaders.find(type_index);
+
+ if(loader == loaders.cend()) {
+ spdlog::error("resource: no loader registered for type [{}]", type.name());
+ return nullptr;
+ }
+
+ auto resource_it = loader->second->resources.find(name_str);
+
+ if(resource_it == loader->second->resources.cend()) {
+ auto resource_raw = loader->second->load_func(name_str.c_str(), flags);
+
+ if(resource_raw == nullptr) {
+ spdlog::error("resource: {} [{}]: load failed", loader->second->class_name, name);
+ return nullptr;
+ }
+
+ std::shared_ptr<const void> resource_ptr(resource_raw, [](const void* ptr) { /* empty */ });
+ auto loaded_it = loader->second->resources.insert_or_assign(name_str, std::move(resource_ptr));
+
+ if(flags & RESOURCE_CACHE) {
+ loader->second->cache.push_back(loaded_it.first->second);
+ }
+
+ return loaded_it.first->second;
+ }
+
+ return resource_it->second;
+}
+
+std::shared_ptr<const void> resource::detail::find_resource(const std::type_info& type, std::string_view name)
+{
+ auto name_str = std::string(name);
+ auto type_index = std::type_index(type);
+ auto loader = loaders.find(type_index);
+
+ if(loader == loaders.cend()) {
+ spdlog::error("resource: no loader registered for type [{}]", type.name());
+ return nullptr;
+ }
+
+ auto resource_it = loader->second->resources.find(name_str);
+
+ if(resource_it == loader->second->resources.cend()) {
+ spdlog::error("resource: {} [{}]: not found", loader->second->class_name, name);
+ return nullptr;
+ }
+
+ return resource_it->second;
+}
+
+void resource::hard_cleanup(void)
+{
+ for(auto& [type_index, loader] : loaders) {
+ loader->cache.clear();
+
+ for(auto& [name, resource_ptr] : loader->resources) {
+ if(resource_ptr.use_count() > 1) {
+ spdlog::warn("resource: zombie resource: {} [{}] [use_count={}]", name, loader->class_name, resource_ptr.use_count());
+ }
+ else {
+ spdlog::debug("resource: releasing {} [{}]", name, loader->class_name);
+ }
+
+ loader->free_func(resource_ptr.get());
+ }
+
+ loader->resources.clear();
+ }
+
+ loaders.clear();
+}
+
+void resource::soft_cleanup(void)
+{
+ for(auto& [type_index, loader] : loaders) {
+ auto resource_it = loader->resources.begin();
+
+ while(resource_it != loader->resources.end()) {
+ if(resource_it->second.use_count() <= 1) {
+ spdlog::debug("resource: releasing {} [{}]", resource_it->first, loader->class_name);
+
+ loader->free_func(resource_it->second.get());
+ resource_it = loader->resources.erase(resource_it);
+
+ continue;
+ }
+
+ resource_it = std::next(resource_it);
+ }
+ }
+}
diff --git a/core/resource/resource.hh b/core/resource/resource.hh
index 5b6b2ac..ad8d944 100644
--- a/core/resource/resource.hh
+++ b/core/resource/resource.hh
@@ -5,14 +5,53 @@
template<typename T>
using resource_ptr = std::shared_ptr<const T>;
+constexpr std::uint32_t RESOURCE_CACHE = 0x00000001U; ///< Cache the resource after loading
+constexpr std::uint32_t RESOURCE_USER = 0xFFFFFF00U; ///< User-defined flags for custom behavior
+
+using ResourceLoadFunc = const void* (*)(const char* name, std::uint32_t flags);
+using ResourceFreeFunc = void (*)(const void* resource);
+
+namespace resource::detail
+{
+void register_loader(const std::type_info& type, ResourceLoadFunc load_func, ResourceFreeFunc free_func);
+resource_ptr<void> load_resource(const std::type_info& type, std::string_view name, std::uint32_t flags);
+resource_ptr<void> find_resource(const std::type_info& type, std::string_view name);
+} // namespace resource::detail
+
namespace resource
{
template<typename T>
-resource_ptr<T> load(std::string_view name, unsigned int flags = 0U);
+void register_loader(ResourceLoadFunc load_func, ResourceFreeFunc free_func);
template<typename T>
-void hard_cleanup(void);
+resource_ptr<T> load(std::string_view name, std::uint32_t flags = 0U);
template<typename T>
+resource_ptr<T> find(std::string_view name);
+} // namespace resource
+
+namespace resource
+{
+void hard_cleanup(void);
void soft_cleanup(void);
} // namespace resource
+template<typename T>
+void resource::register_loader(ResourceLoadFunc load_func, ResourceFreeFunc free_func)
+{
+ resource::detail::register_loader(typeid(T), load_func, free_func);
+}
+
+template<typename T>
+resource_ptr<T> resource::load(std::string_view name, std::uint32_t flags)
+{
+ auto result = resource::detail::load_resource(typeid(T), name, flags);
+ return std::reinterpret_pointer_cast<const T>(result);
+}
+
+template<typename T>
+resource_ptr<T> resource::find(std::string_view name)
+{
+ auto result = resource::detail::find_resource(typeid(T), name);
+ return std::reinterpret_pointer_cast<const T>(result);
+}
+
#endif // CORE_RESOURCE_RESOURCE_HH
diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt
index 4f96261..5d59e8d 100644
--- a/core/utils/CMakeLists.txt
+++ b/core/utils/CMakeLists.txt
@@ -1,5 +1,7 @@
target_sources(core PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/epoch.cc"
"${CMAKE_CURRENT_LIST_DIR}/epoch.hh"
+ "${CMAKE_CURRENT_LIST_DIR}/physfs.cc"
+ "${CMAKE_CURRENT_LIST_DIR}/physfs.hh"
"${CMAKE_CURRENT_LIST_DIR}/string.cc"
"${CMAKE_CURRENT_LIST_DIR}/string.hh")
diff --git a/core/utils/physfs.cc b/core/utils/physfs.cc
new file mode 100644
index 0000000..cb310df
--- /dev/null
+++ b/core/utils/physfs.cc
@@ -0,0 +1,76 @@
+#include "core/pch.hh"
+
+#include "core/utils/physfs.hh"
+
+bool utils::read_file(std::string_view path, std::vector<std::byte>& buffer)
+{
+ auto file = PHYSFS_openRead(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
+ buffer.resize(static_cast<std::size_t>(file_size));
+
+ PHYSFS_readBytes(file, buffer.data(), file_size);
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::read_file(std::string_view path, std::string& buffer)
+{
+ auto file = PHYSFS_openRead(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
+ buffer.resize(static_cast<std::size_t>(file_size));
+
+ PHYSFS_readBytes(file, buffer.data(), file_size);
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::write_file(std::string_view path, const std::vector<std::byte>& buffer)
+{
+ auto file = PHYSFS_openWrite(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_writeBytes(file, buffer.data(), static_cast<PHYSFS_uint64>(buffer.size()));
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::write_file(std::string_view path, const std::string& buffer)
+{
+ auto file = PHYSFS_openWrite(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_writeBytes(file, buffer.data(), static_cast<PHYSFS_uint64>(buffer.size()));
+ PHYSFS_close(file);
+
+ return true;
+}
+
+std::string_view utils::physfs_error(void)
+{
+ auto error_code = PHYSFS_getLastErrorCode();
+ auto error_string = PHYSFS_getErrorByCode(error_code);
+ return std::string_view(error_string, std::strlen(error_string));
+}
diff --git a/core/utils/physfs.hh b/core/utils/physfs.hh
new file mode 100644
index 0000000..df5e392
--- /dev/null
+++ b/core/utils/physfs.hh
@@ -0,0 +1,18 @@
+#ifndef UTILS_PHYSFS_HH
+#define UTILS_PHYSFS_HH 1
+#pragma once
+
+namespace utils
+{
+bool read_file(std::string_view path, std::vector<std::byte>& buffer);
+bool read_file(std::string_view path, std::string& buffer);
+bool write_file(std::string_view path, const std::vector<std::byte>& buffer);
+bool write_file(std::string_view path, const std::string& buffer);
+} // namespace utils
+
+namespace utils
+{
+std::string_view physfs_error(void);
+} // namespace utils
+
+#endif // UTILS_PHYSFS_HH
diff --git a/game/client/main.cc b/game/client/main.cc
index 51d4670..72c116e 100644
--- a/game/client/main.cc
+++ b/game/client/main.cc
@@ -221,6 +221,11 @@ int main(int argc, char** argv)
spdlog::info("opengl: version: {}", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
spdlog::info("opengl: renderer: {}", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
+ BinFile::register_resource();
+ Image::register_resource();
+ TextureGUI::register_resource();
+ SoundEffect::register_resource();
+
glDisable(GL_MULTISAMPLE);
IMGUI_CHECKVERSION();
@@ -406,22 +411,14 @@ int main(int argc, char** argv)
globals::window_framecount += 1;
- resource::soft_cleanup<BinFile>();
- resource::soft_cleanup<Image>();
-
- resource::soft_cleanup<SoundEffect>();
- resource::soft_cleanup<TextureGUI>();
+ resource::soft_cleanup();
threading::update();
}
client_game::shutdown();
- resource::hard_cleanup<BinFile>();
- resource::hard_cleanup<Image>();
-
- resource::hard_cleanup<SoundEffect>();
- resource::hard_cleanup<TextureGUI>();
+ resource::hard_cleanup();
spdlog::info("client: shutdown after {} frames", globals::window_framecount);
spdlog::info("client: average framerate: {:.03f} FPS", 1.0f / globals::window_frametime_avg);
diff --git a/game/client/resource/sound_effect.cc b/game/client/resource/sound_effect.cc
index 75d5984..fad5b18 100644
--- a/game/client/resource/sound_effect.cc
+++ b/game/client/resource/sound_effect.cc
@@ -3,11 +3,10 @@
#include "client/resource/sound_effect.hh"
#include "core/resource/resource.hh"
+#include "core/utils/physfs.hh"
#include "client/globals.hh"
-static emhash8::HashMap<std::string, resource_ptr<SoundEffect>> resource_map;
-
static std::size_t drwav_read_physfs(void* file, void* output, std::size_t count)
{
return static_cast<std::size_t>(PHYSFS_readBytes(reinterpret_cast<PHYSFS_File*>(file), output, count));
@@ -23,38 +22,32 @@ static drwav_bool32 drwav_seek_physfs(void* file, int offset, drwav_seek_origin
}
}
-template<>
-resource_ptr<SoundEffect> resource::load<SoundEffect>(std::string_view name, unsigned int flags)
+static const void* sound_effect_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
-
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
- }
+ assert(name);
if(globals::sound_ctx == nullptr) {
// Sound is disabled
return nullptr;
}
- auto file = PHYSFS_openRead(std::string(name).c_str());
+ auto file = PHYSFS_openRead(name);
if(file == nullptr) {
- spdlog::warn("resource: {} [SoundEffect]: {}", name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ spdlog::warn("sfx: {}: {}", name, utils::physfs_error());
return nullptr;
}
drwav wav_info;
if(!drwav_init(&wav_info, &drwav_read_physfs, &drwav_seek_physfs, file, nullptr)) {
- spdlog::warn("resource: {} [SoundEffect]: drwav_init failed", name);
+ spdlog::warn("sfx: {}: drwav_init failed", name);
PHYSFS_close(file);
return nullptr;
}
if(wav_info.channels != 1) {
- spdlog::warn("resource: {} [SoundEffect]: only mono sound files are allowed", name);
+ spdlog::warn("sfx: {}: only mono sound files are allowed", name);
drwav_uninit(&wav_info);
PHYSFS_close(file);
return nullptr;
@@ -68,7 +61,7 @@ resource_ptr<SoundEffect> resource::load<SoundEffect>(std::string_view name, uns
drwav_uninit(&wav_info);
PHYSFS_close(file);
- auto new_resource = std::make_shared<SoundEffect>();
+ auto new_resource = new SoundEffect();
new_resource->name = std::string(name);
alGenBuffers(1, &new_resource->buffer);
@@ -76,42 +69,21 @@ resource_ptr<SoundEffect> resource::load<SoundEffect>(std::string_view name, uns
delete[] samples;
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
-}
-
-template<>
-void resource::hard_cleanup<SoundEffect>(void)
-{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [SoundEffect] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [SoundEffect] {}", it.first);
- }
-
- alDeleteBuffers(1, &it.second->buffer);
- }
-
- resource_map.clear();
+ return new_resource;
}
-template<>
-void resource::soft_cleanup<SoundEffect>(void)
+static void sound_effect_free_func(const void* resource)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [SoundEffect] {}", iter->first);
+ assert(resource);
- alDeleteBuffers(1, &iter->second->buffer);
+ auto sound_effect = reinterpret_cast<const SoundEffect*>(resource);
- iter = resource_map.erase(iter);
+ alDeleteBuffers(1, &sound_effect->buffer);
- continue;
- }
+ delete sound_effect;
+}
- iter = std::next(iter);
- }
+void SoundEffect::register_resource(void)
+{
+ resource::register_loader<SoundEffect>(&sound_effect_load_func, &sound_effect_free_func);
}
diff --git a/game/client/resource/sound_effect.hh b/game/client/resource/sound_effect.hh
index 8b1372b..8fa3da4 100644
--- a/game/client/resource/sound_effect.hh
+++ b/game/client/resource/sound_effect.hh
@@ -3,6 +3,8 @@
#pragma once
struct SoundEffect final {
+ static void register_resource(void);
+
std::string name;
ALuint buffer;
};
diff --git a/game/client/resource/texture_gui.cc b/game/client/resource/texture_gui.cc
index 415845d..beb2f54 100644
--- a/game/client/resource/texture_gui.cc
+++ b/game/client/resource/texture_gui.cc
@@ -5,17 +5,9 @@
#include "core/resource/image.hh"
#include "core/resource/resource.hh"
-static emhash8::HashMap<std::string, resource_ptr<TextureGUI>> resource_map;
-
-template<>
-resource_ptr<TextureGUI> resource::load<TextureGUI>(std::string_view name, unsigned int flags)
+static const void* texture_gui_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
-
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
- }
+ assert(name);
unsigned int image_load_flags = 0U;
@@ -62,54 +54,30 @@ resource_ptr<TextureGUI> resource::load<TextureGUI>(std::string_view name, unsig
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
- auto new_resource = std::make_shared<TextureGUI>();
+ auto new_resource = new TextureGUI();
new_resource->handle = static_cast<ImTextureID>(gl_texture);
new_resource->size.x = image->size.x;
new_resource->size.y = image->size.y;
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
+ return new_resource;
}
return nullptr;
}
-template<>
-void resource::hard_cleanup<TextureGUI>(void)
+static void texture_gui_free_func(const void* resource)
{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [TextureGUI] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [TextureGUI] {}", it.first);
- }
+ assert(resource);
- auto gl_texture = static_cast<GLuint>(it.second->handle);
+ auto texture_gui = reinterpret_cast<const TextureGUI*>(resource);
+ auto gl_texture = static_cast<GLuint>(texture_gui->handle);
- glDeleteTextures(1, &gl_texture);
- }
+ glDeleteTextures(1, &gl_texture);
- resource_map.clear();
+ delete texture_gui;
}
-template<>
-void resource::soft_cleanup<TextureGUI>(void)
+void TextureGUI::register_resource(void)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [TextureGUI] {}", iter->first);
-
- auto gl_texture = static_cast<GLuint>(iter->second->handle);
-
- glDeleteTextures(1, &gl_texture);
-
- iter = resource_map.erase(iter);
-
- continue;
- }
-
- iter = std::next(iter);
- }
+ resource::register_loader<TextureGUI>(&texture_gui_load_func, &texture_gui_free_func);
}
diff --git a/game/client/resource/texture_gui.hh b/game/client/resource/texture_gui.hh
index 855596e..988c642 100644
--- a/game/client/resource/texture_gui.hh
+++ b/game/client/resource/texture_gui.hh
@@ -9,7 +9,9 @@ constexpr static unsigned int TEXTURE_GUI_LOAD_LINEAR_MIN = 0x0008;
constexpr static unsigned int TEXTURE_GUI_LOAD_VFLIP = 0x0010;
constexpr static unsigned int TEXTURE_GUI_LOAD_GRAYSCALE = 0x0020;
-struct TextureGUI {
+struct TextureGUI final {
+ static void register_resource(void);
+
ImTextureID handle;
glm::ivec2 size;
};
diff --git a/game/server/main.cc b/game/server/main.cc
index 872307b..e25a457 100644
--- a/game/server/main.cc
+++ b/game/server/main.cc
@@ -46,6 +46,9 @@ int main(int argc, char** argv)
std::signal(SIGINT, &on_termination_signal);
std::signal(SIGTERM, &on_termination_signal);
+ BinFile::register_resource();
+ Image::register_resource();
+
server_game::init();
threading::init();
@@ -80,16 +83,14 @@ int main(int argc, char** argv)
std::this_thread::sleep_for(std::chrono::microseconds(globals::tickrate_dt));
- resource::soft_cleanup<BinFile>();
- resource::soft_cleanup<Image>();
+ resource::soft_cleanup();
threading::update();
}
server_game::shutdown();
- resource::hard_cleanup<BinFile>();
- resource::hard_cleanup<Image>();
+ resource::hard_cleanup();
threading::shutdown();