plugin reload

This commit is contained in:
Mikulas Florek 2018-03-25 11:32:08 +02:00
parent a98188c021
commit e987f5bc70
11 changed files with 273 additions and 6 deletions

View file

@ -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

View file

@ -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;
};

View file

@ -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;

View file

@ -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;
};

View file

@ -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;

View file

@ -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];

View file

@ -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);

View file

@ -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];

View file

@ -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;
};

View file

@ -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;

View file

@ -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);