root/src/common/plugins.c

Revision 1, 10.9 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/mmo.h"
5#include "../common/core.h"
6#include "../common/timer.h"
7#include "../common/utils.h" // findfile()
8#include "../common/socket.h"
9#include "../common/malloc.h"
10#include "../common/version.h"
11#include "../common/showmsg.h"
12#include "plugins.h"
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#ifndef WIN32
18#include <unistd.h>
19#endif
20
21//////////////////////////////////////////////
22
23typedef struct _Plugin_Event {
24        struct _Plugin_Event* next;
25        Plugin_Event_Func* func;
26} Plugin_Event;
27
28typedef struct _Plugin_Event_List {
29        struct _Plugin_Event_List* next;
30        char* name;
31        struct _Plugin_Event* events;
32} Plugin_Event_List;
33
34static int auto_search = 0;
35static int load_priority = 0;
36Plugin_Event_List* event_head = NULL;
37Plugin* plugin_head = NULL;
38
39static Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" };
40
41void** plugin_call_table;
42static size_t call_table_size   = 0;
43static size_t max_call_table    = 0;
44
45////// Plugin Events Functions //////////////////
46
47int register_plugin_func(char* name)
48{
49        Plugin_Event_List* evl;
50        if( name ){
51                //ShowDebug("register_plugin_func(%s)\n", name);
52                CREATE(evl, Plugin_Event_List, 1);
53                evl->next = event_head;
54                evl->name = aStrdup(name);
55                evl->events = NULL;
56                event_head = evl;
57        }
58        return 0;
59}
60
61static Plugin_Event_List* search_plugin_func(char* name)
62{
63        Plugin_Event_List* evl = event_head;
64        while( evl ){
65                if( strcmpi(evl->name,name) == 0 )
66                        return evl;
67                evl = evl->next;
68        }
69        return NULL;
70}
71
72int register_plugin_event(Plugin_Event_Func* func, char* name)
73{
74        Plugin_Event_List* evl = search_plugin_func(name);
75        //ShowDebug("register_plugin_event(0x%x, %s)\n", func, name);
76        if( !evl )
77        {// event does not exist, register
78                register_plugin_func(name);
79                // get the new event list
80                evl = search_plugin_func(name);
81        }
82        if( evl ){
83                Plugin_Event* ev;
84
85                CREATE(ev, Plugin_Event, 1);
86                ev->func = func;
87                ev->next = NULL;
88
89                // insert event at the end of the linked list
90                if( evl->events == NULL )
91                        evl->events = ev;
92                else {
93                        Plugin_Event* last_ev = evl->events;
94                        while( last_ev ){
95                                if( last_ev->next == NULL ){
96                                        last_ev->next = ev;
97                                        break;
98                                }
99                                last_ev = last_ev->next;
100                        }
101                }
102        }
103        return 0;
104}
105
106int plugin_event_trigger(char* name)
107{
108        int c = 0;
109        Plugin_Event_List* evl = search_plugin_func(name);
110        //ShowDebug("plugin_event_trigger(%s)\n", name);
111        if( evl ){
112                Plugin_Event* ev = evl->events;
113                while( ev ){
114                        ev->func();
115                        //ShowDebug("plugin_event_trigger: Executing function 0x%x.\n", ev->func);
116                        ev = ev->next;
117                        ++c;
118                }
119        }
120        return c;
121}
122
123////// Plugins Call Table Functions /////////
124
125int export_symbol(void* var, size_t offset)
126{
127        //ShowDebug("export_symbol(0x%x,%d)\n", var,offset);
128
129        // add to the end of the list
130        if( offset < 0 )
131                offset = call_table_size;
132
133        if( offset >= max_call_table )
134        {// realloc if not large enough 
135                max_call_table = 1 + offset;
136                RECREATE(plugin_call_table, void*, max_call_table);
137
138                // clear the new alloced block
139                memset(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*));
140        }
141
142        // the new table size is delimited by the new element at the end
143        if( offset >= call_table_size )
144                call_table_size = offset+1;
145
146        // put the pointer at the selected place
147        plugin_call_table[offset] = var;
148        return 0;
149}
150
151////// Plugins Core /////////////////////////
152
153static int plugin_iscompatible(char* version)
154{
155        int req_major = 0;
156        int req_minor = 0;
157        int major = 0;
158        int minor = 0;
159
160        if( version == NULL )
161                return 0;
162        sscanf(version, "%d.%d", &req_major, &req_minor);
163        sscanf(version, "%d.%d", &major, &minor);
164        return ( req_major == major || req_minor <= minor );
165}
166
167Plugin* plugin_open(const char* filename)
168{
169        Plugin* plugin;
170        Plugin_Info* info;
171        Plugin_Event_Table* events;
172        void** procs;
173        int init_flag = 1;
174
175        //ShowDebug("plugin_open(%s)\n", filename);
176       
177        // Check if the plugin has been loaded before
178        plugin = plugin_head;
179        while (plugin) {
180                // returns handle to the already loaded plugin
181                if( plugin->state && strcmpi(plugin->filename, filename) == 0 ){
182                        ShowWarning("plugin_open: not loaded (duplicate) : '"CL_WHITE"%s"CL_RESET"'\n", filename);
183                        return plugin;
184                }
185                plugin = plugin->next;
186        }
187
188        CREATE(plugin, Plugin, 1);
189        plugin->state = -1;     // not loaded
190
191        plugin->dll = DLL_OPEN(filename);
192        if( !plugin->dll ){
193                ShowWarning("plugin_open: not loaded (invalid file) : '"CL_WHITE"%s"CL_RESET"'\n", filename);
194                plugin_unload(plugin);
195                return NULL;
196        }
197
198        // Retrieve plugin information
199        plugin->state = 0;      // initialising
200        info = (Plugin_Info*)DLL_SYM(plugin->dll, "plugin_info");
201        // For high priority plugins (those that are explicitly loaded from the conf file)
202        // we'll ignore them even (could be a 3rd party dll file)
203        if( !info )
204        {// foreign plugin
205                //ShowDebug("plugin_open: plugin_info not found\n");
206                if( load_priority == 0 )
207                {// not requested
208                        //ShowDebug("plugin_open: not loaded (not requested) : '"CL_WHITE"%s"CL_RESET"'\n", filename);
209                        plugin_unload(plugin);
210                        return NULL;
211                }
212        } else if( !plugin_iscompatible(info->req_version) )
213        {// incompatible version
214                ShowWarning("plugin_open: not loaded (incompatible version '%s' -> '%s') : '"CL_WHITE"%s"CL_RESET"'\n", info->req_version, PLUGIN_VERSION, filename);
215                plugin_unload(plugin);
216                return NULL;
217        } else if( (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) ||
218                (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP) )
219        {// not for this server
220                //ShowDebug("plugin_open: not loaded (incompatible) : '"CL_WHITE"%s"CL_RESET"'\n", filename);
221                plugin_unload(plugin);
222                return NULL;
223        }
224
225        plugin->info = ( info != NULL ? info : &default_info );
226        plugin->filename = aStrdup(filename);
227
228        // Initialise plugin call table (For exporting procedures)
229        procs = (void**)DLL_SYM(plugin->dll, "plugin_call_table");
230        if( procs )
231                *procs = plugin_call_table;
232        //else ShowDebug("plugin_open: plugin_call_table not found\n");
233
234        // Register plugin events
235        events = (Plugin_Event_Table*)DLL_SYM(plugin->dll, "plugin_event_table");
236        if( events ){
237                int i = 0;
238                //ShowDebug("plugin_open: parsing plugin_event_table\n");
239                while( events[i].func_name ){
240                        if( strcmpi(events[i].event_name, EVENT_PLUGIN_TEST) == 0 ){
241                                Plugin_Test_Func* test_func;
242                                test_func = (Plugin_Test_Func*)DLL_SYM(plugin->dll, events[i].func_name);
243                                //ShowDebug("plugin_open: invoking "EVENT_PLUGIN_TEST" with %s()\n", events[i].func_name);
244                                if( test_func && test_func() == 0 ){
245                                        // plugin has failed test, disabling
246                                        //ShowDebug("plugin_open: disabled (failed test) : %s\n", filename);
247                                        init_flag = 0;
248                                }
249                        } else {
250                                Plugin_Event_Func* func;
251                                func = (Plugin_Event_Func*)DLL_SYM(plugin->dll, events[i].func_name);
252                                if (func)
253                                        register_plugin_event(func, events[i].event_name);
254                        }
255                        i++;
256                }
257        }
258        //else ShowDebug("plugin_open: plugin_event_table not found\n");
259
260        plugin->next = plugin_head;
261        plugin_head = plugin;
262
263        plugin->state = init_flag;      // fully loaded
264        ShowStatus("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename);
265
266        return plugin;
267}
268
269void plugin_load(const char* filename)
270{
271        plugin_open(filename);
272}
273
274void plugin_unload(Plugin* plugin)
275{
276        if( plugin == NULL )
277                return;
278        if( plugin->filename )
279                aFree(plugin->filename);
280        if( plugin->dll )
281                DLL_CLOSE(plugin->dll);
282        aFree(plugin);
283}
284
285#ifdef WIN32
286char *DLL_ERROR(void)
287{
288        static char dllbuf[80];
289        DWORD dw = GetLastError();
290        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL);
291        return dllbuf;
292}
293#endif
294
295////// Initialize/Finalize ////////////////////
296
297static int plugins_config_read(const char *cfgName)
298{
299        char line[1024], w1[1024], w2[1024];
300        FILE *fp;
301
302        fp = fopen(cfgName, "r");
303        if( fp == NULL ){
304                ShowError("File not found: %s\n", cfgName);
305                return 1;
306        }
307        while( fgets(line, sizeof(line), fp) )
308        {
309                if( line[0] == '/' && line[1] == '/' )
310                        continue;
311                if( sscanf(line,"%[^:]: %[^\r\n]",w1,w2) != 2 )
312                        continue;
313
314                if( strcmpi(w1,"auto_search") == 0 ){
315                        if( strcmpi(w2,"yes") == 0 )
316                                auto_search = 1;
317                        else if( strcmpi(w2,"no") == 0 )
318                                auto_search = 0;
319                        else
320                                auto_search = atoi(w2);
321                } else if( strcmpi(w1,"plugin") == 0 ){
322                        char filename[128];
323                        sprintf(filename, "plugins/%s%s", w2, DLL_EXT);
324                        plugin_load(filename);
325                } else if( strcmpi(w1,"import") == 0 )
326                        plugins_config_read(w2);
327        }
328        fclose(fp);
329        return 0;
330}
331
332void plugins_init(void)
333{
334        // Sugested functionality:
335        // add atcommands/script commands (Borf)
336        char* PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf";
337        //ShowDebug("plugins_init()\n");
338        register_plugin_func(EVENT_PLUGIN_INIT);
339        register_plugin_func(EVENT_PLUGIN_FINAL);
340        register_plugin_func(EVENT_ATHENA_INIT);
341        register_plugin_func(EVENT_ATHENA_FINAL);
342
343        // networking
344        EXPORT_SYMBOL(RFIFOSKIP,  SYMBOL_RFIFOSKIP);
345        EXPORT_SYMBOL(WFIFOSET,   SYMBOL_WFIFOSET);
346        EXPORT_SYMBOL(do_close,   SYMBOL_DELETE_SESSION);
347        EXPORT_SYMBOL(session,    SYMBOL_SESSION);
348        EXPORT_SYMBOL(&fd_max,    SYMBOL_FD_MAX);
349        EXPORT_SYMBOL(addr_,      SYMBOL_ADDR);
350        // timers
351        EXPORT_SYMBOL(get_uptime,              SYMBOL_GET_UPTIME);
352        EXPORT_SYMBOL(delete_timer,            SYMBOL_DELETE_TIMER);
353        EXPORT_SYMBOL(add_timer_func_list,     SYMBOL_ADD_TIMER_FUNC_LIST);
354        EXPORT_SYMBOL(add_timer_interval,      SYMBOL_ADD_TIMER_INTERVAL);
355        EXPORT_SYMBOL(add_timer,               SYMBOL_ADD_TIMER);
356        EXPORT_SYMBOL((void*)get_svn_revision, SYMBOL_GET_SVN_REVISION);
357        EXPORT_SYMBOL(gettick,                 SYMBOL_GETTICK);
358        // core
359        EXPORT_SYMBOL(parse_console, SYMBOL_PARSE_CONSOLE);
360        EXPORT_SYMBOL(&runflag,      SYMBOL_RUNFLAG);
361        EXPORT_SYMBOL(arg_v,         SYMBOL_ARG_V);
362        EXPORT_SYMBOL(&arg_c,        SYMBOL_ARG_C);
363        EXPORT_SYMBOL(SERVER_NAME,   SYMBOL_SERVER_NAME);
364        EXPORT_SYMBOL(&SERVER_TYPE,  SYMBOL_SERVER_TYPE);
365
366        load_priority = 1;
367        plugins_config_read(PLUGIN_CONF_FILENAME);
368        load_priority = 0;
369
370        if( auto_search )
371                findfile("plugins", DLL_EXT, plugin_load);
372
373        plugin_event_trigger(EVENT_PLUGIN_INIT);
374
375        return;
376}
377
378void plugins_final(void)
379{
380        Plugin* plugin = plugin_head;
381        Plugin* next_plugin;
382        Plugin_Event_List* evl = event_head;
383        Plugin_Event_List* next_evl;
384        Plugin_Event* ev;
385        Plugin_Event* next_ev;
386
387        //ShowDebug("plugins_final()\n");
388        plugin_event_trigger(EVENT_PLUGIN_FINAL);
389
390        while( plugin ){
391                next_plugin = plugin->next;
392                plugin_unload(plugin);
393                plugin = next_plugin;
394        }
395
396        while( evl ){
397                ev = evl->events;
398                while( ev ){
399                        next_ev = ev->next;
400                        aFree(ev);
401                        ev = next_ev;
402                }
403                next_evl = evl->next;
404                aFree(evl->name);
405                aFree(evl);
406                evl = next_evl;
407        }
408
409        aFree(plugin_call_table);
410
411        return;
412}
Note: See TracBrowser for help on using the browser.