2016-07-26 03:22:47 +03:00
# include "precompiled.h"
2016-07-04 09:07:29 +03:00
// Constructor
2016-07-26 03:22:47 +03:00
MPluginList : : MPluginList ( const char * ifile )
2016-07-04 09:07:29 +03:00
: size ( MAX_PLUGINS ) , endlist ( 0 )
{
// store filename of ini file
2016-07-26 03:22:47 +03:00
Q_strncpy ( inifile , ifile , sizeof ( inifile ) - 1 ) ;
inifile [ sizeof ( inifile ) - 1 ] = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// initialize array
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < size ; i + + )
{
2016-07-04 09:07:29 +03:00
//reset to empty
2016-07-26 03:22:47 +03:00
plist [ i ] . index = i + 1 ;
2016-07-04 09:07:29 +03:00
reset_plugin ( & plist [ i ] ) ;
}
2016-07-26 03:22:47 +03:00
endlist = 0 ;
2016-07-04 09:07:29 +03:00
}
// Resets plugin to empty
2016-07-26 03:22:47 +03:00
void MPluginList : : reset_plugin ( MPlugin * pl_find )
{
2016-07-04 09:07:29 +03:00
//calculate index
2016-07-26 03:22:47 +03:00
int i = pl_find - & plist [ 0 ] ;
2016-07-04 09:07:29 +03:00
//free any pointers first
pl_find - > free_api_pointers ( ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
//set zero
2016-07-04 10:11:20 +03:00
Q_memset ( pl_find , 0 , sizeof ( * pl_find ) ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
pl_find - > index = i + 1 ; // 1-based
}
// Find a plugin based on the plugin index #.
// meta_errno values:
// - ME_ARGUMENT invalid pindex
// - ME_NOTFOUND couldn't find a matching plugin
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find ( int pindex )
{
if ( pindex < = 0 )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
MPlugin * pfound = & plist [ pindex - 1 ] ;
if ( pfound - > status < PL_VALID )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
else
2016-07-26 03:22:47 +03:00
return pfound ;
2016-07-04 09:07:29 +03:00
}
// Find a plugin based on the plugin handle.
// meta_errno values:
// - ME_ARGUMENT invalid pindex
// - ME_NOTFOUND couldn't find a matching plugin
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find ( DLHANDLE handle )
{
if ( ! handle )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . handle = = handle )
return & plist [ i ] ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
// Clear source_plugin_index on all matching plugins
2016-07-26 03:22:47 +03:00
void MPluginList : : clear_source_plugin_index ( int source_index )
{
if ( source_index < = 0 )
2016-07-04 09:07:29 +03:00
return ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . source_plugin_index = = source_index )
2016-07-04 09:07:29 +03:00
plist [ i ] . source_plugin_index = - 1 ;
}
}
// Find if any plugin has been loaded by plugin 'source_index'
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : found_child_plugins ( int source_index )
{
if ( source_index < = 0 )
return mFALSE ;
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . source_plugin_index = = source_index )
return mTRUE ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
// Try make endlist lower (called after plugin unload)
2016-07-26 03:22:47 +03:00
void MPluginList : : trim_list ( )
{
if ( endlist < = 0 )
2016-07-04 09:07:29 +03:00
return ;
2016-07-26 03:22:47 +03:00
int n = 0 ;
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status = = PL_EMPTY )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
n = i + 1 ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
if ( n < endlist )
endlist = n ;
2016-07-04 09:07:29 +03:00
}
// Find a plugin with the given plid.
// meta_errno values:
// - ME_ARGUMENT null plid_t
// - ME_NOTFOUND couldn't find a matching plugin
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find ( plid_t id )
{
if ( ! id )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . info = = id )
return & plist [ i ] ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
// Find a plugin with the given pathname.
// meta_errno values:
// - ME_ARGUMENT null path
// - ME_NOTFOUND couldn't find a matching plugin
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find ( const char * findpath )
{
if ( ! findpath )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_DEBUG ( 8 , ( " Looking for loaded plugin with dlfnamepath: %s " , findpath ) ) ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 9 , ( " Looking at: plugin %s loadedpath: %s " , plist [ i ] . file , plist [ i ] . pathname ) ) ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( ! Q_strcmp ( plist [ i ] . pathname , findpath ) )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 8 , ( " Found loaded plugin %s " , plist [ i ] . file ) ) ;
2016-07-26 03:22:47 +03:00
return & plist [ i ] ;
2016-07-04 09:07:29 +03:00
}
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_DEBUG ( 8 , ( " No loaded plugin found with path: %s " , findpath ) ) ;
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
// Find a plugin that uses the given memory location.
// meta_errno values:
// - ME_ARGUMENT null memptr
// - ME_NOTFOUND couldn't find a matching plugin
// - errno's from DLFNAME()
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find_memloc ( void * memptr )
{
# ifdef _WIN32
DLHANDLE dlhandle ;
2016-07-04 09:07:29 +03:00
2016-07-26 03:22:47 +03:00
if ( ! memptr )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
if ( ! ( dlhandle = get_module_handle_of_memptr ( memptr ) ) )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 8 , ( " DLFNAME failed to find memloc %d " , memptr ) ) ;
2016-07-26 03:22:47 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
return find ( dlhandle ) ;
2016-07-04 09:07:29 +03:00
# else
2016-07-26 03:22:47 +03:00
const char * dlfile ;
if ( ! memptr )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
if ( ! ( dlfile = DLFNAME ( memptr ) ) )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 8 , ( " DLFNAME failed to find memloc %d " , memptr ) ) ;
2016-07-26 03:22:47 +03:00
// meta_errno should be already set in DLFNAME
return NULL ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
return find ( dlfile ) ;
2016-07-04 09:07:29 +03:00
# endif
}
2016-07-26 03:22:47 +03:00
// Find a plugin with non-ambiguous prefix string matching desc, file,
2016-07-04 09:07:29 +03:00
// name, or logtag.
// meta_errno values:
// - ME_ARGUMENT null prefix
// - ME_NOTFOUND couldn't find a matching plugin
// - ME_NOTUNIQ found multiple matches; no unique match
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find_match ( const char * prefix )
{
int len ;
2016-07-04 09:07:29 +03:00
MPlugin * iplug , * pfound ;
char buf [ NAME_MAX ] ;
2016-07-26 03:22:47 +03:00
if ( ! prefix )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
pfound = nullptr ;
len = Q_strlen ( prefix ) ;
2016-07-04 09:07:29 +03:00
safevoid_snprintf ( buf , sizeof ( buf ) , " mm_%s " , prefix ) ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
iplug = & plist [ i ] ;
if ( iplug - > status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( iplug - > info & & Q_strnicmp ( iplug - > info - > name , prefix , len ) = = 0 )
{
if ( pfound )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTUNIQ ) ;
2016-07-26 03:22:47 +03:00
pfound = iplug ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
else if ( Q_strnicmp ( iplug - > desc , prefix , len ) = = 0 )
{
if ( pfound )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTUNIQ ) ;
2016-07-26 03:22:47 +03:00
pfound = iplug ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
else if ( Q_strnicmp ( iplug - > file , prefix , len ) = = 0 )
{
if ( pfound )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTUNIQ ) ;
2016-07-26 03:22:47 +03:00
pfound = iplug ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
else if ( Q_strnicmp ( iplug - > file , buf , Q_strlen ( buf ) ) = = 0 )
{
if ( pfound )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTUNIQ ) ;
2016-07-26 03:22:47 +03:00
pfound = iplug ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
else if ( iplug - > info & & Q_strnicmp ( iplug - > info - > logtag , prefix , len ) = = 0 )
2016-07-04 09:07:29 +03:00
{
2016-07-26 03:22:47 +03:00
if ( pfound )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTUNIQ ) ;
2016-07-26 03:22:47 +03:00
pfound = iplug ;
2016-07-04 09:07:29 +03:00
continue ;
}
}
2016-07-26 03:22:47 +03:00
if ( pfound )
return pfound ;
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
2016-07-26 03:22:47 +03:00
// Find a plugin with same file, logtag, desc or significant
2016-07-04 09:07:29 +03:00
// prefix of file. Uses the platform_match() method of MPlugin.
// meta_errno values:
// - ME_ARGUMENT null prefix
// - ME_NOTFOUND couldn't find a matching plugin
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : find_match ( MPlugin * pmatch )
{
2016-07-04 09:07:29 +03:00
MPlugin * iplug , * pfound ;
2016-07-26 03:22:47 +03:00
if ( ! pmatch )
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_ARGUMENT ) ;
2016-07-26 03:22:47 +03:00
pfound = nullptr ;
for ( int i = 0 ; i < endlist ; i + + )
{
iplug = & plist [ i ] ;
if ( pmatch - > platform_match ( iplug ) )
{
2016-07-04 09:07:29 +03:00
pfound = iplug ;
break ;
}
}
2016-07-26 03:22:47 +03:00
if ( pfound )
return pfound ;
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
// Add a plugin to the list.
// meta_errno values:
// - ME_MAXREACHED reached max plugins
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : add ( MPlugin * padd )
{
2016-07-04 09:07:29 +03:00
int i ;
MPlugin * iplug ;
// Find either:
// - a slot in the list that's not being used
// - the end of the list
2016-07-26 03:22:47 +03:00
for ( i = 0 ; i < endlist & & plist [ i ] . status ! = PL_EMPTY ; i + + ) { }
2016-07-04 09:07:29 +03:00
// couldn't find a slot to use
2016-07-26 03:22:47 +03:00
if ( i = = size )
{
META_WARNING ( " Couldn't add plugin '%s' to list; reached max plugins (%d) " , padd - > file , i ) ;
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( NULL , ME_MAXREACHED ) ;
}
// if we found the end of the list, advance end marker
2016-07-26 03:22:47 +03:00
if ( i = = endlist )
2016-07-04 09:07:29 +03:00
endlist + + ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
iplug = & plist [ i ] ;
// copy filename into this free slot
2016-07-26 03:22:47 +03:00
Q_strncpy ( iplug - > filename , padd - > filename , sizeof ( iplug - > filename ) - 1 ) ;
iplug - > filename [ sizeof ( iplug - > filename ) - 1 ] = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// 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 ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// copy description
2016-07-26 03:22:47 +03:00
Q_strncpy ( iplug - > desc , padd - > desc , sizeof ( iplug - > desc ) - 1 ) ;
iplug - > desc [ sizeof ( iplug - > desc ) - 1 ] = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// copy pathname
2016-07-26 03:22:47 +03:00
Q_strncpy ( iplug - > pathname , padd - > pathname , sizeof ( iplug - > pathname ) - 1 ) ;
iplug - > pathname [ sizeof ( iplug - > pathname ) - 1 ] = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// copy source
2016-07-26 03:22:47 +03:00
iplug - > source = padd - > source ;
2016-07-04 09:07:29 +03:00
// copy loader-plugin
2016-07-26 03:22:47 +03:00
iplug - > source_plugin_index = padd - > source_plugin_index ;
2016-07-04 09:07:29 +03:00
// copy status
2016-07-26 03:22:47 +03:00
iplug - > status = padd - > status ;
2016-07-04 09:07:29 +03:00
2016-07-26 03:22:47 +03:00
return iplug ;
}
2016-07-04 09:07:29 +03:00
// Read plugins.ini at server startup.
// meta_errno values:
// - ME_NOFILE ini file missing or empty
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : ini_startup ( )
{
2016-07-04 09:07:29 +03:00
FILE * fp ;
char line [ MAX_STRBUF_LEN ] ;
int n , ln ;
MPlugin * pmatch ;
2016-07-26 03:22:47 +03:00
if ( ! valid_gamedir_file ( inifile ) )
{
2016-07-04 09:07:29 +03:00
META_WARNING ( " ini: Metamod plugins file empty or missing: %s " , inifile ) ;
RETURN_ERRNO ( mFALSE , ME_NOFILE ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
full_gamedir_path ( inifile , inifile ) ;
2016-07-26 03:22:47 +03:00
fp = fopen ( inifile , " r " ) ;
if ( ! fp )
{
META_WARNING ( " ini: Unable to open plugins file '%s': %s " , inifile , strerror ( errno ) ) ;
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( mFALSE , ME_NOFILE ) ;
}
META_LOG ( " ini: Begin reading plugins list: %s " , inifile ) ;
2016-07-26 03:22:47 +03:00
for ( n = 0 , ln = 1 ; ! feof ( fp ) & & fgets ( line , sizeof ( line ) , fp ) & & n < size ; ln + + )
{
2016-07-04 09:07:29 +03:00
// Remove line terminations.
char * cp ;
2016-07-26 03:22:47 +03:00
if ( ( cp = Q_strrchr ( line , ' \r ' ) ) )
* cp = ' \0 ' ;
if ( ( cp = Q_strrchr ( line , ' \n ' ) ) )
* cp = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// Parse directly into next entry in array
2016-07-26 03:22:47 +03:00
if ( ! plist [ n ] . ini_parseline ( line ) )
{
if ( meta_errno = = ME_FORMAT )
META_WARNING ( " ini: Skipping malformed line %d of %s " , ln , inifile ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// Check for a duplicate - an existing entry with this pathname.
2016-07-26 03:22:47 +03:00
if ( find ( plist [ n ] . pathname ) )
{
2016-07-04 09:07:29 +03:00
// Should we check platform specific level here?
2016-07-26 03:22:47 +03:00
META_INFO ( " ini: Skipping duplicate plugin, line %d of %s: %s " , ln , inifile , plist [ n ] . pathname ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// Check for a matching platform with different platform specifics
// level.
2016-07-26 03:22:47 +03:00
if ( ( 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 ) ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
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 ) ) ;
2016-07-04 09:07:29 +03:00
//reset to empty
reset_plugin ( pmatch ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
plist [ n ] . action = PA_LOAD ;
META_LOG ( " ini: Read plugin config for: %s " , plist [ n ] . desc ) ;
n + + ;
2016-07-26 03:22:47 +03:00
endlist = n ; // mark end of list
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
META_LOG ( " ini: Finished reading plugins list: %s; Found %d plugins to load " , inifile , n ) ;
2016-07-04 09:07:29 +03:00
fclose ( fp ) ;
2016-07-26 03:22:47 +03:00
if ( ! n )
{
2016-07-04 09:07:29 +03:00
META_WARNING ( " ini: Warning; no plugins found to load? " ) ;
}
2016-07-26 03:22:47 +03:00
return mTRUE ;
2016-07-04 09:07:29 +03:00
}
// Re-read plugins.ini looking for added/deleted/changed plugins.
// meta_errno values:
// - ME_NOFILE ini file missing or empty
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : ini_refresh ( )
{
2016-07-04 09:07:29 +03:00
FILE * fp ;
char line [ MAX_STRBUF_LEN ] ;
int n , ln ;
MPlugin pl_temp ;
MPlugin * pl_found , * pl_added ;
2016-07-26 03:22:47 +03:00
fp = fopen ( inifile , " r " ) ;
if ( ! fp )
{
META_WARNING ( " ini: Unable to open plugins file '%s': %s " , inifile , strerror ( errno ) ) ;
2016-07-04 09:07:29 +03:00
RETURN_ERRNO ( mFALSE , ME_NOFILE ) ;
}
META_LOG ( " ini: Begin re-reading plugins list: %s " , inifile ) ;
2016-07-26 03:22:47 +03:00
for ( n = 0 , ln = 1 ; ! feof ( fp ) & & fgets ( line , sizeof ( line ) , fp ) & & n < size ; ln + + )
2016-07-04 09:07:29 +03:00
{
// Remove line terminations.
char * cp ;
2016-07-26 03:22:47 +03:00
if ( ( cp = Q_strrchr ( line , ' \r ' ) ) )
* cp = ' \0 ' ;
if ( ( cp = Q_strrchr ( line , ' \n ' ) ) )
* cp = ' \0 ' ;
2016-07-04 09:07:29 +03:00
// Parse into a temp plugin
2016-07-04 10:11:20 +03:00
Q_memset ( & pl_temp , 0 , sizeof ( pl_temp ) ) ;
2016-07-26 03:22:47 +03:00
if ( ! pl_temp . ini_parseline ( line ) )
{
if ( meta_errno = = ME_FORMAT )
{
META_WARNING ( " ini: Skipping malformed line %d of %s " , ln , inifile ) ;
}
2016-07-04 09:07:29 +03:00
continue ;
}
// Try to find plugin with this pathname in the current list of
// plugins.
2016-07-26 03:22:47 +03:00
if ( ! ( pl_found = find ( pl_temp . pathname ) ) )
{
2016-07-04 09:07:29 +03:00
// Check for a matching platform with higher platform specifics
// level.
2016-07-26 03:22:47 +03:00
if ( ( 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 ) ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
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 ) ) ;
2016-07-04 09:07:29 +03:00
//reset to empty
reset_plugin ( pl_found ) ;
}
2016-07-26 03:22:47 +03:00
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 ) ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// new plugin; add to list
2016-07-26 03:22:47 +03:00
if ( ( pl_added = add ( & pl_temp ) ) )
{
2016-07-04 09:07:29 +03:00
// try to load this plugin at the next opportunity
2016-07-26 03:22:47 +03:00
pl_added - > action = PA_LOAD ;
2016-07-04 09:07:29 +03:00
}
else
// error details logged in add()
continue ;
}
2016-07-26 03:22:47 +03:00
else
{
2016-07-04 09:07:29 +03:00
// This plugin is already in the current list of plugins.
// Pathname already matches. Recopy desc, if specified in
// plugins.ini.
2016-07-26 03:22:47 +03:00
if ( pl_temp . desc [ 0 ] ! = ' < ' )
{
Q_strncpy ( pl_found - > desc , pl_temp . desc , sizeof ( pl_found - > desc ) - 1 ) ;
pl_found - > desc [ sizeof ( pl_found - > desc ) - 1 ] = ' \0 ' ;
}
2016-07-04 09:07:29 +03:00
// Check the file to see if it looks like it's been modified
// since we last loaded it.
2016-07-26 03:22:47 +03:00
if ( ! pl_found - > newer_file ( ) )
{
if ( meta_errno = = ME_NOFILE )
{
META_WARNING ( " ini: Skipping plugin, couldn't stat file '%s': %s " , pl_found - > pathname , strerror ( errno ) ) ;
2016-07-04 09:07:29 +03:00
continue ;
}
2016-07-26 03:22:47 +03:00
else
{
2016-07-04 09:07:29 +03:00
// File hasn't been updated.
// Keep plugin (don't let refresh() unload it).
2016-07-26 03:22:47 +03:00
pl_found - > action = PA_KEEP ;
2016-07-04 09:07:29 +03:00
}
}
// Newer file on disk.
2016-07-26 03:22:47 +03:00
else if ( pl_found - > status > = PL_OPENED )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 2 , ( " ini: Plugin '%s' has newer file on disk " , pl_found - > desc ) ) ;
2016-07-26 03:22:47 +03:00
pl_found - > action = PA_RELOAD ;
2016-07-04 09:07:29 +03:00
}
else
2016-07-26 03:22:47 +03:00
META_WARNING ( " ini: Plugin '%s' has newer file, but unexpected status (%s) " , pl_found - > desc , pl_found - > str_status ( ) ) ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
if ( pl_found )
{
2016-07-04 09:07:29 +03:00
META_LOG ( " ini: Read plugin config for: %s " , pl_found - > desc ) ;
}
2016-07-26 03:22:47 +03:00
else
{
2016-07-04 09:07:29 +03:00
META_LOG ( " ini: Read plugin config for: %s " , pl_temp . desc ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
n + + ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_LOG ( " ini: Finished reading plugins list: %s; Found %d plugins " , inifile , n ) ;
fclose ( fp ) ;
2016-07-26 03:22:47 +03:00
if ( ! n )
{
2016-07-04 09:07:29 +03:00
META_WARNING ( " ini: Warning; no plugins found to load? " ) ;
}
2016-07-26 03:22:47 +03:00
return mTRUE ;
2016-07-04 09:07:29 +03:00
}
// Load a plugin from plugin request.
// meta_errno values:
// - errno's from resolve()
// - ME_ALREADY this plugin already loaded
// - errno's from add()
// - errno's from load()
2016-07-26 03:22:47 +03:00
MPlugin * MPluginList : : plugin_addload ( plid_t plid , const char * fname , PLUG_LOADTIME now )
{
2016-07-04 09:07:29 +03:00
MPlugin pl_temp ;
MPlugin * pl_found , * pl_added , * pl_loader ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// Find loader plugin
2016-07-26 03:22:47 +03:00
if ( ! ( pl_loader = find ( plid ) ) )
{
2016-07-04 09:07:29 +03:00
// Couldn't find a matching file on disk
META_DEBUG ( 1 , ( " Couldn't find plugin that gave this loading request! " ) ) ;
// meta_errno should be already set in resolve()
RETURN_ERRNO ( NULL , ME_BADREQ ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 10:11:20 +03:00
Q_memset ( & pl_temp , 0 , sizeof ( pl_temp ) ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// copy filename
2016-07-26 03:22:47 +03:00
if ( ! pl_temp . plugin_parseline ( fname , pl_loader - > index ) )
{
2016-07-04 09:07:29 +03:00
// parse_plugin_load doesn't return mFALSE.
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// resolve given path into a file; accepts various "shortcut"
// pathnames.
2016-07-26 03:22:47 +03:00
if ( pl_temp . resolve ( ) ! = mTRUE )
{
2016-07-04 09:07:29 +03:00
// Couldn't find a matching file on disk
META_DEBUG ( 1 , ( " Couldn't resolve given path into a file: %s " , pl_temp . file ) ) ;
// meta_errno should be already set in resolve()
RETURN_ERRNO ( NULL , ME_NOTFOUND ) ;
}
// Try to find plugin with this pathname in the current list of
// plugins.
2016-07-26 03:22:47 +03:00
if ( ( pl_found = find ( pl_temp . pathname ) ) )
{
2016-07-04 09:07:29 +03:00
// Already in list
2016-07-26 03:22:47 +03:00
META_DEBUG ( 1 , ( " Plugin '%s' already in current list; file=%s desc='%s' " ,
2016-07-04 09:07:29 +03:00
pl_temp . file , pl_found - > file , pl_found - > desc ) ) ;
RETURN_ERRNO ( NULL , ME_ALREADY ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// new plugin; add to list
2016-07-26 03:22:47 +03:00
if ( ! ( pl_added = add ( & pl_temp ) ) )
{
2016-07-04 09:07:29 +03:00
META_DEBUG ( 1 , ( " Couldn't add plugin '%s' to list; see log " , pl_temp . desc ) ) ;
// meta_errno should be already set in add()
2016-07-26 03:22:47 +03:00
return NULL ;
2016-07-04 09:07:29 +03:00
}
// try to load new plugin (setting 'must load now')
2016-07-26 03:22:47 +03:00
pl_added - > action = PA_LOAD ;
if ( ! pl_added - > load ( now ) )
{
2016-07-04 09:07:29 +03:00
// load failed
2016-07-26 03:22:47 +03:00
if ( meta_errno = = ME_NOTALLOWED | | meta_errno = = ME_DELAYED )
{
META_DEBUG ( 1 , ( " Plugin '%s' couldn't attach; only allowed %s " ,
2016-07-04 09:07:29 +03:00
pl_added - > desc , pl_added - > str_loadable ( SL_ALLOWED ) ) ) ;
pl_added - > clear ( ) ;
}
2016-07-26 03:22:47 +03:00
else if ( pl_added - > status = = PL_OPENED )
2016-07-04 09:07:29 +03:00
META_DEBUG ( 1 , ( " Opened plugin '%s', but failed to attach; see log " , pl_added - > desc ) ) ;
else
META_DEBUG ( 1 , ( " Couldn't load plugin '%s'; see log " , pl_added - > desc ) ) ;
// meta_errno should be already set in load()
2016-07-26 03:22:47 +03:00
return NULL ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_DEBUG ( 1 , ( " Loaded plugin '%s' successfully " , pl_added - > desc ) ) ;
meta_errno = ME_NOERROR ;
2016-07-26 03:22:47 +03:00
return pl_added ;
2016-07-04 09:07:29 +03:00
}
// Load a plugin from a console command.
// meta_errno values:
// - errno's from cmd_parseline()
// - errno's from resolve()
// - ME_ALREADY this plugin already loaded
// - errno's from add()
// - errno's from load()
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : cmd_addload ( const char * args )
{
2016-07-04 09:07:29 +03:00
MPlugin pl_temp ;
MPlugin * pl_found , * pl_added ;
2016-07-26 03:22:47 +03:00
2016-07-04 10:11:20 +03:00
Q_memset ( & pl_temp , 0 , sizeof ( pl_temp ) ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
// XXX move back to comands_meta ?
// parse into a temp plugin
2016-07-26 03:22:47 +03:00
if ( pl_temp . cmd_parseline ( args ) ! = mTRUE )
{
2016-07-04 09:07:29 +03:00
META_CONS ( " Couldn't parse 'meta load' arguments: %s " , args ) ;
// meta_errno should be already set in cmd_parseline()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
// resolve given path into a file; accepts various "shortcut"
// pathnames.
2016-07-26 03:22:47 +03:00
if ( pl_temp . resolve ( ) ! = mTRUE )
{
2016-07-04 09:07:29 +03:00
// Couldn't find a matching file on disk
2016-07-26 03:22:47 +03:00
META_CONS ( " Couldn't resolve given path into a file: %s " ,
2016-07-04 09:07:29 +03:00
pl_temp . file ) ;
// meta_errno should be already set in resolve()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
// Try to find plugin with this pathname in the current list of
// plugins.
2016-07-26 03:22:47 +03:00
if ( ( pl_found = find ( pl_temp . pathname ) ) )
{
2016-07-04 09:07:29 +03:00
// Already in list
2016-07-26 03:22:47 +03:00
META_CONS ( " Plugin '%s' already in current list; file=%s desc='%s' " ,
2016-07-04 09:07:29 +03:00
pl_temp . file , pl_found - > file , pl_found - > desc ) ;
RETURN_ERRNO ( mFALSE , ME_ALREADY ) ;
}
// new plugin; add to list
2016-07-26 03:22:47 +03:00
if ( ! ( pl_added = add ( & pl_temp ) ) )
{
2016-07-04 09:07:29 +03:00
META_CONS ( " Couldn't add plugin '%s' to list; see log " , pl_temp . desc ) ;
// meta_errno should be already set in add()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
// try to load new plugin
pl_added - > action = PA_LOAD ;
2016-07-26 03:22:47 +03:00
if ( ! pl_added - > load ( PT_ANYTIME ) )
{
2016-07-04 09:07:29 +03:00
// load failed
2016-07-26 03:22:47 +03:00
if ( meta_errno = = ME_DELAYED )
META_CONS ( " Loaded plugin '%s', but will wait to become active, %s " , pl_added - > desc , pl_added - > str_loadable ( SL_ALLOWED ) ) ;
else if ( meta_errno = = ME_NOTALLOWED )
{
META_CONS ( " Plugin '%s' couldn't attach; only allowed %s " , pl_added - > desc , pl_added - > str_loadable ( SL_ALLOWED ) ) ;
2016-07-04 09:07:29 +03:00
pl_added - > clear ( ) ;
}
2016-07-26 03:22:47 +03:00
else if ( pl_added - > status = = PL_OPENED )
2016-07-04 09:07:29 +03:00
META_CONS ( " Opened plugin '%s', but failed to attach; see log " , pl_added - > desc ) ;
else
META_CONS ( " Couldn't load plugin '%s'; see log " , pl_added - > desc ) ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
show ( ) ;
// meta_errno should be already set in load()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_CONS ( " Loaded plugin '%s' successfully " , pl_added - > desc ) ;
show ( ) ;
2016-07-26 03:22:47 +03:00
return mTRUE ;
2016-07-04 09:07:29 +03:00
}
// Load plugins at startup.
// meta_errno values:
// - errno's from ini_startup()
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : load ( )
{
if ( ! ini_startup ( ) )
{
2016-07-04 09:07:29 +03:00
META_WARNING ( " Problem loading plugins.ini: %s " , inifile ) ;
// meta_errno should be already set in ini_startup()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
META_LOG ( " dll: Loading plugins... " ) ;
2016-07-26 03:22:47 +03:00
int n = 0 ;
for ( int i = 0 ; i < endlist ; i + + )
{
if ( plist [ i ] . status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( plist [ i ] . load ( PT_STARTUP ) = = mTRUE )
2016-07-04 09:07:29 +03:00
n + + ;
else
// all plugins should be loadable at startup...
META_WARNING ( " dll: Failed to load plugin '%s' " , plist [ i ] . file ) ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_LOG ( " dll: Finished loading %d plugins " , n ) ;
2016-07-26 03:22:47 +03:00
return mTRUE ;
2016-07-04 09:07:29 +03:00
}
// Update list of loaded plugins from ini file, and load any new/changed plugins.
// meta_errno values:
// - errno's from ini_refresh()
2016-07-26 03:22:47 +03:00
mBOOL MPluginList : : refresh ( PLUG_LOADTIME now )
{
int i , ndone = 0 , nkept = 0 , nloaded = 0 , nunloaded = 0 , nreloaded = 0 , ndelayed = 0 ;
2016-07-04 09:07:29 +03:00
MPlugin * iplug ;
2016-07-26 03:22:47 +03:00
if ( ! ini_refresh ( ) )
{
2016-07-04 09:07:29 +03:00
META_WARNING ( " dll: Problem reloading plugins.ini: %s " , inifile ) ;
// meta_errno should be already set in ini_refresh()
2016-07-26 03:22:47 +03:00
return mFALSE ;
2016-07-04 09:07:29 +03:00
}
META_LOG ( " dll: Updating plugins... " ) ;
2016-07-26 03:22:47 +03:00
for ( i = 0 ; i < endlist ; i + + )
{
iplug = & plist [ i ] ;
if ( iplug - > status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
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 ) )
2016-07-04 09:07:29 +03:00
nunloaded + + ;
2016-07-26 03:22:47 +03:00
else if ( meta_errno = = ME_DELAYED )
2016-07-04 09:07:29 +03:00
ndelayed + + ;
2016-07-26 03:22:47 +03:00
}
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_WARNING ( " dll: Unexpected action for plugin '%s': '%s' " , iplug - > desc , iplug - > str_action ( ) ) ;
break ;
default :
META_WARNING ( " dll: Unrecognized action for plugin '%s': '%s' " , iplug - > desc , iplug - > str_action ( ) ) ;
break ;
2016-07-04 09:07:29 +03:00
}
ndone + + ;
}
2016-07-26 03:22:47 +03:00
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 ;
2016-07-04 09:07:29 +03:00
}
// Re-enable any plugins currently paused.
// meta_errno values:
// - none
2016-07-26 03:22:47 +03:00
void MPluginList : : unpause_all ( )
{
2016-07-04 09:07:29 +03:00
MPlugin * iplug ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
iplug = & plist [ i ] ;
if ( iplug - > status = = PL_PAUSED )
2016-07-04 09:07:29 +03:00
iplug - > unpause ( ) ;
}
}
// Retry any pending actions on plugins, for instance load/unload delayed
// until changelevel.
// meta_errno values:
// - none
2016-07-26 03:22:47 +03:00
void MPluginList : : retry_all ( PLUG_LOADTIME now )
{
2016-07-04 09:07:29 +03:00
MPlugin * iplug ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
iplug = & plist [ i ] ;
if ( iplug - > action ! = PA_NONE )
2016-07-04 09:07:29 +03:00
iplug - > retry ( now , PNL_DELAYED ) ;
}
}
// List plugins and information about them in a formatted table.
// meta_errno values:
// - none
2016-07-26 03:22:47 +03:00
void MPluginList : : show ( int source_index )
{
int n = 0 , r = 0 ;
2016-07-04 09:07:29 +03:00
MPlugin * pl ;
2016-07-26 03:22:47 +03:00
char desc [ 15 + 1 ] , file [ 16 + 1 ] , vers [ 7 + 1 ] ; // plus 1 for term null
if ( source_index < = 0 )
2016-07-04 09:07:29 +03:00
META_CONS ( " Currently loaded plugins: " ) ;
else
META_CONS ( " Child plugins: " ) ;
2016-07-26 03:22:47 +03:00
META_CONS ( " %*s %-*s %-4s %-4s %-*s v%-*s %-*s %-5s %-5s " , WIDTH_MAX_PLUGINS , " " , sizeof ( desc ) - 1 , " description " , " stat " , " pend " ,
sizeof ( file ) - 1 , " file " , sizeof ( vers ) - 1 , " ers " , 2 + WIDTH_MAX_PLUGINS , " src " , " load " , " unlod " ) ;
for ( int i = 0 ; i < endlist ; i + + )
{
pl = & plist [ i ] ;
if ( pl - > status < PL_VALID )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
if ( source_index > 0 & & pl - > source_plugin_index ! = source_index )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
Q_strncpy ( desc , pl - > desc , sizeof ( desc ) - 1 ) ;
desc [ sizeof ( desc ) - 1 ] = ' \0 ' ;
Q_strncpy ( file , pl - > file , sizeof ( file ) - 1 ) ;
file [ sizeof ( file ) - 1 ] = ' \0 ' ;
if ( pl - > info & & pl - > info - > version )
{
Q_strncpy ( vers , pl - > info - > version , sizeof ( vers ) - 1 ) ;
vers [ sizeof ( vers ) - 1 ] = ' \0 ' ;
}
else
{
Q_strncpy ( vers , " - " , sizeof ( vers ) - 1 ) ;
vers [ sizeof ( vers ) - 1 ] = ' \0 ' ;
}
META_CONS ( " [%*d] %-*s %-4s %-4s %-*s v%-*s %-*s %-5s %-5s " , WIDTH_MAX_PLUGINS , pl - > index ,
sizeof ( desc ) - 1 , desc , pl - > str_status ( ST_SHOW ) , pl - > str_action ( SA_SHOW ) , sizeof ( file ) - 1 , file , sizeof ( vers ) - 1 , vers ,
2 + WIDTH_MAX_PLUGINS , pl - > str_source ( SO_SHOW ) , pl - > str_loadable ( SL_SHOW ) , pl - > str_unloadable ( SL_SHOW ) ) ;
if ( pl - > status = = PL_RUNNING )
2016-07-04 09:07:29 +03:00
r + + ;
n + + ;
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_CONS ( " %d plugins, %d running " , n , r ) ;
}
// List plugins and information to Player/client entity. Differs from the
// "meta list" console command in that:
// - Shows only "running" plugins, skipping any failed or paused plugins.
2016-07-26 03:22:47 +03:00
// - Limited info about each plugin, mostly the "public" info (name, author,
2016-07-04 09:07:29 +03:00
// etc).
// meta_errno values:
// - none
2016-07-26 03:22:47 +03:00
void MPluginList : : show_client ( edict_t * pEntity )
{
int n = 0 ;
2016-07-04 09:07:29 +03:00
MPlugin * pl ;
META_CLIENT ( pEntity , " Currently running plugins: " ) ;
2016-07-26 03:22:47 +03:00
for ( int i = 0 ; i < endlist ; i + + )
{
pl = & plist [ i ] ;
if ( pl - > status ! = PL_RUNNING | | ! pl - > info )
2016-07-04 09:07:29 +03:00
continue ;
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
n + + ;
2016-07-26 03:22:47 +03:00
META_CLIENT ( pEntity , " [%3d] %s, v%s, %s, by %s, see %s " , n ,
pl - > info - > name ? pl - > info - > name : " <unknown> " ,
pl - > info - > version ? pl - > info - > version : " <?> " ,
pl - > info - > date ? pl - > info - > date : " <../../..> " ,
pl - > info - > author ? pl - > info - > author : " <unknown> " ,
pl - > info - > url ? pl - > info - > url : " <unknown> " ) ;
2016-07-04 09:07:29 +03:00
}
2016-07-26 03:22:47 +03:00
2016-07-04 09:07:29 +03:00
META_CLIENT ( pEntity , " %d plugins " , n ) ;
}