inital commit
This commit is contained in:
commit
2a9b6ac9f9
18
CMakeLists.txt
Normal file
18
CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
|
|
||||||
|
project(solanaceae)
|
||||||
|
|
||||||
|
add_library(solanaceae_plugin
|
||||||
|
./solanaceae/plugin/solana_plugin_v1.h
|
||||||
|
./solanaceae/plugin/plugin.hpp
|
||||||
|
./solanaceae/plugin/plugin.cpp
|
||||||
|
./solanaceae/plugin/plugin_manager.hpp
|
||||||
|
./solanaceae/plugin/plugin_manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_plugin PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_plugin PUBLIC cxx_std_17)
|
||||||
|
#target_link_libraries(solanaceae_plugin PUBLIC
|
||||||
|
#solanaceae_core
|
||||||
|
#)
|
||||||
|
|
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
The Code is under the following License, if not stated otherwise:
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Erik Scholz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
124
solanaceae/plugin/plugin.cpp
Normal file
124
solanaceae/plugin/plugin.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "./plugin.hpp"
|
||||||
|
|
||||||
|
// https://youtu.be/RsHGUL5E1_s
|
||||||
|
|
||||||
|
#define SOLANA_PLUGIN_HOST
|
||||||
|
#include "./solana_plugin_v1.h"
|
||||||
|
|
||||||
|
#if (defined(_WIN32) || defined(_WIN64))
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#define WINDOWS
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define APPLE
|
||||||
|
#else
|
||||||
|
#define LINUX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
void* Plugin::loadSymbol(const char* s_name) {
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
return GetProcAddress(_dl, s_name);
|
||||||
|
#else
|
||||||
|
return dlsym(_dl, s_name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads lib and gets name (and version)
|
||||||
|
Plugin::Plugin(const char* path) {
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
_dl = (void*)LoadLibraryA(path);
|
||||||
|
#else
|
||||||
|
_dl = (void*)dlopen(path, RTLD_NOW /*| RTLD_LOCAL*/);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!_dl) {
|
||||||
|
std::cerr << "PLG opening '" << path << "' failed\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fn_name = loadSymbol("solana_plugin_get_name");
|
||||||
|
_fn_version = loadSymbol("solana_plugin_get_version");
|
||||||
|
_fn_start = loadSymbol("solana_plugin_start");
|
||||||
|
_fn_stop = loadSymbol("solana_plugin_stop");
|
||||||
|
_fn_tick = loadSymbol("solana_plugin_tick");
|
||||||
|
|
||||||
|
if (!_fn_name || !_fn_version || !_fn_start || !_fn_stop || !_fn_tick) {
|
||||||
|
std::cerr << "PLG '" << path << "' misses functions\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = reinterpret_cast<decltype(&solana_plugin_get_name)>(_fn_name)();
|
||||||
|
|
||||||
|
if (name.empty()) {
|
||||||
|
std::cerr << "PLG '" << path << "' misses name\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
version = reinterpret_cast<decltype(&solana_plugin_get_version)>(_fn_version)();
|
||||||
|
|
||||||
|
if (version != SOLANA_PLUGIN_VERSION) {
|
||||||
|
std::cerr << "PLG '" << path << "' version mismatch IS:" << version << " SHOULD:" << SOLANA_PLUGIN_VERSION << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_plugin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::Plugin(Plugin&& other) {
|
||||||
|
valid_plugin = other.valid_plugin;
|
||||||
|
name = other.name;
|
||||||
|
|
||||||
|
_dl = other._dl;
|
||||||
|
other._dl = nullptr;
|
||||||
|
|
||||||
|
_fn_name = other._fn_name;
|
||||||
|
other._fn_name = nullptr;
|
||||||
|
|
||||||
|
_fn_start = other._fn_start;
|
||||||
|
other._fn_start = nullptr;
|
||||||
|
|
||||||
|
_fn_stop = other._fn_stop;
|
||||||
|
other._fn_stop = nullptr;
|
||||||
|
|
||||||
|
_fn_tick = other._fn_tick;
|
||||||
|
other._fn_tick = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unloads the plugin
|
||||||
|
Plugin::~Plugin(void) {
|
||||||
|
if (_dl != nullptr) {
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
FreeLibrary(_dl);
|
||||||
|
#else
|
||||||
|
dlclose(_dl);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runs the start function
|
||||||
|
uint32_t Plugin::start(SolanaAPI* solana_api) const {
|
||||||
|
assert(valid_plugin);
|
||||||
|
return reinterpret_cast<decltype(&solana_plugin_start)>(_fn_start)(solana_api);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop function
|
||||||
|
void Plugin::stop(void) const {
|
||||||
|
assert(valid_plugin);
|
||||||
|
reinterpret_cast<decltype(&solana_plugin_stop)>(_fn_stop)();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tick function
|
||||||
|
void Plugin::tick(float delta) const {
|
||||||
|
assert(valid_plugin);
|
||||||
|
reinterpret_cast<decltype(&solana_plugin_tick)>(_fn_tick)(delta);
|
||||||
|
}
|
||||||
|
|
47
solanaceae/plugin/plugin.hpp
Normal file
47
solanaceae/plugin/plugin.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
struct SolanaAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Plugin {
|
||||||
|
bool valid_plugin = false;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
// void* is platform independent enough, maybe use uint64_t
|
||||||
|
void* _dl = nullptr;
|
||||||
|
void* _fn_name = nullptr; // TODO: make variable instead of function?
|
||||||
|
void* _fn_version = nullptr; // TODO: make variable instead of function?
|
||||||
|
void* _fn_start = nullptr;
|
||||||
|
void* _fn_stop = nullptr;
|
||||||
|
void* _fn_tick = nullptr;
|
||||||
|
|
||||||
|
void* loadSymbol(const char* name);
|
||||||
|
|
||||||
|
// loads lib and gets name (and version)
|
||||||
|
Plugin(const char* path);
|
||||||
|
Plugin(Plugin&& other);
|
||||||
|
Plugin(const Plugin& other) = delete;
|
||||||
|
|
||||||
|
// unloads the plugin
|
||||||
|
~Plugin(void);
|
||||||
|
|
||||||
|
|
||||||
|
// runs the start function
|
||||||
|
uint32_t start(SolanaAPI* solana_api) const;
|
||||||
|
|
||||||
|
// stop function
|
||||||
|
void stop(void) const;
|
||||||
|
|
||||||
|
// tick function
|
||||||
|
void tick(float delta) const;
|
||||||
|
|
||||||
|
operator bool(void) const {
|
||||||
|
return valid_plugin;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
53
solanaceae/plugin/plugin_manager.cpp
Normal file
53
solanaceae/plugin/plugin_manager.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "./plugin_manager.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// def
|
||||||
|
std::map<std::string, void*> g_instance_map {};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void* g_resolveInstance__internal(const char* id) {
|
||||||
|
if (auto it = g_instance_map.find(id); it != g_instance_map.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void g_provideInstance__internal(const char* id, const char* plugin_name, void* instance) {
|
||||||
|
g_instance_map[id] = instance;
|
||||||
|
std::cout << "PLM '" << plugin_name << "' provided '" << id << "'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // extern C
|
||||||
|
|
||||||
|
PluginManager::~PluginManager(void) {
|
||||||
|
// destruct in reverse!
|
||||||
|
for (auto it = _plugins.rbegin(); it != _plugins.rend(); it++) {
|
||||||
|
it->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginManager::add(const std::string& plug_path) {
|
||||||
|
Plugin p{plug_path.c_str()};
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.start(&_sapi) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_plugins.emplace_back(std::move(p));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::tick(float delta) {
|
||||||
|
for (const auto& p : _plugins) {
|
||||||
|
p.tick(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
44
solanaceae/plugin/plugin_manager.hpp
Normal file
44
solanaceae/plugin/plugin_manager.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define SOLANA_PLUGIN_HOST
|
||||||
|
#include <solanaceae/plugin/solana_plugin_v1.h>
|
||||||
|
|
||||||
|
#include <solanaceae/plugin/plugin.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// TODO: save plug name to instance
|
||||||
|
extern std::map<std::string, void*> g_instance_map;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void* g_resolveInstance__internal(const char* id);
|
||||||
|
|
||||||
|
void g_provideInstance__internal(const char* id, const char* plugin_name, void* instance);
|
||||||
|
|
||||||
|
} // extern C
|
||||||
|
|
||||||
|
// templated helper, use or make sure vtable is right
|
||||||
|
template<typename T>
|
||||||
|
void g_provideInstance(const char* id, const char* plugin_name, T* instance) {
|
||||||
|
g_provideInstance__internal(id, plugin_name, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only on host!
|
||||||
|
struct PluginManager {
|
||||||
|
SolanaAPI _sapi {
|
||||||
|
&g_resolveInstance__internal,
|
||||||
|
&g_provideInstance__internal,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Plugin> _plugins;
|
||||||
|
|
||||||
|
~PluginManager(void);
|
||||||
|
|
||||||
|
bool add(const std::string& plug_path);
|
||||||
|
|
||||||
|
void tick(float delta);
|
||||||
|
};
|
||||||
|
|
58
solanaceae/plugin/solana_plugin_v1.h
Normal file
58
solanaceae/plugin/solana_plugin_v1.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#ifndef SOLANA_PLUGIN__H
|
||||||
|
#define SOLANA_PLUGIN__H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SOLANA_PLUGIN_VERSION 1
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
#define SOLANA_PLUGIN_EXPORT __declspec(dllexport)
|
||||||
|
#elif defined(__GNUC__) // also clang
|
||||||
|
#define SOLANA_PLUGIN_EXPORT __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#error unsupported platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SOLANA_PLUGIN_HOST)
|
||||||
|
#define SOLANA_PLUGIN_DECL
|
||||||
|
#else
|
||||||
|
#define SOLANA_PLUGIN_DECL SOLANA_PLUGIN_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// public plugin stuff here
|
||||||
|
|
||||||
|
struct SolanaAPI {
|
||||||
|
void* (*resolveInstance)(const char* id);
|
||||||
|
|
||||||
|
// resolve_all_instances(const char* id)
|
||||||
|
void (*provideInstance)(const char* id, const char* plugin_name, void* instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------- info ----------
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT const char* solana_plugin_get_name(void);
|
||||||
|
|
||||||
|
// get the SOLANA_PLUGIN_VERSION the plugin was compiled with
|
||||||
|
SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_get_version(void);
|
||||||
|
|
||||||
|
// ---------- plugin control ----------
|
||||||
|
|
||||||
|
// return 0 on success
|
||||||
|
SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api);
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT void solana_plugin_stop(void);
|
||||||
|
|
||||||
|
// called periodically
|
||||||
|
SOLANA_PLUGIN_EXPORT void solana_plugin_tick(float delta);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SOLANA_PLUGIN__H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user