diff --git a/data/images/background.png b/data/images/background.png index dbb4feca..0daa6550 100644 Binary files a/data/images/background.png and b/data/images/background.png differ diff --git a/modules/client/client.lua b/modules/client/client.lua index 209b7b7d..436a6f5a 100644 --- a/modules/client/client.lua +++ b/modules/client/client.lua @@ -11,7 +11,7 @@ function setMusic(filename) end function reloadScripts() - g_textures.clearTexturesCache() + g_textures.clearCache() g_modules.reloadModules() local script = '/' .. g_app.getCompactName() .. 'rc' diff --git a/modules/client_terminal/commands.lua b/modules/client_terminal/commands.lua index 1f2506a9..f85617fb 100644 --- a/modules/client_terminal/commands.lua +++ b/modules/client_terminal/commands.lua @@ -15,6 +15,10 @@ function show_map() modules.game_interface.getMapPanel():show() end +function live_textures_reload() + g_textures.liveReload() +end + function auto_reload_module(name) local function reloadEvent() reloadModule(name) diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index 89a0cb5f..60a2d300 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -297,6 +297,11 @@ std::string ResourceManager::getRealDir(const std::string& path) return dir; } +std::string ResourceManager::getRealPath(const std::string& path) +{ + return getRealDir(path) + "/" + path; +} + std::string ResourceManager::getBaseDir() { return PHYSFS_getBaseDir(); @@ -320,3 +325,8 @@ bool ResourceManager::isFileType(const std::string& filename, const std::string& return true; return false; } + +ticks_t ResourceManager::getFileTime(const std::string& filename) +{ + return g_platform.getFileModificationTime(getRealPath(filename)); +} diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index e698767e..c280c3c5 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -64,6 +64,7 @@ public: std::string resolvePath(const std::string& path); std::string getRealDir(const std::string& path); + std::string getRealPath(const std::string& path); std::string getBaseDir(); std::string getUserDir(); std::string getWriteDir() { return m_writeDir; } @@ -72,6 +73,7 @@ public: std::string guessFilePath(const std::string& filename, const std::string& type); bool isFileType(const std::string& filename, const std::string& type); + ticks_t getFileTime(const std::string& filename); private: std::string m_workDir; diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index f6aefb18..d4721a89 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -30,11 +30,13 @@ Texture::Texture() { m_id = 0; + m_time = 0; } Texture::Texture(const Size& size) { m_id = 0; + m_time = 0; if(!setupSize(size)) return; @@ -49,9 +51,7 @@ Texture::Texture(const Size& size) Texture::Texture(const ImagePtr& image, bool buildMipmaps, bool compress) { m_id = 0; - - if(!setupSize(image->getSize(), buildMipmaps)) - return; + m_time = 0; createTexture(); @@ -70,6 +70,9 @@ Texture::~Texture() void Texture::uploadPixels(const ImagePtr& image, bool buildMipmaps, bool compress) { + if(!setupSize(image->getSize(), buildMipmaps)) + return; + ImagePtr glImage = image; if(m_size != m_glSize) { glImage = ImagePtr(new Image(m_glSize, image->getBpp())); diff --git a/src/framework/graphics/texture.h b/src/framework/graphics/texture.h index 491092cf..2ef0bfb0 100644 --- a/src/framework/graphics/texture.h +++ b/src/framework/graphics/texture.h @@ -41,8 +41,10 @@ public: virtual void setSmooth(bool smooth); virtual void setRepeat(bool repeat); void setUpsideDown(bool upsideDown); + void setTime(ticks_t time) { m_time = time; } - uint getId() { return m_id; } + uint getId() { return m_id; } + ticks_t getTime() { return m_time; } int getWidth() { return m_size.width(); } int getHeight() { return m_size.height(); } const Size& getSize() { return m_size; } @@ -62,6 +64,7 @@ protected: void setupPixels(int level, const Size& size, uchar *pixels, int channels = 4, bool compress = false); uint m_id; + ticks_t m_time; Size m_size; Size m_glSize; Matrix3 m_transformMatrix; diff --git a/src/framework/graphics/texturemanager.cpp b/src/framework/graphics/texturemanager.cpp index cb4dd6e7..389c3c36 100644 --- a/src/framework/graphics/texturemanager.cpp +++ b/src/framework/graphics/texturemanager.cpp @@ -27,6 +27,7 @@ #include #include +#include #include TextureManager g_textures; @@ -38,6 +39,10 @@ void TextureManager::init() void TextureManager::terminate() { + if(m_liveReloadEvent) { + m_liveReloadEvent->cancel(); + m_liveReloadEvent = nullptr; + } m_textures.clear(); m_animatedTextures.clear(); m_emptyTexture = nullptr; @@ -56,12 +61,32 @@ void TextureManager::poll() animatedTexture->updateAnimation(); } -void TextureManager::clearTexturesCache() +void TextureManager::clearCache() { m_animatedTextures.clear(); m_textures.clear(); } +void TextureManager::liveReload() +{ + if(m_liveReloadEvent) + return; + m_liveReloadEvent = g_dispatcher.cycleEvent([this] { + for(auto& it : m_textures) { + const std::string& path = g_resources.guessFilePath(it.first, "png"); + const TexturePtr& tex = it.second; + if(tex->getTime() >= g_resources.getFileTime(path)) + continue; + + ImagePtr image = Image::load(path); + if(!image) + continue; + tex->uploadPixels(image, tex->hasMipmaps()); + tex->setTime(stdext::time()); + } + }, 1000); +} + TexturePtr TextureManager::getTexture(const std::string& fileName) { TexturePtr texture; @@ -83,13 +108,14 @@ TexturePtr TextureManager::getTexture(const std::string& fileName) // load texture file data std::stringstream fin; g_resources.readFileStream(filePathEx, fin); - texture = loadPNG(fin); + texture = loadTexture(fin); } catch(stdext::exception& e) { g_logger.error(stdext::format("Unable to load texture '%s': %s", fileName, e.what())); texture = g_textures.getEmptyTexture(); } if(texture) { + texture->setTime(stdext::time()); texture->setSmooth(true); m_textures[filePath] = texture; } @@ -98,7 +124,7 @@ TexturePtr TextureManager::getTexture(const std::string& fileName) return texture; } -TexturePtr TextureManager::loadPNG(std::stringstream& file) +TexturePtr TextureManager::loadTexture(std::stringstream& file) { TexturePtr texture; diff --git a/src/framework/graphics/texturemanager.h b/src/framework/graphics/texturemanager.h index 4997ea0a..3aa2bbba 100644 --- a/src/framework/graphics/texturemanager.h +++ b/src/framework/graphics/texturemanager.h @@ -24,6 +24,7 @@ #define TEXTUREMANAGER_H #include "texture.h" +#include class TextureManager { @@ -32,17 +33,20 @@ public: void terminate(); void poll(); - void clearTexturesCache(); + void clearCache(); + void liveReload(); + void preload(const std::string& fileName) { getTexture(fileName); } TexturePtr getTexture(const std::string& fileName); const TexturePtr& getEmptyTexture() { return m_emptyTexture; } private: - TexturePtr loadPNG(std::stringstream& file); + TexturePtr loadTexture(std::stringstream& file); std::unordered_map m_textures; std::vector m_animatedTextures; TexturePtr m_emptyTexture; + ScheduledEventPtr m_liveReloadEvent; }; extern TextureManager g_textures; diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index febb5c80..43f477a9 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -192,6 +192,7 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_resources", "readFileContents", &ResourceManager::readFileContents, &g_resources); g_lua.bindSingletonFunction("g_resources", "guessFilePath", &ResourceManager::guessFilePath, &g_resources); g_lua.bindSingletonFunction("g_resources", "isFileType", &ResourceManager::isFileType, &g_resources); + g_lua.bindSingletonFunction("g_resources", "getFileTime", &ResourceManager::getFileTime, &g_resources); // Module g_lua.registerClass(); @@ -304,7 +305,8 @@ void Application::registerLuaFunctions() // Textures g_lua.registerSingletonClass("g_textures"); g_lua.bindSingletonFunction("g_textures", "preload", &TextureManager::preload, &g_textures); - g_lua.bindSingletonFunction("g_textures", "clearTexturesCache", &TextureManager::clearTexturesCache, &g_textures); + g_lua.bindSingletonFunction("g_textures", "clearCache", &TextureManager::clearCache, &g_textures); + g_lua.bindSingletonFunction("g_textures", "liveReload", &TextureManager::liveReload, &g_textures); // UI g_lua.registerSingletonClass("g_ui"); diff --git a/src/framework/platform/platform.h b/src/framework/platform/platform.h index cdeee57a..a97a99aa 100644 --- a/src/framework/platform/platform.h +++ b/src/framework/platform/platform.h @@ -25,6 +25,7 @@ #include #include +#include class Platform { @@ -39,11 +40,11 @@ public: bool copyFile(std::string from, std::string to); bool fileExists(std::string file); bool removeFile(std::string file); + ticks_t getFileModificationTime(std::string file); void openUrl(std::string url); std::string getCPUName(); double getTotalSystemMemory(); std::string getOSName(); - time_t getFileModificationTime(const std::string& filename); }; extern Platform g_platform; diff --git a/src/framework/platform/unixplatform.cpp b/src/framework/platform/unixplatform.cpp index c85bb62c..29fc9565 100644 --- a/src/framework/platform/unixplatform.cpp +++ b/src/framework/platform/unixplatform.cpp @@ -108,6 +108,14 @@ bool Platform::removeFile(std::string file) return false; } +ticks_t Platform::getFileModificationTime(std::string file) +{ + struct stat attrib; + if(stat(file.c_str(), &attrib) == 0) + return attrib.st_mtime; + return 0; +} + void Platform::openUrl(std::string url) { if(url.find("http://") == std::string::npos) @@ -160,15 +168,5 @@ std::string Platform::getOSName() return std::string(); } -time_t Platform::getFileModificationTime(const std::string& filename) -{ - struct stat attrib; - if(stat(filename.c_str(), &attrib)) - perror(filename.c_str()); - else - return attrib.st_mtime; - return 0; -} - #endif diff --git a/src/framework/platform/win32platform.cpp b/src/framework/platform/win32platform.cpp index ea01dea0..724a2568 100644 --- a/src/framework/platform/win32platform.cpp +++ b/src/framework/platform/win32platform.cpp @@ -132,6 +132,19 @@ bool Platform::removeFile(std::string file) return true; } +ticks_t Platform::getFileModificationTime(std::string file) +{ + boost::replace_all(file, "/", "\\"); + std::wstring wfile = stdext::utf8_to_utf16(file); + WIN32_FILE_ATTRIBUTE_DATA fileAttrData; + memset(&fileAttrData, 0, sizeof(fileAttrData)); + GetFileAttributesExW(wfile.c_str(), GetFileExInfoStandard, &fileAttrData); + ULARGE_INTEGER uli; + uli.LowPart = fileAttrData.ftLastWriteTime.dwLowDateTime; + uli.HighPart = fileAttrData.ftLastWriteTime.dwHighDateTime; + return uli.QuadPart; +} + void Platform::openUrl(std::string url) { if(url.find("http://") == std::string::npos) @@ -401,13 +414,4 @@ std::string Platform::getOSName() return ret; } -time_t Platform::getFileModificationTime(const std::string& filename) -{ - //TODO - /*WIN32_FILE_ATTRIBUTE_DATA fileAttrData = {0}; - GetFileAttributesEx(filename.c_str(), GetFileExInfoStandard, &fileAttrData); - return fileAttrData.ftLastWriteTime;*/ - return 0; -} - #endif diff --git a/src/framework/stdext/time.cpp b/src/framework/stdext/time.cpp index 0a3a96ba..95b47b93 100644 --- a/src/framework/stdext/time.cpp +++ b/src/framework/stdext/time.cpp @@ -28,6 +28,10 @@ namespace stdext { const static auto startup_time = boost::chrono::high_resolution_clock::now(); +ticks_t time() { + return std::time(NULL); +} + ticks_t millis() { return boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - startup_time).count(); diff --git a/src/framework/stdext/time.h b/src/framework/stdext/time.h index fa148dca..e9565838 100644 --- a/src/framework/stdext/time.h +++ b/src/framework/stdext/time.h @@ -27,6 +27,7 @@ namespace stdext { +ticks_t time(); ticks_t millis(); ticks_t micros(); void millisleep(size_t ms);