aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--include/plugins.h30
-rw-r--r--src/plugins.c91
3 files changed, 108 insertions, 17 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a3c50fc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# **P**rotocol **T**ool**b**ox for **M**ine**c**raft (PTXMC)
+PTXMC is a modular tool for dissecting and manipulating the Minecraft Java Edition multiplayer protocol.
+
+The tool is developed on and for Linux at this time (ports may come later).
diff --git a/include/plugins.h b/include/plugins.h
index cbd841d..6d4a87d 100644
--- a/include/plugins.h
+++ b/include/plugins.h
@@ -8,7 +8,7 @@ typedef struct ptx__plugin_manager_tag ptx_plugin_manager_t;
typedef struct ptx__plugin_handle_tag ptx_plugin_t;
/* returns:
- * - < 0 for plugin loading error (plugin will not be loaded)
+ * - < 0 for plugin loading error (plugin will be unloaded)
* - 0 for successful plugin startup
* - (don't return values greater than 0 at this time)
* usage:
@@ -22,30 +22,30 @@ typedef int (ptx_plugin_init_proc_t)(ptx_plugin_t * /*plugin*/, void * /*resume*
enum {
/* This plugin cannot be resumed at this time. The plugin manager will not unload it if it expects a "seamless" reload.
* The plugin is expected to act as if this function was not called. */
- PTX_PLUGIN_CANNOT_RESUME = -1,
+ DEINIT_ERR_RESUME_UNSUPPORTED = -1,
/* The plugin supports being resumed, but due to a transient error condition (malloc failure?) the plugin could not set itself up for
* resuming. If a plugin returns this value to the plugin manager, it is expected to act as if the function was not called. */
- PTX_PLUGIN_RESUME_ERR = -2,
+ DEINIT_ERR_RESUME_FAILURE = -2,
/* The plugin supports being resumed, but due to a transient error condition, the plugin could not set itself up for resuming.
* Additionally, as a part of preparing to be reloaded, the plugin performed operations it could not roll back. Likely, an error
* will be reported to the user, and the plugin will be unloaded. The plugin manager makes no assumptions about the behavior of the
* plugin at this time. */
- PTX_PLUGIN_RESUME_ERR_UNRECOVERABLE = -3,
+ DEINIT_ERR_RESUME_UNRECOVERABLE = -3,
- /* The plugin is ready to be resumed. */
- PTX_PLUGIN_RESUME_OK = 0
+ /* The plugin is ready to be resumed/unloaded. */
+ DEINIT_OK = 0
};
/* returns:
- * - one of the PTX_PLUGIN_* values in the above enum. if the return value is anything else, the plugin manager's behavior is not defined.
+ * - one of the values in the above enum. if the return value is anything else, the plugin manager's behavior is not defined.
*/
-typedef int (ptx_plugin_restart_proc_t)(ptx_plugin_t * /*plugin*/, void ** /*presume*/);
+typedef int (ptx_plugin_deinit_proc_t)(ptx_plugin_t * /*plugin*/, void ** /*presume*/);
-#define PTX_MOD_VERSION (1u)
+#define PTX_CUR_PLUGIN_VERSION (1u)
-/* the fields of this struct are public. ABI changes should be marked by incrementing PTX_MOD_VERSION */
+/* the fields of this struct are public. ABI changes should be marked by incrementing PTX_CUR_PLUGIN_VERSION */
struct ptx_plugin_desc
{
size_t desc_len;
@@ -53,7 +53,7 @@ struct ptx_plugin_desc
unsigned reserved;
ptx_plugin_init_proc_t *init_proc;
- ptx_plugin_restart_proc_t *restart_proc;
+ ptx_plugin_deinit_proc_t *deinit_proc;
const char *name;
const char *description;
@@ -72,7 +72,7 @@ struct ptx_plugin_desc
#define PTX_PLUGIN_DESC_START PTX_EXPORT \
const struct ptx_plugin_desc PTX__INTERNAL_PLUGIN_DESC_SYM = { \
.desc_len = sizeof(struct ptx_plugin_desc), \
- .desc_ver = PTX_MOD_VERSION,
+ .desc_ver = PTX_CUR_PLUGIN_VERSION,
#define PTX_PLUGIN_NAME(_x) .name = _x,
#define PTX_PLUGIN_DESCRIPTION(_x) .description = _x,
@@ -89,4 +89,10 @@ PTX_INTERNAL(void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr));
PTX_INTERNAL(int ptx_plugin_manager_load_dir(ptx_plugin_manager_t *mgr, const char *dir));
+/* initializes plugins which have not yet been initialized. returns the number of plugins which have been initialized and are still loaded
+ * (plugins can return a failure from their initialization function, at which point the plugin will be unloaded.) */
+PTX_INTERNAL(int ptx_plugin_manager_init_plugins(ptx_plugin_manager_t *mgr));
+
+PTX_API void ptx_plugin_set_mainloop_func(ptx_plugin_t *plugin, void *func);
+
#endif
diff --git a/src/plugins.c b/src/plugins.c
index b96aade..8ccde06 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -30,10 +30,22 @@ struct ptx__plugin_handle_tag
ptx_plugin_t *next;
int path_fd;
+ int pl_state; /* used to assert that the application is following its contract with plugins */
+
/* plugin filename */
char fname[1];
};
+enum {
+ PL_STATE_NEW, /* plugin struct just initialized */
+ PL_STATE_LOADED, /* plugin module loaded */
+ PL_STATE_INITIALIZING, /* plugin initializer call started */
+ PL_STATE_INITIALIZED, /* plugin initializer completed */
+ PL_STATE_DEINITIALIZED, /* plugin deinitializer called */
+ PL_STATE_ERROR, /* error in initializer, only valid operation is free */
+ PL_STATE_UNLOADED /* plugin unloaded */
+};
+
ptx_plugin_manager_t *ptx_plugin_manager_new(void)
{
ptx_plugin_manager_t *mgr = malloc(sizeof(ptx_plugin_manager_t));
@@ -46,7 +58,7 @@ ptx_plugin_manager_t *ptx_plugin_manager_new(void)
}
/* inform the plugin that it is about to be unloaded. It should clean up its resources. */
-static void deinit_plugin(ptx_plugin_t *plugin);
+static int deinit_plugin(ptx_plugin_t *plugin, void **presume);
/* unload/cleanup/free the plugin. */
static void free_plugin(ptx_plugin_t *plugin);
@@ -58,7 +70,7 @@ void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr)
/* do cleanup */
for (ptx_plugin_t *plugin = mgr->pl_head, *next; plugin; plugin = next) {
next = plugin->next;
- deinit_plugin(plugin);
+ deinit_plugin(plugin, NULL); /* result ignored (not resuming) */
free_plugin(plugin);
}
@@ -82,6 +94,7 @@ static ptx_plugin_t *load_plugin(ptx_plugin_manager_t *manager, const char *plug
goto cleanup;
}
+ plugin->pl_state = PL_STATE_NEW;
plugin->manager = manager;
memcpy(plugin->fname, plugin_fname, fnamelen); /* NUL-termination achieved thanks to calloc */
@@ -99,8 +112,19 @@ static ptx_plugin_t *load_plugin(ptx_plugin_manager_t *manager, const char *plug
goto cleanup;
}
+ if (pl_desc->desc_len != sizeof(struct ptx_plugin_desc)) {
+ fprintf(stderr, "(plugin) %s: descriptor struct size mismatch: %zu != %zu (expected)\n", plugin_fname, pl_desc->desc_len, sizeof(struct ptx_plugin_desc));
+ goto cleanup;
+ }
+
+ if (pl_desc->desc_ver != PTX_CUR_PLUGIN_VERSION) {
+ fprintf(stderr, "(plugin) %s: is for a different version of ptxmc! %u != %u (expected)\n", plugin_fname, pl_desc->desc_ver, PTX_CUR_PLUGIN_VERSION);
+ goto cleanup;
+ }
+
plugin->desc = pl_desc;
+ plugin->pl_state = PL_STATE_LOADED;
return plugin;
cleanup:
@@ -182,15 +206,72 @@ cleanup_iter:
return 0;
}
-static void deinit_plugin(ptx_plugin_t *plugin)
+static int init_plugin(ptx_plugin_t *plugin, void *resume)
{
- /* TODO */
- UNUSED(plugin);
+ assert(plugin);
+ assert(plugin->pl_state == plugin->pl_state == PL_STATE_LOADED);
+
+ plugin->pl_state = PL_STATE_INITIALIZING;
+ int result = 0;
+
+ if (plugin->desc->init_proc) {
+ result = (plugin->desc->init_proc)(plugin, resume);
+ } else {
+ fprintf(stderr, "(plugin) warning: plugin %s/%s has no initialization function! (does it do anything?)\n", plugin->desc->name, plugin->fname);
+ }
+
+ if (result < 0) {
+ plugin->pl_state = PL_STATE_ERROR;
+ fprintf(stderr, "(plugin) init_plugin: %s/%s: initializer failed (%d)\n", plugin->desc->name, plugin->fname, result);
+ } else {
+ plugin->pl_state = PL_STATE_INITIALIZED;
+ }
+
+ return result;
+}
+
+int ptx_plugin_manager_init_plugins(ptx_plugin_manager_t *mgr)
+{
+ for (ptx_plugin_t *plugin = mgr->pl_head; plugin; plugin = plugin->next)
+ {
+ if (plugin->pl_state >= PL_STATE_INITIALIZED) continue;
+ assert(plugin->pl_state != PL_STATE_INITIALIZING); /* function should never be called from a plugin initializer */
+
+ init_plugin(plugin, NULL);
+ }
+}
+
+static int deinit_plugin(ptx_plugin_t *plugin, void **presume)
+{
+ assert(plugin);
+ assert(plugin->pl_state >= PL_STATE_INITIALIZED && plugin->pl_state < PL_STATE_DEINITIALIZED);
+
+ int result = DEINIT_OK;
+ if (plugin->desc->deinit_proc) {
+ result = (plugin->desc->deinit_proc)(plugin, presume);
+ } else {
+ result = presume ? DEINIT_ERR_RESUME_UNSUPPORTED : DEINIT_OK;
+ }
+
+ plugin->pl_state = PL_STATE_DEINITIALIZED;
+ return result;
}
static void free_plugin(ptx_plugin_t *plugin)
{
+ assert(plugin->pl_state < PL_STATE_INITIALIZED || plugin->pl_state >= PL_STATE_DEINITIALIZED);
+
dlclose(plugin->modp);
close(plugin->path_fd);
+
+ plugin->pl_state = PL_STATE_UNLOADED; /* state never observed by the software */
+
free(plugin);
}
+
+PTX_API void ptx_plugin_set_mainloop_func(ptx_plugin_t *plugin, void *func)
+{
+ assert(plugin);
+ assert(plugin->pl_state == PL_STATE_INITIALIZING || plugin->pl_state == PL_STATE_INITIALIZED);
+ UNUSED(plugin, func);
+}