plugin reload
This commit is contained in:
parent
a98188c021
commit
e987f5bc70
11 changed files with 273 additions and 6 deletions
|
@ -17,6 +17,8 @@ local build_gui = _ACTION == "vs2017"
|
|||
local build_steam = false
|
||||
local build_game = false
|
||||
local working_dir = nil
|
||||
local debug_args = nil
|
||||
local release_args = nil
|
||||
local plugins = {}
|
||||
local embed_resources = false
|
||||
build_studio_callbacks = {}
|
||||
|
@ -73,6 +75,16 @@ newoption {
|
|||
description = "Working directory."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "debug-args",
|
||||
description = "Arguments passed to Studio in debug mode."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "release-args",
|
||||
description = "Arguments passed to Studio in release mode."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "no-studio",
|
||||
description = "Do not build Studio."
|
||||
|
@ -99,6 +111,14 @@ if _OPTIONS["working-dir"] then
|
|||
working_dir = _OPTIONS["working-dir"]
|
||||
end
|
||||
|
||||
if _OPTIONS["debug-args"] then
|
||||
debug_args = _OPTIONS["debug-args"]
|
||||
end
|
||||
|
||||
if _OPTIONS["release-args"] then
|
||||
release_args = _OPTIONS["release-args"]
|
||||
end
|
||||
|
||||
if _OPTIONS["no-physics"] then
|
||||
build_physics = false
|
||||
end
|
||||
|
@ -862,6 +882,17 @@ if build_studio then
|
|||
project "studio"
|
||||
kind "WindowedApp"
|
||||
|
||||
if debug_args then
|
||||
configuration { "Debug" }
|
||||
debugargs { debug_args }
|
||||
configuration {}
|
||||
end
|
||||
if release_args then
|
||||
configuration { "RelWithDebInfo" }
|
||||
debugargs { release_args }
|
||||
configuration {}
|
||||
end
|
||||
|
||||
if build_game then
|
||||
debugdir ("../../" .. build_game)
|
||||
elseif working_dir then
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "asset_browser.h"
|
||||
#include "audio/audio_scene.h"
|
||||
#include "audio/clip_manager.h"
|
||||
#include "editor/file_system_watcher.h"
|
||||
#include "editor/gizmo.h"
|
||||
#include "editor/prefab_system.h"
|
||||
#include "editor/render_interface.h"
|
||||
|
@ -230,6 +231,8 @@ public:
|
|||
|
||||
~StudioAppImpl()
|
||||
{
|
||||
if(m_watched_plugin.watcher) FileSystemWatcher::destroy(m_watched_plugin.watcher);
|
||||
|
||||
saveSettings();
|
||||
unloadIcons();
|
||||
|
||||
|
@ -580,6 +583,8 @@ public:
|
|||
void update()
|
||||
{
|
||||
PROFILE_FUNCTION();
|
||||
if (m_watched_plugin.reload_request) tryReloadPlugin();
|
||||
|
||||
guiBeginFrame();
|
||||
|
||||
float time_delta = m_editor->getEngine().getLastTimeDelta();
|
||||
|
@ -929,6 +934,13 @@ public:
|
|||
}
|
||||
|
||||
|
||||
void removeAction(Action* action) override
|
||||
{
|
||||
m_actions.eraseItem(action);
|
||||
m_window_actions.eraseItem(action);
|
||||
}
|
||||
|
||||
|
||||
void addWindowAction(Action* action) override
|
||||
{
|
||||
addAction(action);
|
||||
|
@ -1525,6 +1537,42 @@ public:
|
|||
}
|
||||
|
||||
|
||||
static bool copyPlugin(const char* src, int iteration, char(&out)[MAX_PATH_LENGTH])
|
||||
{
|
||||
char tmp_path[MAX_PATH_LENGTH];
|
||||
getExecutablePath(tmp_path, lengthOf(tmp_path));
|
||||
StaticString<MAX_PATH_LENGTH> copy_path;
|
||||
PathUtils::getDir(copy_path.data, lengthOf(copy_path.data), tmp_path);
|
||||
copy_path << "plugins/" << iteration;
|
||||
PlatformInterface::makePath(copy_path);
|
||||
PathUtils::getBasename(tmp_path, lengthOf(tmp_path), src);
|
||||
copy_path << "/" << tmp_path << "." << getPluginExtension();
|
||||
#ifdef _WIN32
|
||||
StaticString<MAX_PATH_LENGTH> src_pdb(src);
|
||||
StaticString<MAX_PATH_LENGTH> dest_pdb(copy_path);
|
||||
if (PathUtils::replaceExtension(dest_pdb.data, "pdb")
|
||||
&& PathUtils::replaceExtension(src_pdb.data, "pda"))
|
||||
{
|
||||
PlatformInterface::deleteFile(dest_pdb);
|
||||
if (!copyFile(src_pdb, dest_pdb))
|
||||
{
|
||||
copyString(out, src);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PlatformInterface::deleteFile(copy_path);
|
||||
if (!copyFile(src, copy_path))
|
||||
{
|
||||
copyString(out, src);
|
||||
return false;
|
||||
}
|
||||
copyString(out, copy_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void loadUserPlugins()
|
||||
{
|
||||
char cmd_line[2048];
|
||||
|
@ -1537,16 +1585,127 @@ public:
|
|||
if (!parser.currentEquals("-plugin")) continue;
|
||||
if (!parser.next()) break;
|
||||
|
||||
char tmp[MAX_PATH_LENGTH];
|
||||
parser.getCurrent(tmp, lengthOf(tmp));
|
||||
bool loaded = plugin_manager.load(tmp) != nullptr;
|
||||
if (!loaded)
|
||||
char src[MAX_PATH_LENGTH];
|
||||
parser.getCurrent(src, lengthOf(src));
|
||||
|
||||
bool is_full_path = findSubstring(src, ".") != nullptr;
|
||||
Lumix::IPlugin* loaded_plugin;
|
||||
if (is_full_path)
|
||||
{
|
||||
g_log_error.log("Editor") << "Could not load plugin " << tmp << " requested by command line";
|
||||
char copy_path[MAX_PATH_LENGTH];
|
||||
copyPlugin(src, 0, copy_path);
|
||||
loaded_plugin = plugin_manager.load(copy_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
loaded_plugin = plugin_manager.load(src);
|
||||
}
|
||||
|
||||
if (!loaded_plugin)
|
||||
{
|
||||
g_log_error.log("Editor") << "Could not load plugin " << src << " requested by command line";
|
||||
}
|
||||
else if (is_full_path && !m_watched_plugin.watcher)
|
||||
{
|
||||
char dir[MAX_PATH_LENGTH];
|
||||
char basename[MAX_PATH_LENGTH];
|
||||
PathUtils::getBasename(basename, lengthOf(basename), src);
|
||||
m_watched_plugin.basename = basename;
|
||||
PathUtils::getDir(dir, lengthOf(dir), src);
|
||||
m_watched_plugin.watcher = FileSystemWatcher::create(dir, m_allocator);
|
||||
m_watched_plugin.watcher->getCallback().bind<StudioAppImpl, &StudioAppImpl::onPluginChanged>(this);
|
||||
m_watched_plugin.dir = dir;
|
||||
m_watched_plugin.plugin = loaded_plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char* getPluginExtension()
|
||||
{
|
||||
const char* ext =
|
||||
#ifdef _WIN32
|
||||
"dll";
|
||||
#elif defined __linux__
|
||||
"so";
|
||||
#else
|
||||
#error Unknown platform
|
||||
#endif
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
void onPluginChanged(const char* path)
|
||||
{
|
||||
const char* ext = getPluginExtension();
|
||||
if (!PathUtils::hasExtension(path, ext)
|
||||
#ifdef _WIN32
|
||||
&& !PathUtils::hasExtension(path, "pda")
|
||||
#endif
|
||||
) return;
|
||||
|
||||
char basename[MAX_PATH_LENGTH];
|
||||
PathUtils::getBasename(basename, lengthOf(basename), path);
|
||||
if (!equalIStrings(basename, m_watched_plugin.basename)) return;
|
||||
|
||||
m_watched_plugin.reload_request = true;
|
||||
}
|
||||
|
||||
|
||||
void tryReloadPlugin()
|
||||
{
|
||||
m_watched_plugin.reload_request = false;
|
||||
|
||||
StaticString<MAX_PATH_LENGTH> src(m_watched_plugin.dir, m_watched_plugin.basename, ".", getPluginExtension());
|
||||
char copy_path[MAX_PATH_LENGTH];
|
||||
++m_watched_plugin.iteration;
|
||||
|
||||
if (!copyPlugin(src, m_watched_plugin.iteration, copy_path)) return;
|
||||
|
||||
g_log_info.log("Editor") << "Trying to reload plugin " << m_watched_plugin.basename;
|
||||
|
||||
OutputBlob blob(m_allocator);
|
||||
blob.reserve(16 * 1024);
|
||||
PluginManager& plugin_manager = m_editor->getEngine().getPluginManager();
|
||||
void* lib = plugin_manager.getLibrary(m_watched_plugin.plugin);
|
||||
|
||||
for (Universe* universe : m_editor->getEngine().getUniverses())
|
||||
{
|
||||
for (IScene* scene : universe->getScenes())
|
||||
{
|
||||
if (&scene->getPlugin() != m_watched_plugin.plugin) continue;
|
||||
if (m_editor->isGameMode()) scene->stopGame();
|
||||
scene->serialize(blob);
|
||||
universe->removeScene(scene);
|
||||
scene->getPlugin().destroyScene(scene);
|
||||
}
|
||||
}
|
||||
plugin_manager.unload(m_watched_plugin.plugin);
|
||||
|
||||
// TODO try to delete the old version
|
||||
|
||||
m_watched_plugin.plugin = plugin_manager.load(copy_path);
|
||||
if (!m_watched_plugin.plugin)
|
||||
{
|
||||
g_log_error.log("Editor") << "Failed to load plugin " << copy_path << ". Reload failed.";
|
||||
return;
|
||||
}
|
||||
|
||||
InputBlob input_blob(blob);
|
||||
for (Universe* universe : m_editor->getEngine().getUniverses())
|
||||
{
|
||||
m_watched_plugin.plugin->createScenes(*universe);
|
||||
for (IScene* scene : universe->getScenes())
|
||||
{
|
||||
if (&scene->getPlugin() != m_watched_plugin.plugin) continue;
|
||||
scene->deserialize(input_blob);
|
||||
if (m_editor->isGameMode()) scene->startGame();
|
||||
}
|
||||
}
|
||||
g_log_info.log("Editor") << "Finished reloading plugin.";
|
||||
}
|
||||
|
||||
|
||||
bool shouldSleepWhenInactive()
|
||||
{
|
||||
char cmd_line[2048];
|
||||
|
@ -2602,6 +2761,16 @@ public:
|
|||
bool m_is_save_as_dialog_open;
|
||||
ImFont* m_font;
|
||||
ImFont* m_bold_font;
|
||||
|
||||
struct WatchedPlugin
|
||||
{
|
||||
FileSystemWatcher* watcher = nullptr;
|
||||
StaticString<MAX_PATH_LENGTH> dir;
|
||||
StaticString<MAX_PATH_LENGTH> basename;
|
||||
Lumix::IPlugin* plugin = nullptr;
|
||||
int iteration = 0;
|
||||
bool reload_request = false;
|
||||
} m_watched_plugin;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ struct ImFont;
|
|||
extern "C" void setStudioApp_##plugin_name(StudioApp& app)
|
||||
#else
|
||||
#define LUMIX_STUDIO_ENTRY(plugin_name) \
|
||||
extern "C" LUMIX_LIBRARY_EXPORT void setStudioApp(StudioApp& app)
|
||||
extern "C" LUMIX_LIBRARY_EXPORT void setStudioApp(StudioApp& app)
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -108,6 +108,7 @@ public:
|
|||
virtual const Array<Action*>& getActions() = 0;
|
||||
virtual Array<Action*>& getToolbarActions() = 0;
|
||||
virtual void addAction(Action* action) = 0;
|
||||
virtual void removeAction(Action* action) = 0;
|
||||
virtual void addWindowAction(Action* action) = 0;
|
||||
virtual Action* getAction(const char* name) = 0;
|
||||
virtual SDL_Window* getWindow() = 0;
|
||||
|
|
|
@ -461,6 +461,7 @@ public:
|
|||
, m_next_frame(false)
|
||||
, m_lifo_allocator(m_allocator, 10 * 1024 * 1024)
|
||||
, m_working_dir(working_dir)
|
||||
, m_universes(m_allocator)
|
||||
{
|
||||
g_log_info.log("Core") << "Creating engine...";
|
||||
Profiler::setThreadName("Main");
|
||||
|
@ -1306,9 +1307,16 @@ public:
|
|||
const char* getWorkingDirectory() const override { return m_working_dir; }
|
||||
|
||||
|
||||
const Array<Universe*>& getUniverses() const override
|
||||
{
|
||||
return m_universes;
|
||||
}
|
||||
|
||||
|
||||
Universe& createUniverse(bool set_lua_globals) override
|
||||
{
|
||||
Universe* universe = LUMIX_NEW(m_allocator, Universe)(m_allocator);
|
||||
m_universes.push(universe);
|
||||
const Array<IPlugin*>& plugins = m_plugin_manager->getPlugins();
|
||||
for (auto* plugin : plugins)
|
||||
{
|
||||
|
@ -1337,6 +1345,7 @@ public:
|
|||
|
||||
void destroyUniverse(Universe& universe) override
|
||||
{
|
||||
m_universes.eraseItem(&universe);
|
||||
auto& scenes = universe.getScenes();
|
||||
for (int i = scenes.size() - 1; i >= 0; --i)
|
||||
{
|
||||
|
@ -1695,6 +1704,7 @@ private:
|
|||
PathManager m_path_manager;
|
||||
lua_State* m_state;
|
||||
HashMap<int, Resource*> m_lua_resources;
|
||||
Array<Universe*> m_universes;
|
||||
int m_last_lua_resource_idx;
|
||||
StaticString<MAX_PATH_LENGTH> m_working_dir;
|
||||
};
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
virtual const char* getWorkingDirectory() const = 0;
|
||||
virtual Universe& createUniverse(bool set_lua_globals) = 0;
|
||||
virtual void destroyUniverse(Universe& context) = 0;
|
||||
virtual const Array<Universe*>& getUniverses() const = 0;
|
||||
virtual void setPlatformData(const PlatformData& data) = 0;
|
||||
virtual const PlatformData& getPlatformData() = 0;
|
||||
|
||||
|
|
|
@ -112,6 +112,31 @@ void getExtension(char* extension, int max_length, const char* src)
|
|||
}
|
||||
|
||||
|
||||
bool replaceExtension(char* path, const char* ext)
|
||||
{
|
||||
char* end = path + stringLength(path);
|
||||
while (end > path && *end != '.')
|
||||
{
|
||||
--end;
|
||||
}
|
||||
if (*end != '.') return false;
|
||||
|
||||
++end;
|
||||
const char* src = ext;
|
||||
while (*src != '\0' && *end != '\0')
|
||||
{
|
||||
*end = *src;
|
||||
++end;
|
||||
++src;
|
||||
}
|
||||
bool copied_whole_ext = *src == '\0';
|
||||
if (!copied_whole_ext) return false;
|
||||
|
||||
*end = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool hasExtension(const char* filename, const char* ext)
|
||||
{
|
||||
char tmp[20];
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace PathUtils
|
|||
LUMIX_ENGINE_API void getFilename(char* filename, int max_length, const char* src);
|
||||
LUMIX_ENGINE_API void getExtension(char* extension, int max_length, const char* src);
|
||||
LUMIX_ENGINE_API bool hasExtension(const char* filename, const char* ext);
|
||||
LUMIX_ENGINE_API bool replaceExtension(char* path, const char* ext);
|
||||
LUMIX_ENGINE_API bool isAbsolute(const char* path);
|
||||
|
||||
|
||||
|
|
|
@ -72,6 +72,15 @@ class PluginManagerImpl LUMIX_FINAL : public PluginManager
|
|||
}
|
||||
|
||||
|
||||
void* getLibrary(IPlugin* plugin) const
|
||||
{
|
||||
int idx = m_plugins.indexOf(plugin);
|
||||
if (idx < 0) return nullptr;
|
||||
|
||||
return m_libraries[idx];
|
||||
}
|
||||
|
||||
|
||||
const Array<void*>& getLibraries() const override
|
||||
{
|
||||
return m_libraries;
|
||||
|
@ -103,6 +112,17 @@ class PluginManagerImpl LUMIX_FINAL : public PluginManager
|
|||
}
|
||||
|
||||
|
||||
void unload(IPlugin* plugin) override
|
||||
{
|
||||
int idx = m_plugins.indexOf(plugin);
|
||||
ASSERT(idx >= 0);
|
||||
LUMIX_DELETE(m_engine.getAllocator(), m_plugins[idx]);
|
||||
unloadLibrary(m_libraries[idx]);
|
||||
m_libraries.erase(idx);
|
||||
m_plugins.erase(idx);
|
||||
}
|
||||
|
||||
|
||||
IPlugin* load(const char* path) override
|
||||
{
|
||||
char path_with_ext[MAX_PATH_LENGTH];
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace Lumix
|
|||
static PluginManager* create(Engine& engine);
|
||||
static void destroy(PluginManager* manager);
|
||||
|
||||
virtual void unload(IPlugin* plugin) = 0;
|
||||
virtual IPlugin* load(const char* path) = 0;
|
||||
virtual void addPlugin(IPlugin* plugin) = 0;
|
||||
virtual void update(float dt, bool paused) = 0;
|
||||
|
@ -33,6 +34,7 @@ namespace Lumix
|
|||
virtual IPlugin* getPlugin(const char* name) = 0;
|
||||
virtual const Array<IPlugin*>& getPlugins() const = 0;
|
||||
virtual const Array<void*>& getLibraries() const = 0;
|
||||
virtual void* getLibrary(IPlugin* plugin) const = 0;
|
||||
virtual DelegateList<void(void*)>& libraryLoaded() = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -68,6 +68,12 @@ void Universe::addScene(IScene* scene)
|
|||
}
|
||||
|
||||
|
||||
void Universe::removeScene(IScene* scene)
|
||||
{
|
||||
m_scenes.eraseItemFast(scene);
|
||||
}
|
||||
|
||||
|
||||
const Vec3& Universe::getPosition(Entity entity) const
|
||||
{
|
||||
return m_entities[entity.index].position;
|
||||
|
|
|
@ -130,6 +130,7 @@ public:
|
|||
IScene* getScene(u32 hash) const;
|
||||
Array<IScene*>& getScenes();
|
||||
void addScene(IScene* scene);
|
||||
void removeScene(IScene* scene);
|
||||
|
||||
private:
|
||||
void transformEntity(Entity entity, bool update_local);
|
||||
|
|
Loading…
Reference in a new issue