
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. EVERYTHING ELSE GOES INTO TRUNK AND WILL BE MERGED INTO STABLE BY VALARIS AND WIZPUTER. -- VALARIS git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@5094 54d463be-8e91-2dee-dedb-b68131a5f0ec
368 lines
9.0 KiB
C
368 lines
9.0 KiB
C
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
|
|
// For more information, see LICENCE in the main folder
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "plugin.h"
|
|
#include "plugins.h"
|
|
#include "../common/mmo.h"
|
|
#include "../common/core.h"
|
|
#include "../common/timer.h"
|
|
#include "../common/utils.h"
|
|
#include "../common/socket.h"
|
|
#include "../common/malloc.h"
|
|
#include "../common/version.h"
|
|
#include "../common/showmsg.h"
|
|
|
|
//////////////////////////////////////////////
|
|
|
|
typedef struct _Plugin_Event {
|
|
void (*func)(void);
|
|
struct _Plugin_Event *next;
|
|
} Plugin_Event;
|
|
|
|
typedef struct _Plugin_Event_List {
|
|
char *name;
|
|
struct _Plugin_Event_List *next;
|
|
struct _Plugin_Event *events;
|
|
} Plugin_Event_List;
|
|
|
|
static int auto_search = 1;
|
|
static int load_priority = 0;
|
|
Plugin_Event_List *event_head = NULL;
|
|
Plugin *plugin_head = NULL;
|
|
|
|
Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" };
|
|
|
|
static size_t call_table_size = 0;
|
|
static size_t max_call_table = 0;
|
|
|
|
////// Plugin Events Functions //////////////////
|
|
|
|
int register_plugin_func (char *name)
|
|
{
|
|
Plugin_Event_List *evl;
|
|
if (name) {
|
|
evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List));
|
|
evl->name = (char *) aMalloc (strlen(name) + 1);
|
|
|
|
evl->next = event_head;
|
|
strcpy(evl->name, name);
|
|
evl->events = NULL;
|
|
event_head = evl;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Plugin_Event_List *search_plugin_func (char *name)
|
|
{
|
|
Plugin_Event_List *evl = event_head;
|
|
while (evl) {
|
|
if (strcmpi(evl->name, name) == 0)
|
|
return evl;
|
|
evl = evl->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int register_plugin_event (void (*func)(void), char* name)
|
|
{
|
|
Plugin_Event_List *evl = search_plugin_func(name);
|
|
if (!evl) {
|
|
// register event if it doesn't exist already
|
|
register_plugin_func(name);
|
|
// relocate the new event list
|
|
evl = search_plugin_func(name);
|
|
}
|
|
if (evl) {
|
|
Plugin_Event *ev;
|
|
|
|
ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event));
|
|
ev->func = func;
|
|
ev->next = NULL;
|
|
|
|
if (evl->events == NULL)
|
|
evl->events = ev;
|
|
else {
|
|
Plugin_Event *ev2 = evl->events;
|
|
while (ev2) {
|
|
if (ev2->next == NULL) {
|
|
ev2->next = ev;
|
|
break;
|
|
}
|
|
ev2 = ev2->next;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int plugin_event_trigger (char *name)
|
|
{
|
|
int c = 0;
|
|
Plugin_Event_List *evl = search_plugin_func(name);
|
|
if (evl) {
|
|
Plugin_Event *ev = evl->events;
|
|
while (ev) {
|
|
ev->func();
|
|
ev = ev->next;
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
////// Plugins Call Table Functions /////////
|
|
|
|
int export_symbol (void *var, int offset)
|
|
{
|
|
//printf ("0x%x\n", var);
|
|
|
|
// add to the end of the list
|
|
if (offset < 0)
|
|
offset = call_table_size;
|
|
|
|
// realloc if not large enough
|
|
if ((size_t)offset >= max_call_table) {
|
|
max_call_table = 1 + offset;
|
|
plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*));
|
|
|
|
// clear the new alloced block
|
|
memset(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*));
|
|
}
|
|
|
|
// the new table size is delimited by the new element at the end
|
|
if ((size_t)offset >= call_table_size)
|
|
call_table_size = offset+1;
|
|
|
|
// put the pointer at the selected place
|
|
plugin_call_table[offset] = var;
|
|
return 0;
|
|
}
|
|
|
|
////// Plugins Core /////////////////////////
|
|
|
|
Plugin *plugin_open (const char *filename)
|
|
{
|
|
Plugin *plugin;
|
|
Plugin_Info *info;
|
|
Plugin_Event_Table *events;
|
|
void **procs;
|
|
int init_flag = 1;
|
|
|
|
//printf ("loading %s\n", filename);
|
|
|
|
// Check if the plugin has been loaded before
|
|
plugin = plugin_head;
|
|
while (plugin) {
|
|
// returns handle to the already loaded plugin
|
|
if (plugin->state && strcmpi(plugin->filename, filename) == 0) {
|
|
//printf ("not loaded (duplicate) : %s\n", filename);
|
|
return plugin;
|
|
}
|
|
plugin = plugin->next;
|
|
}
|
|
|
|
plugin = (Plugin *)aCalloc(1, sizeof(Plugin));
|
|
plugin->state = -1; // not loaded
|
|
|
|
plugin->dll = DLL_OPEN(filename);
|
|
if (!plugin->dll) {
|
|
//printf ("not loaded (invalid file) : %s\n", filename);
|
|
plugin_unload(plugin);
|
|
return NULL;
|
|
}
|
|
|
|
// Retrieve plugin information
|
|
plugin->state = 0; // initialising
|
|
DLL_SYM (info, plugin->dll, "plugin_info");
|
|
// For high priority plugins (those that are explicitly loaded from the conf file)
|
|
// we'll ignore them even (could be a 3rd party dll file)
|
|
if ((!info && load_priority == 0) ||
|
|
(info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code
|
|
(info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server
|
|
(info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP))))
|
|
{
|
|
//printf ("not loaded (incompatible) : %s\n", filename);
|
|
plugin_unload(plugin);
|
|
return NULL;
|
|
}
|
|
plugin->info = (info) ? info : &default_info;
|
|
|
|
plugin->filename = (char *) aMalloc (strlen(filename) + 1);
|
|
strcpy(plugin->filename, filename);
|
|
|
|
// Initialise plugin call table (For exporting procedures)
|
|
DLL_SYM (procs, plugin->dll, "plugin_call_table");
|
|
if (procs) *procs = plugin_call_table;
|
|
|
|
// Register plugin events
|
|
DLL_SYM (events, plugin->dll, "plugin_event_table");
|
|
if (events) {
|
|
int i = 0;
|
|
while (events[i].func_name) {
|
|
if (strcmpi(events[i].event_name, "Plugin_Test") == 0) {
|
|
int (*test_func)(void);
|
|
DLL_SYM (test_func, plugin->dll, events[i].func_name);
|
|
if (test_func && test_func() == 0) {
|
|
// plugin has failed test, disabling
|
|
//printf ("disabled (failed test) : %s\n", filename);
|
|
init_flag = 0;
|
|
}
|
|
} else {
|
|
void (*func)(void);
|
|
DLL_SYM (func, plugin->dll, events[i].func_name);
|
|
if (func) register_plugin_event (func, events[i].event_name);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
plugin->next = plugin_head;
|
|
plugin_head = plugin;
|
|
|
|
plugin->state = init_flag; // fully loaded
|
|
ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename);
|
|
|
|
return plugin;
|
|
}
|
|
|
|
void plugin_load (const char *filename)
|
|
{
|
|
plugin_open(filename);
|
|
}
|
|
|
|
void plugin_unload (Plugin *plugin)
|
|
{
|
|
if (plugin == NULL)
|
|
return;
|
|
if (plugin->filename) aFree(plugin->filename);
|
|
if (plugin->dll) DLL_CLOSE(plugin->dll);
|
|
aFree(plugin);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
char *DLL_ERROR(void)
|
|
{
|
|
static char dllbuf[80];
|
|
DWORD dw = GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL);
|
|
return dllbuf;
|
|
}
|
|
#endif
|
|
|
|
////// Initialize/Finalize ////////////////////
|
|
|
|
int plugins_config_read(const char *cfgName)
|
|
{
|
|
char line[1024], w1[1024], w2[1024];
|
|
FILE *fp;
|
|
|
|
fp = fopen(cfgName, "r");
|
|
if (fp == NULL) {
|
|
ShowError("File not found: %s\n", cfgName);
|
|
return 1;
|
|
}
|
|
while (fgets(line, 1020, fp)) {
|
|
if(line[0] == '/' && line[1] == '/')
|
|
continue;
|
|
if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
|
|
continue;
|
|
|
|
if (strcmpi(w1, "auto_search") == 0) {
|
|
if(strcmpi(w2, "yes")==0)
|
|
auto_search = 1;
|
|
else if(strcmpi(w2, "no")==0)
|
|
auto_search = 0;
|
|
else auto_search = atoi(w2);
|
|
} else if (strcmpi(w1, "plugin") == 0) {
|
|
char filename[128];
|
|
sprintf (filename, "plugins/%s%s", w2, DLL_EXT);
|
|
plugin_load(filename);
|
|
} else if (strcmpi(w1, "import") == 0)
|
|
plugins_config_read(w2);
|
|
}
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
void plugins_init (void)
|
|
{
|
|
char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf";
|
|
register_plugin_func("Plugin_Init");
|
|
register_plugin_func("Plugin_Final");
|
|
register_plugin_func("Athena_Init");
|
|
register_plugin_func("Athena_Final");
|
|
|
|
// networking
|
|
export_symbol (func_parse_table, 18);
|
|
export_symbol (RFIFOSKIP, 17);
|
|
export_symbol (WFIFOSET, 16);
|
|
export_symbol (delete_session, 15);
|
|
export_symbol (session, 14);
|
|
export_symbol (&fd_max, 13);
|
|
export_symbol (addr_, 12);
|
|
// timers
|
|
export_symbol (get_uptime, 11);
|
|
export_symbol (delete_timer, 10);
|
|
export_symbol (add_timer_func_list, 9);
|
|
export_symbol (add_timer_interval, 8);
|
|
export_symbol (add_timer, 7);
|
|
export_symbol ((void *)get_svn_revision, 6);
|
|
export_symbol (gettick, 5);
|
|
// core
|
|
export_symbol (&runflag, 4);
|
|
export_symbol (arg_v, 3);
|
|
export_symbol (&arg_c, 2);
|
|
export_symbol (SERVER_NAME, 1);
|
|
export_symbol (&SERVER_TYPE, 0);
|
|
|
|
load_priority = 1;
|
|
plugins_config_read (PLUGIN_CONF_FILENAME);
|
|
load_priority = 0;
|
|
|
|
if (auto_search)
|
|
findfile("plugins", DLL_EXT, plugin_load);
|
|
|
|
plugin_event_trigger("Plugin_Init");
|
|
|
|
return;
|
|
}
|
|
|
|
void plugins_final (void)
|
|
{
|
|
Plugin *plugin = plugin_head, *plugin2;
|
|
Plugin_Event_List *evl = event_head, *evl2;
|
|
Plugin_Event *ev, *ev2;
|
|
|
|
plugin_event_trigger("Plugin_Final");
|
|
|
|
while (plugin) {
|
|
plugin2 = plugin->next;
|
|
plugin_unload(plugin);
|
|
plugin = plugin2;
|
|
}
|
|
|
|
while (evl) {
|
|
ev = evl->events;
|
|
while (ev) {
|
|
ev2 = ev->next;
|
|
aFree(ev);
|
|
ev = ev2;
|
|
}
|
|
evl2 = evl->next;
|
|
aFree(evl->name);
|
|
aFree(evl);
|
|
evl = evl2;
|
|
}
|
|
|
|
aFree(plugin_call_table);
|
|
|
|
return;
|
|
}
|