int mm_RegUserMsg(const char *pszName, int iSize) { int imsgid; MRegMsg *nmsg=NULL; META_ENGINE_HANDLE(int, 0, FN_REGUSERMSG, pfnRegUserMsg, (pszName, iSize)); // Expand the macro, since we need to do extra work. /// RETURN_API() if (--CALL_API_count>0) /*Restore backup*/ PublicMetaGlobals = backup_meta_globals; if(status==MRES_OVERRIDE) { META_DEBUG(loglevel, ("Returning (override) %s()", pfn_string)); imsgid=override_ret; } else imsgid=orig_ret; // Add the msgid, name, and size to our saved list, if we haven't // already. nmsg=RegMsgs->find(imsgid); if(nmsg) { if(FStrEq(pszName, nmsg->name)) // This name/msgid pair was already registered. META_DEBUG(3, ("user message registered again: name=%s, msgid=%d", pszName, imsgid)); else // This msgid was previously used by a different message name. META_ERROR("user message id reused: msgid=%d, oldname=%s, newname=%s", imsgid, nmsg->name, pszName); } else RegMsgs->add(pszName, imsgid, iSize); return(imsgid); }
mBOOL MHook::enqueue(MFuncQueue *mfq, event_args_t *args, const char *logline) { func_item_t *fp; if(!mfq || !args || !logline) RETURN_ERRNO(mFALSE, ME_ARGUMENT); fp = (func_item_t *) malloc(sizeof(func_item_t)); if(!fp) { META_ERROR("malloc failed for func_item_t"); RETURN_ERRNO(mFALSE, ME_NOMEM); } fp->hook=this; fp->evargs=args; fp->logline=logline; mfq->push(fp); return(mTRUE); }
void mm_AlertMessage(ALERT_TYPE atype, char *szFmt, ...) { #ifndef UNFINISHED META_ENGINE_HANDLE_void_varargs(FN_ALERTMESSAGE, pfnAlertMessage, atype, szFmt); #else /* UNFINISHED */ // Expand macro, since we need to do extra work here. // usual setup SETUP_API_CALLS_void(FN_ALERTMESSAGE, pfnAlertMessage, engine_info); char buf[MAX_STRBUF_LEN]; va_list ap; int len; char *qmsg; META_DEBUG(loglevel, ("In %s: fmt=%s", pfn_string, szFmt)); va_start(ap, szFmt); len=vsnprintf(buf, sizeof(buf), szFmt, ap) + 1; va_end(ap); // pass logmsg string to log parsing thread /// qmsg=strdup(buf); qmsg=(char *) malloc(len * sizeof(char)); if(!qmsg) META_ERROR("malloc failed for logmsg to thread queue"); else { STRNCPY(qmsg, buf, len); LogQueue->push(qmsg); } // usual passing to plugins/engine CALL_PLUGIN_API_void(P_PRE, pfnAlertMessage, (atype, "%s", buf), engine_table); CALL_ENGINE_API_void(pfnAlertMessage, (atype, "%s", buf)); CALL_PLUGIN_API_void(P_POST, pfnAlertMessage, (atype, "%s", buf), engine_post_table); #endif /* UNFINISHED */ // usual return. RETURN_API_void() }
// Return various string-based info about the game/MOD/gamedll. const char *mutil_GetGameInfo(plid_t plid, ginfo_t type) { static char buf[MAX_STRBUF_LEN]; const char *cp; switch(type) { case GINFO_NAME: cp=GameDLL.name; break; case GINFO_DESC: cp=GameDLL.desc; break; case GINFO_GAMEDIR: cp=GameDLL.gamedir; break; case GINFO_DLL_FULLPATH: cp=GameDLL.pathname; break; case GINFO_DLL_FILENAME: cp=GameDLL.file; break; default: META_ERROR("GetGameInfo: invalid request '%d' from plugin '%s'", type, plid->name); return(NULL); } STRNCPY(buf, cp, sizeof(buf)); return(buf); }
// Return the full path of the plugin's loaded dll/so file. const char *mutil_GetPluginPath(plid_t plid) { static char buf[PATH_MAX]; MPlugin *plug; plug=Plugins->find(plid); if(!plug) { META_ERROR("GetPluginPath: couldn't find plugin '%s'", plid->name); return(NULL); } STRNCPY(buf, plug->pathname, sizeof(buf)); return(buf); }
mBOOL MPluginList::load() { int i, n; if (!ini_startup()) { META_ERROR("Problem loading plugins.ini: %s", inifile); // meta_errno should be already set in ini_startup() return(mFALSE); } META_LOG("dll: Loading plugins..."); for(i=0, n=0; i < endlist; i++) { if(plist[i].status < PL_VALID) continue; if(plist[i].load(PT_STARTUP) == mTRUE) n++; else // all plugins should be loadable at startup... META_ERROR("dll: Failed to load plugin '%s'", plist[i].file); } META_LOG("dll: Finished loading %d plugins", n); return(mTRUE); }
// Start the log parsing thread. void startup_logparse_thread(void) { int ret; LogQueue = new MLogmsgQueue(MAX_QUEUE_SIZE); HookQueue = new MFuncQueue(MAX_QUEUE_SIZE); ret=THREAD_CREATE(&logparse_thread_id, logparse_handler); if(ret != THREAD_OK) { // For now, we just exit if this fails. META_ERROR("Couldn't start thread for log parsing! Exiting..."); do_exit(1); } }
// Allow plugins to call the entity functions in the GameDLL. In // particular, calling "player()" as needed by most Bots. Suggested by // Jussi Kivilinna. qboolean mutil_CallGameEntity(plid_t plid, const char *entStr, entvars_t *pev) { plugin_info_t *plinfo; ENTITY_FN pfnEntity; plinfo=(plugin_info_t *)plid; META_DEBUG(8, ("Looking up game entity '%s' for plugin '%s'", entStr, plinfo->name)); pfnEntity = (ENTITY_FN) DLSYM(GameDLL.handle, entStr); if(!pfnEntity) { META_ERROR("Couldn't find game entity '%s' in game DLL '%s' for plugin '%s'", entStr, GameDLL.name, plinfo->name); return(false); } META_DEBUG(7, ("Calling game entity '%s' for plugin '%s'", entStr, plinfo->name)); (*pfnEntity)(pev); return(true); }
void do_link_ent(ENTITY_FN *pfnEntity, int *missing, char *entStr, entvars_t *pev) {; if(*missing) { META_DEBUG(9, ("Skipping entity '%s'; was previously found missing", entStr)); return; } if(!*pfnEntity) { META_DEBUG(9, ("Looking up game entity '%s'", entStr)); *pfnEntity = (ENTITY_FN) DLSYM(GameDLL.handle, entStr); } if(!*pfnEntity) { META_ERROR("Couldn't find game entity '%s' in game DLL '%s': %s", entStr, GameDLL.name, DLERROR()); *missing=1; return; } META_DEBUG(8, ("Linking game entity '%s'", entStr)); (*pfnEntity)(pev); }
MPlugin *MPluginList::add(MPlugin *padd) { int i; MPlugin *iplug; // Find either: // - a slot in the list that's not being used // - the end of the list for(i=0; i < endlist && plist[i].status != PL_EMPTY; i++); // couldn't find a slot to use if(i==size) { META_ERROR("Couldn't add plugin '%s' to list; reached max plugins (%d)", padd->file, i); RETURN_ERRNO(NULL, ME_MAXREACHED); } // if we found the end of the list, advance end marker if(i==endlist) endlist++; iplug = &plist[i]; // copy filename into this free slot STRNCPY(iplug->filename, padd->filename, sizeof(iplug->filename)); // Copy file offset ptr. // Can't just copy ptr, as it points to offset in padd, which will go // away; need to point to corresponding offset in iplug. iplug->file = iplug->filename + (padd->file - padd->filename); // copy description STRNCPY(iplug->desc, padd->desc, sizeof(iplug->desc)); // copy pathname STRNCPY(iplug->pathname, padd->pathname, sizeof(iplug->pathname)); // copy source iplug->source=padd->source; // copy status iplug->status=padd->status; //copy other things iplug->source_plugin_index = padd->source_plugin_index; return(iplug); }
// Add the given user msg the list and return the instance. // meta_errno values: // - ME_MAXREACHED reached max number of msgs allowed MRegMsg * DLLINTERNAL MRegMsgList::add(const char *addname, int addmsgid, int addsize) { MRegMsg *imsg; if(endlist==size) { // all slots used META_ERROR("Couldn't add registered msg '%s' to list; reached max msgs (%d)", addname, size); RETURN_ERRNO(NULL, ME_MAXREACHED); } imsg = &mlist[endlist]; endlist++; // Copy msg data into empty slot. // Note: 'addname' assumed to be a constant string allocated in the // gamedll. imsg->name=addname; imsg->msgid=addmsgid; imsg->size=addsize; return(imsg); }
mBOOL MHook::call(event_args_t *args, const char *logline) { MPlugin *plug; // missing necessary data if(type==H_NONE || !pfnHandle) RETURN_ERRNO(mFALSE, ME_BADREQ); // no event for event hook if(type==H_EVENT && event==EV_NONE) RETURN_ERRNO(mFALSE, ME_BADREQ); // no match string for logline hook if(type!=H_EVENT && !match) RETURN_ERRNO(mFALSE, ME_BADREQ); if(!(plug=Plugins->find(pl_index))) { META_ERROR("Couldn't find plugin index '%d' for hook", pl_index); RETURN_ERRNO(mFALSE, ME_NOTFOUND); } // Skip any paused plugins, for instance... if(plug->status!=PL_RUNNING) RETURN_ERRNO(mFALSE, ME_SKIPPED); switch(type) { case H_EVENT: { event_func_t pfn; pfn=(event_func_t) pfnHandle; pfn(event, args, logline); } break; case H_TRIGGER: case H_STRING: case H_REGEX: { logmatch_func_t pfn; pfn=(logmatch_func_t) pfnHandle; pfn(match, args, logline); } break; default: META_ERROR("Unrecognized hook type (%d) from plugin '%s'", type, plid->name); RETURN_ERRNO(mFALSE, ME_ARGUMENT); break; } return(mTRUE); }
int MHookList::add(plid_t plid, hook_t type, const char *match, logmatch_func_t pfnHandle) { int i; if(!plid || !match || !pfnHandle) RETURN_ERRNO(0, ME_ARGUMENT); if(type == H_NONE || type == H_EVENT) RETURN_ERRNO(0, ME_BADREQ); // any open slots? if(endlist==size) { plugin_info_t *plinfo; plinfo=(plugin_info_t *) plid; META_ERROR("Couldn't add log hook '%s' for plugin '%s'; reached max hooks (%d)", match, plinfo->name, size); RETURN_ERRNO(0, ME_MAXREACHED); } // Find either: // - a slot in the list that's not being used (malloc'd) // - the end of the list for(i=0; hlist[i]; i++); if(i==endlist) endlist++; MXlock(); hlist[i] = new MHook(i+1, plid, type, match, pfnHandle); MXunlock(); // malloc failed? if(!hlist[i]) { plugin_info_t *plinfo; plinfo=(plugin_info_t *) plid; META_ERROR("Couldn't add game event hook for plugin '%s'; malloc failed: %s", plinfo->name, strerror(errno)); RETURN_ERRNO(0, ME_NOMEM); } return(i); }
mBOOL MPluginList::refresh(PLUG_LOADTIME now) { int i, ndone=0, nkept=0, nloaded=0, nunloaded=0, nreloaded=0, ndelayed=0; MPlugin *iplug; if (!ini_refresh()) { META_ERROR("dll: Problem reloading plugins.ini: %s", inifile); // meta_errno should be already set in ini_refresh() return(mFALSE); } META_LOG("dll: Updating plugins..."); for(i=0; i < endlist; i++) { iplug=&plist[i]; if(iplug->status < PL_VALID) continue; switch(iplug->action) { case PA_KEEP: META_DEBUG(1, ("Keeping plugin '%s'", iplug->desc)); iplug->action=PA_NONE; nkept++; break; case PA_LOAD: META_DEBUG(1, ("Loading plugin '%s'", iplug->desc)); if(iplug->load(now)) nloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_RELOAD: META_DEBUG(1, ("Reloading plugin '%s'", iplug->desc)); if(iplug->reload(now, PNL_FILE_NEWER)) nreloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_NONE: // If previously loaded from ini, but apparently removed from new ini. if(iplug->source==PS_INI && iplug->status >= PL_RUNNING) { META_DEBUG(1, ("Unloading plugin '%s'", iplug->desc)); iplug->action=PA_UNLOAD; if(iplug->unload(now, PNL_INI_DELETED, PNL_INI_DELETED)) nunloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; } break; case PA_ATTACH: // Previously requested attach, but was delayed? META_DEBUG(1, ("Retrying attach plugin '%s'", iplug->desc)); if(iplug->retry(now, PNL_DELAYED)) nloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_UNLOAD: // Previously requested unload, but was delayed? META_DEBUG(1, ("Retrying unload plugin '%s'", iplug->desc)); if(iplug->retry(now, PNL_DELAYED)) nunloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_NULL: META_ERROR("dll: Unexpected action for plugin '%s': '%s'", iplug->desc, iplug->str_action()); break; default: META_ERROR("dll: Unrecognized action for plugin '%s': '%s'", iplug->desc, iplug->str_action()); break; } ndone++; } META_LOG("dll: Finished updating %d plugins; kept %d, loaded %d, unloaded %d, reloaded %d, delayed %d", ndone, nkept, nloaded, nunloaded, nreloaded, ndelayed); return(mTRUE); }
mBOOL MHookList::enqueue(MFuncQueue *mfq, hook_t htype, event_args_t *evargs, const char *logline) { int i; META_ERRNO ret=ME_NOERROR; if(!mfq || !evargs || !logline) RETURN_ERRNO(mFALSE, ME_ARGUMENT); if(htype==H_NONE) RETURN_ERRNO(mFALSE, ME_BADREQ); MXlock(); for(i=0; i < endlist && hlist[i] && hlist[i]->type==htype; i++) { MHook *ihook; ihook=hlist[i]; switch(ihook->type) { case H_EVENT: if(ihook->event == evargs->evtype) ihook->enqueue(mfq, evargs, logline); break; case H_TRIGGER: if(strmatch(ihook->match, evargs->action)) ihook->enqueue(mfq, evargs, logline); break; case H_STRING: if(strstr(logline, ihook->match)) ihook->enqueue(mfq, evargs, logline); break; case H_REGEX: META_LOG("Not currently handling REGEX hooks..."); ret=ME_BADREQ; break; default: META_ERROR("Invalid hook type (%d) encountered", ihook->type); ret=ME_BADREQ; break; } } MXunlock(); if(ret==ME_NOERROR) return(mTRUE); else RETURN_ERRNO(mFALSE, ret); }
mBOOL MPluginList::ini_refresh() { FILE *fp; char line[MAX_STRBUF_LEN]; int n, ln; MPlugin pl_temp; MPlugin *pl_found, *pl_added; fp=fopen(inifile, "r"); if(!fp) { META_ERROR("ini: Unable to open plugins file '%s': %s", inifile, strerror(errno)); RETURN_ERRNO(mFALSE, ME_NOFILE); } META_LOG("ini: Begin re-reading plugins list: %s", inifile); for(n=0, ln=1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) { // Remove line terminations. char *cp; if((cp=strrchr(line, '\r'))) *cp='\0'; if((cp=strrchr(line, '\n'))) *cp='\0'; // Parse into a temp plugin memset(&pl_temp, 0, sizeof(pl_temp)); if(!pl_temp.ini_parseline(line)) { if(meta_errno==ME_FORMAT) META_ERROR("ini: Skipping malformed line %d of %s", ln, inifile); continue; } // Try to find plugin with this pathname in the current list of // plugins. if(!(pl_found=find(pl_temp.pathname))) { // Check for a matching platform with higher platform specifics // level. if(NULL != (pl_found=find_match(&pl_temp))) { if(pl_found->pfspecific >= pl_temp.pfspecific) { META_DEBUG(1, ("ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, inifile, pl_found->pfspecific, pl_temp.pfspecific)); continue; } if(PA_LOAD == pl_found->action) { META_DEBUG(1, ("ini: Plugin in line %d overrides loading of plugin with lower platform specific level %d, ours %d", ln, pl_found->pfspecific, pl_temp.pfspecific)); int _index = pl_found->index; memset(pl_found, 0, sizeof(MPlugin)); pl_found->index = _index; } else { META_DEBUG(1, ("ini: Plugin in line %d should override existing plugin with lower platform specific level %d, ours %d. Unable to comply.", ln, pl_found->pfspecific, pl_temp.pfspecific)); continue; } } // new plugin; add to list if((pl_added=add(&pl_temp))) { // try to load this plugin at the next opportunity pl_added->action=PA_LOAD; } else // error details logged in add() continue; } else { // This plugin is already in the current list of plugins. // Pathname already matches. Recopy desc, if specified in // plugins.ini. if(pl_temp.desc[0] != '<') STRNCPY(pl_found->desc, pl_temp.desc, sizeof(pl_found->desc)); // Check the file to see if it looks like it's been modified // since we last loaded it. if(!pl_found->newer_file()) { if(meta_errno==ME_NOFILE) { META_ERROR("ini: Skipping plugin, couldn't stat file '%s': %s", pl_found->pathname, strerror(errno)); continue; } else { // File hasn't been updated. // Keep plugin (don't let refresh() unload it). pl_found->action=PA_KEEP; } } // Newer file on disk. else if(pl_found->status >= PL_OPENED) { META_DEBUG(2, ("ini: Plugin '%s' has newer file on disk", pl_found->desc)); pl_found->action=PA_RELOAD; } else META_ERROR("ini: Plugin '%s' has newer file, but unexpected status (%s)", pl_found->desc, pl_found->str_status()); } if(NULL != pl_found) { META_LOG("ini: Read plugin config for: %s", pl_found->desc); } else { META_LOG("ini: Read plugin config for: %s", pl_temp.desc); } n++; } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins", inifile, n); fclose(fp); if(!n) { META_ERROR("ini: Warning; no plugins found to load?"); } return(mTRUE); }
mBOOL MPluginList::ini_startup() { FILE *fp; char line[MAX_STRBUF_LEN]; int n, ln; MPlugin *pmatch; if(!valid_gamedir_file(inifile)) { META_ERROR("ini: Metamod plugins file empty or missing: %s", inifile); RETURN_ERRNO(mFALSE, ME_NOFILE); } full_gamedir_path(inifile, inifile); fp=fopen(inifile, "r"); if(!fp) { META_ERROR("ini: Unable to open plugins file '%s': %s", inifile, strerror(errno)); RETURN_ERRNO(mFALSE, ME_NOFILE); } META_LOG("ini: Begin reading plugins list: %s", inifile); for(n=0, ln=1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) { // Remove line terminations. char *cp; if((cp=strrchr(line, '\r'))) *cp='\0'; if((cp=strrchr(line, '\n'))) *cp='\0'; // Parse directly into next entry in array if(!plist[n].ini_parseline(line)) { if(meta_errno==ME_FORMAT) META_ERROR("ini: Skipping malformed line %d of %s", ln, inifile); continue; } // Check for a duplicate - an existing entry with this pathname. if(find(plist[n].pathname)) { // Should we check platform specific level here? META_INFO("ini: Skipping duplicate plugin, line %d of %s: %s", ln, inifile, plist[n].pathname); continue; } // Check for a matching platform with different platform specifics // level. if(NULL != (pmatch=find_match(&plist[n]))) { if(pmatch->pfspecific >= plist[n].pfspecific) { META_DEBUG(1, ("ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, inifile, pmatch->pfspecific, plist[n].pfspecific)); continue; } META_DEBUG(1, ("ini: Plugin in line %d overrides existing plugin with lower platform specific level %d, ours %d", ln, pmatch->pfspecific, plist[n].pfspecific)); int _index = pmatch->index; memset(pmatch, 0, sizeof(MPlugin)); pmatch->index = _index; } plist[n].action=PA_LOAD; META_LOG("ini: Read plugin config for: %s", plist[n].desc); n++; endlist=n; // mark end of list } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins to load", inifile, n); fclose(fp); if(!n) { META_ERROR("ini: Warning; no plugins found to load?"); } return(mTRUE); }