// Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder #include "../common/mmo.h" #include "../common/core.h" #include "../common/timer.h" #include "../common/utils.h" // findfile() #include "../common/socket.h" #include "../common/malloc.h" #include "../common/version.h" #include "../common/showmsg.h" #include "plugins.h" #include #include #include #ifndef WIN32 #include #endif ////////////////////////////////////////////// typedef struct _Plugin_Event { struct _Plugin_Event* next; Plugin_Event_Func* func; } Plugin_Event; typedef struct _Plugin_Event_List { struct _Plugin_Event_List* next; char* name; struct _Plugin_Event* events; } Plugin_Event_List; static int auto_search = 0; static int load_priority = 0; Plugin_Event_List* event_head = NULL; Plugin* plugin_head = NULL; static 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 ){ //ShowDebug("register_plugin_func(%s)\n", name); CREATE(evl, Plugin_Event_List, 1); evl->next = event_head; evl->name = aStrdup(name); evl->events = NULL; event_head = evl; } return 0; } static 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(Plugin_Event_Func* func, char* name) { Plugin_Event_List* evl = search_plugin_func(name); //ShowDebug("register_plugin_event(0x%x, %s)\n", func, name); if( !evl ) {// event does not exist, register register_plugin_func(name); // get the new event list evl = search_plugin_func(name); } if( evl ){ Plugin_Event* ev; CREATE(ev, Plugin_Event, 1); ev->func = func; ev->next = NULL; // insert event at the end of the linked list if( evl->events == NULL ) evl->events = ev; else { Plugin_Event* last_ev = evl->events; while( last_ev ){ if( last_ev->next == NULL ){ last_ev->next = ev; break; } last_ev = last_ev->next; } } } return 0; } int plugin_event_trigger(char* name) { int c = 0; Plugin_Event_List* evl = search_plugin_func(name); //ShowDebug("plugin_event_trigger(%s)\n", name); if( evl ){ Plugin_Event* ev = evl->events; while( ev ){ ev->func(); //ShowDebug("plugin_event_trigger: Executing function 0x%x.\n", ev->func); ev = ev->next; ++c; } } return c; } ////// Plugins Call Table Functions ///////// int export_symbol(void* var, size_t offset) { //ShowDebug("export_symbol(0x%x,%d)\n", var,offset); // add to the end of the list if( offset < 0 ) offset = call_table_size; if( offset >= max_call_table ) {// realloc if not large enough max_call_table = 1 + offset; RECREATE(plugin_call_table, void*, max_call_table); // 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( 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 ///////////////////////// static int plugin_iscompatible(char* version) { int req_major = 0; int req_minor = 0; int major = 0; int minor = 0; if( version == NULL ) return 0; sscanf(version, "%d.%d", &req_major, &req_minor); sscanf(version, "%d.%d", &major, &minor); return ( req_major == major || req_minor <= minor ); } Plugin* plugin_open(const char* filename) { Plugin* plugin; Plugin_Info* info; Plugin_Event_Table* events; void** procs; int init_flag = 1; //ShowDebug("plugin_open(%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 ){ ShowWarning("plugin_open: not loaded (duplicate) : '"CL_WHITE"%s"CL_RESET"'\n", filename); return plugin; } plugin = plugin->next; } CREATE(plugin, Plugin, 1); plugin->state = -1; // not loaded plugin->dll = DLL_OPEN(filename); if( !plugin->dll ){ ShowWarning("plugin_open: not loaded (invalid file) : '"CL_WHITE"%s"CL_RESET"'\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 ) {// foreign plugin //ShowDebug("plugin_open: plugin_info not found\n"); if( load_priority == 0 ) {// not requested //ShowDebug("plugin_open: not loaded (not requested) : '"CL_WHITE"%s"CL_RESET"'\n", filename); plugin_unload(plugin); return NULL; } } else if( !plugin_iscompatible(info->req_version) ) {// incompatible version ShowWarning("plugin_open: not loaded (incompatible version '%s' -> '%s') : '"CL_WHITE"%s"CL_RESET"'\n", info->req_version, PLUGIN_VERSION, filename); plugin_unload(plugin); return NULL; } else if( (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP) ) {// not for this server //ShowDebug("plugin_open: not loaded (incompatible) : '"CL_WHITE"%s"CL_RESET"'\n", filename); plugin_unload(plugin); return NULL; } plugin->info = ( info != NULL ? info : &default_info ); plugin->filename = aStrdup(filename); // Initialise plugin call table (For exporting procedures) DLL_SYM(procs, plugin->dll, "plugin_call_table"); if( procs ) *procs = plugin_call_table; //else ShowDebug("plugin_open: plugin_call_table not found\n"); // Register plugin events DLL_SYM(events, plugin->dll, "plugin_event_table"); if( events ){ int i = 0; //ShowDebug("plugin_open: parsing plugin_event_table\n"); while( events[i].func_name ){ if( strcmpi(events[i].event_name, EVENT_PLUGIN_TEST) == 0 ){ Plugin_Test_Func* test_func; DLL_SYM(test_func, plugin->dll, events[i].func_name); //ShowDebug("plugin_open: invoking "EVENT_PLUGIN_TEST" with %s()\n", events[i].func_name); if( test_func && test_func() == 0 ){ // plugin has failed test, disabling //ShowDebug("plugin_open: disabled (failed test) : %s\n", filename); init_flag = 0; } } else { Plugin_Event_Func* func; DLL_SYM(func, plugin->dll, events[i].func_name); if (func) register_plugin_event(func, events[i].event_name); } i++; } } //else ShowDebug("plugin_open: plugin_event_table not found\n"); 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 //////////////////// static 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, sizeof(line), 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) { // Sugested functionality: // add atcommands/script commands (Borf) char* PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf"; //ShowDebug("plugins_init()\n"); register_plugin_func(EVENT_PLUGIN_INIT); register_plugin_func(EVENT_PLUGIN_FINAL); register_plugin_func(EVENT_ATHENA_INIT); register_plugin_func(EVENT_ATHENA_FINAL); // networking export_symbol(RFIFOSKIP, SYMBOL_RFIFOSKIP); export_symbol(WFIFOSET, SYMBOL_WFIFOSET); export_symbol(do_close, SYMBOL_DELETE_SESSION); export_symbol(session, SYMBOL_SESSION); export_symbol(&fd_max, SYMBOL_FD_MAX); export_symbol(addr_, SYMBOL_ADDR); // timers export_symbol(get_uptime, SYMBOL_GET_UPTIME); export_symbol(delete_timer, SYMBOL_DELETE_TIMER); export_symbol(add_timer_func_list, SYMBOL_ADD_TIMER_FUNC_LIST); export_symbol(add_timer_interval, SYMBOL_ADD_TIMER_INTERVAL); export_symbol(add_timer, SYMBOL_ADD_TIMER); export_symbol((void*)get_svn_revision, SYMBOL_GET_SVN_REVISION); export_symbol(gettick, SYMBOL_GETTICK); // core export_symbol(parse_console, SYMBOL_PARSE_CONSOLE); export_symbol(&runflag, SYMBOL_RUNFLAG); export_symbol(arg_v, SYMBOL_ARG_V); export_symbol(&arg_c, SYMBOL_ARG_C); export_symbol(SERVER_NAME, SYMBOL_SERVER_NAME); export_symbol(&SERVER_TYPE, SYMBOL_SERVER_TYPE); load_priority = 1; plugins_config_read(PLUGIN_CONF_FILENAME); load_priority = 0; if( auto_search ) findfile("plugins", DLL_EXT, plugin_load); plugin_event_trigger(EVENT_PLUGIN_INIT); return; } void plugins_final(void) { Plugin* plugin = plugin_head; Plugin* next_plugin; Plugin_Event_List* evl = event_head; Plugin_Event_List* next_evl; Plugin_Event* ev; Plugin_Event* next_ev; //ShowDebug("plugins_final()\n"); plugin_event_trigger(EVENT_PLUGIN_FINAL); while( plugin ){ next_plugin = plugin->next; plugin_unload(plugin); plugin = next_plugin; } while( evl ){ ev = evl->events; while( ev ){ next_ev = ev->next; aFree(ev); ev = next_ev; } next_evl = evl->next; aFree(evl->name); aFree(evl); evl = next_evl; } aFree(plugin_call_table); return; }