2009-04-30 07:36:57 +02:00
/*
* ============================================================================
*
2009-07-05 08:49:23 +02:00
* Zombie : Reloaded
2009-04-30 07:36:57 +02:00
*
2009-06-12 05:51:26 +02:00
* File : models . inc
* Type : Core
2009-11-17 08:47:39 +01:00
* Description : Model manager .
2009-06-12 05:51:26 +02:00
*
2013-01-12 08:47:36 +01:00
* Copyright ( C ) 2009 - 2013 Greyscale , Richard Helgeby
2009-06-12 05:51:26 +02:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http :// www . gnu . org / licenses />.
2009-04-30 07:36:57 +02:00
*
* ============================================================================
2008-10-04 22:59:11 +02:00
*/
2009-11-17 08:47:39 +01:00
/*
* Note : Data structures and constants defined in models . h . inc .
2009-04-30 07:36:57 +02:00
*/
2008-10-04 22:59:11 +02:00
2009-04-30 07:36:57 +02:00
/**
2009-11-17 08:47:39 +01:00
* Parsed model data .
2009-04-30 07:36:57 +02:00
*/
2009-11-17 08:47:39 +01:00
new ModelData [ MODELS_MAX ][ ModelAttributes ];
2008-10-04 22:59:11 +02:00
2009-04-30 07:36:57 +02:00
/**
2009-11-17 08:47:39 +01:00
* Number of valid models .
2009-04-30 07:36:57 +02:00
*/
2009-11-17 08:47:39 +01:00
new ModelCount ;
2009-04-30 07:36:57 +02:00
/**
2009-05-18 06:26:13 +02:00
* Prepare all model / download data .
2009-04-30 07:36:57 +02:00
*/
2009-05-18 06:26:13 +02:00
ModelsLoad ()
2009-04-30 07:36:57 +02:00
{
2009-11-17 08:47:39 +01:00
new Handle : kvModels = INVALID_HANDLE ;
2009-05-29 08:43:15 +02:00
// Register config file.
ConfigRegisterConfig ( File_Models , Structure_List , CONFIG_FILE_ALIAS_MODELS );
2009-04-30 07:36:57 +02:00
// Get models file path.
2009-11-17 08:47:39 +01:00
decl String : modelPath [ PLATFORM_MAX_PATH ];
new bool : exists = ConfigGetCvarFilePath ( CVAR_CONFIG_PATH_MODELS , modelPath );
2009-05-18 06:26:13 +02:00
2009-04-30 07:36:57 +02:00
// If file doesn't exist, then log and stop.
if ( ! exists )
2008-10-04 22:59:11 +02:00
{
2009-04-30 07:36:57 +02:00
// Log failure and stop plugin.
2009-11-17 08:47:39 +01:00
LogEvent ( false , LogType_Fatal , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Missing model list: \" %s \" " , modelPath );
2008-10-04 22:59:11 +02:00
}
2009-05-29 08:43:15 +02:00
// Set the path to the config file.
2009-11-17 08:47:39 +01:00
ConfigSetConfigPath ( File_Models , modelPath );
2008-10-04 22:59:11 +02:00
2009-11-17 08:47:39 +01:00
// Prepare key/value structure.
kvModels = CreateKeyValues ( CONFIG_FILE_ALIAS_MODELS );
2008-10-04 22:59:11 +02:00
2009-11-17 08:47:39 +01:00
// Log what models file that is loaded.
LogEvent ( false , LogType_Normal , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Loading models from file \" %s \" . " , modelPath );
2008-10-04 22:59:11 +02:00
2009-11-17 08:47:39 +01:00
// Load model data file.
FileToKeyValues ( kvModels , modelPath );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
// Try to find the first model.
KvRewind ( kvModels );
if ( ! KvGotoFirstSubKey ( kvModels ))
{
LogEvent ( false , LogType_Fatal , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Can't find any models in \" %s \" " , modelPath );
}
2008-10-04 22:59:11 +02:00
2009-11-17 08:47:39 +01:00
decl String : buffer [ 256 ];
decl String : name [ 64 ];
decl String : path [ PLATFORM_MAX_PATH ];
decl String : team [ 64 ];
decl String : access [ 64 ];
decl String : group [ 64 ];
2008-10-04 22:59:11 +02:00
2009-11-17 08:47:39 +01:00
ModelCount = 0 ;
new failedCount ;
new publicCount ;
2009-11-28 22:32:44 +01:00
new downloadCount ;
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
// Loop through all models and store attributes in ModelData array.
do
2008-10-04 22:59:11 +02:00
{
2009-11-17 08:47:39 +01:00
if ( ModelCount > MODELS_MAX )
{
// Maximum number of models reached. Log a warning and exit the loop.
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Warning: Maximum number of models reached (%d). Skipping other models. " , MODELS_MAX + 1 );
break ;
}
2009-07-17 03:34:56 +02:00
2009-11-17 08:47:39 +01:00
KvGetString ( kvModels , " name " , name , sizeof ( name ));
strcopy ( ModelData [ ModelCount ][ Model_Name ], 64 , name );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
KvGetString ( kvModels , " path " , path , sizeof ( path ));
strcopy ( ModelData [ ModelCount ][ Model_Path ], 64 , path );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
KvGetString ( kvModels , " team " , team , sizeof ( team ));
ModelData [ ModelCount ][ Model_Team ] = ModelsStringToTeam ( team );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
KvGetString ( kvModels , " access " , access , sizeof ( access ));
ModelData [ ModelCount ][ Model_Access ] = ModelsStringToAccess ( access );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
KvGetString ( kvModels , " group " , group , sizeof ( group ));
strcopy ( ModelData [ ModelCount ][ Model_Group ], 64 , group );
// Validate model attributes.
// Build path and check if model file exist.
strcopy ( buffer , sizeof ( buffer ), path );
StrCat ( buffer , sizeof ( buffer ), name );
StrCat ( buffer , sizeof ( buffer ), " .mdl " );
2013-04-16 02:43:29 +02:00
if ( ! FileExists ( buffer , true ))
2009-06-05 05:58:48 +02:00
{
2009-11-17 08:47:39 +01:00
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Warning: Invalid model name/path setting at index %d. File not found: \" %s \" . " , ModelCount + failedCount , buffer );
failedCount ++ ;
2009-06-05 05:58:48 +02:00
continue ;
}
2009-11-17 08:47:39 +01:00
// Validate team.
if ( ModelData [ ModelCount ][ Model_Team ] == ModelTeam_Invalid )
{
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Warning: Invalid model team setting at index %d: \" %s \" . " , ModelCount + failedCount , team );
failedCount ++ ;
continue ;
}
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
// Validate access.
if ( ModelData [ ModelCount ][ Model_Access ] == ModelAccess_Invalid )
2009-04-30 07:36:57 +02:00
{
2009-11-17 08:47:39 +01:00
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Warning: Invalid model access setting at index %d: \" %s \" . " , ModelCount + failedCount , access );
failedCount ++ ;
continue ;
}
else
{
2009-11-28 22:32:44 +01:00
// Increment public model counter for the current team.
2009-11-17 08:47:39 +01:00
if ( ModelData [ ModelCount ][ Model_Access ] == ModelAccess_Public )
{
publicCount ++ ;
}
}
// Validate group.
if ( ModelData [ ModelCount ][ Model_Access ] == ModelAccess_Group &&
FindAdmGroup ( group ) == INVALID_GROUP_ID )
{
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Warning: Invalid model group setting at index %d. Couldn't find SourceMod group \" %s \" . " , ModelCount + failedCount , group );
failedCount ++ ;
continue ;
}
// Open directory with model files.
new Handle : dir = OpenDirectory ( path );
// Check if failed.
if ( dir == INVALID_HANDLE )
{
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Error opening directory: %s " , dir );
continue ;
}
2009-11-28 22:32:44 +01:00
// Reset file counter for the current model.
downloadCount = 0 ;
2009-11-17 08:47:39 +01:00
new FileType : type ;
decl String : file [ 64 ];
decl String : fileShort [ 64 ];
// Search for model files with the specified name and add them to
// downloads table.
while ( ReadDirEntry ( dir , file , sizeof ( file ), type ))
{
// Skip if entry isn't a file.
2009-04-30 07:36:57 +02:00
if ( type != FileType_File )
{
continue ;
}
// Find break point index in the string to get model name.
2009-11-17 08:47:39 +01:00
// Add one to make space for null terminator.
new breakpoint = FindCharInString ( file , '.' ) + 1 ;
strcopy ( fileShort , breakpoint , file );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
// If this file doesn't match model name, then skip it.
if ( ! StrEqual ( name , fileShort , false ))
2009-04-30 07:36:57 +02:00
{
continue ;
}
// Format a full path string.
2009-11-17 08:47:39 +01:00
strcopy ( buffer , sizeof ( buffer ), path );
Format ( buffer , sizeof ( buffer ), " %s%s " , buffer , file );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
AddFileToDownloadsTable ( buffer );
2009-11-28 22:32:44 +01:00
downloadCount ++ ;
2009-04-30 07:36:57 +02:00
}
2009-11-17 08:47:39 +01:00
CloseHandle ( dir );
2009-10-26 01:42:15 +01:00
2009-11-28 22:32:44 +01:00
// Check if no model files were found.
if ( ! downloadCount )
{
LogEvent ( false , LogType_Error , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Couldn't find any model files for \" %s \" . Check name and path. " , name );
}
else
{
ModelCount ++ ;
}
2009-11-17 08:47:39 +01:00
} while ( KvGotoNextKey ( kvModels ));
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
CloseHandle ( kvModels );
2009-04-30 07:36:57 +02:00
2009-11-17 08:47:39 +01:00
// Check if there are no public models.
if ( ! publicCount )
2009-04-30 07:36:57 +02:00
{
2009-11-28 22:32:44 +01:00
LogEvent ( false , LogType_Fatal , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Missing public model in \" %s \" . There must be at least one public model. " , modelPath );
2009-04-30 07:36:57 +02:00
}
2009-05-18 06:26:13 +02:00
2009-12-04 00:44:46 +01:00
// Precache models.
ModelsPrecache ();
2009-11-17 08:47:39 +01:00
// Log model validation info.
LogEvent ( false , LogType_Normal , LOG_CORE_EVENTS , LogModule_Models , " Config Validation " , " Successful: %d | Unsuccessful: %d " , ModelCount , failedCount );
2009-05-18 06:26:13 +02:00
// Set config data.
2009-05-29 08:43:15 +02:00
ConfigSetConfigLoaded ( File_Models , true );
ConfigSetConfigReloadFunc ( File_Models , GetFunctionByName ( GetMyHandle (), " ModelsOnConfigReload " ));
2008-10-04 22:59:11 +02:00
}
2009-04-30 07:36:57 +02:00
/**
2009-05-29 08:43:15 +02:00
* Called when config is being reloaded .
2009-04-30 07:36:57 +02:00
*/
2009-05-18 06:26:13 +02:00
public ModelsOnConfigReload ( ConfigFile : config )
2008-10-04 22:59:11 +02:00
{
2009-05-29 08:43:15 +02:00
// Reload models config.
ModelsLoad ();
2009-06-12 15:52:51 +02:00
}
2009-07-09 23:17:50 +02:00
2009-12-04 00:44:46 +01:00
/**
* Precaches all models .
*/
ModelsPrecache ()
{
decl String : file [ PLATFORM_MAX_PATH ];
// Loop through all models, build full path and cache them.
for ( new model = 0 ; model < ModelCount ; model ++ )
{
ModelsGetFullPath ( model , file , sizeof ( file ));
PrecacheModel ( file );
}
}
2009-07-09 23:17:50 +02:00
/**
2009-11-17 08:47:39 +01:00
* Returns a random model index according to the specified filter settings .
*
* @ param client Optional . Client index used to check for
* permissions in " group " or " admins " access mode .
* Use negative index to disable permission check
* ( default ) .
* @ param teamFilter Optional . Team filtering settings . Use
* ModelTeam_Invalid to disable filter . Default is
* ModelTeam_Zombies .
* @ param accessRequireFlags Optional . One or more required access flags .
* Default is MODEL_ACCESS_PUBLIC .
* @ return Random model index according to filter , or - 1 on error .
2009-07-09 23:17:50 +02:00
*/
2009-11-17 08:47:39 +01:00
ModelsGetRandomModel ( client = - 1 , ModelTeam : teamFilter = ModelTeam_Zombies , accessRequireFlags = MODEL_ACCESS_PUBLIC )
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
decl modelIndexes [ MODELS_MAX ];
new listCount ;
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
// Loop through all models.
for ( new index = 0 ; index < ModelCount ; index ++ )
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
// Check team filtering. Skip if no match.
if ( teamFilter != ModelTeam_Invalid &&
ModelsGetTeam ( index ) != teamFilter )
{
continue ;
}
// Cache current model access flag.
new ModelAccess : access = ModelsGetAccess ( index );
new accessFlag = ModelsGetAccessFlag ( access );
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
// Check access filtering. Skip if no match.
if ( accessRequireFlags > 0 &&
! ( accessRequireFlags & accessFlag ))
{
continue ;
}
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
// Do client group authentication if client is specified.
if ( client > 0 )
{
// Check if current model use group authentication.
if ( access == ModelAccess_Group )
{
decl String : group [ 64 ];
ModelsGetGroup ( index , group , sizeof ( group ));
if ( ! ZRIsClientInGroup ( client , group ))
{
// Client not authorized to use this model.
continue ;
}
}
else
{
// No group authentication. Do regular authentication if model
// is a admin model.
if ( access == ModelAccess_Admins &&
! ZRIsClientAdmin ( client ))
{
// Client not authorized to use this model.
continue ;
}
}
}
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
// Model passed filter tests. Add to list.
modelIndexes [ listCount ] = index ;
listCount ++ ;
}
// Check if any models passed the filter.
if ( listCount )
{
return modelIndexes [ GetRandomInt ( 0 , listCount - 1 )];
}
else
{
return - 1 ;
}
}
/**
* Validates the specified index according to maximum number of models , and
* number of models in use . Unused indexes will fail validation by default .
*
* @ param index Model index to validate .
* @ param rangeOnly Optional . Do not check if the index is in use . Default
* is false , check if in use .
* @ return True if valid , false otherwise .
*/
bool : ModelsIsValidIndex ( index , bool : rangeOnly = false )
{
new bool : rangeValid = ( index >= 0 && index < MODELS_MAX );
if ( rangeOnly )
{
// Only check if the index is valid.
return rangeValid ;
}
else
{
// Check if the index is valid, and if it's in use.
return rangeValid && ( index < ModelCount );
}
}
/**
* Gets the name for the specified model .
*
* @ param index Model index .
* @ param buffer Destination string buffer .
* @ param maxlen Size of buffer .
* @ return Number of cells written , or - 1 on error .
*/
ModelsGetName ( index , String : buffer [], maxlen )
{
// Validate index.
if ( ! ModelsIsValidIndex ( index ))
{
return - 1 ;
2009-07-09 23:17:50 +02:00
}
2009-11-17 08:47:39 +01:00
return strcopy ( buffer , maxlen , ModelData [ index ][ Model_Name ]);
2009-07-09 23:17:50 +02:00
}
/**
2009-11-17 08:47:39 +01:00
* Gets the path for the specified model .
*
* @ param index Model index .
* @ param buffer Destination string buffer .
* @ param maxlen Size of buffer .
* @ return Number of cells written , or - 1 on error .
2009-07-09 23:17:50 +02:00
*/
2009-11-17 08:47:39 +01:00
ModelsGetPath ( index , String : buffer [], maxlen )
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
// Validate index.
if ( ! ModelsIsValidIndex ( index ))
{
return - 1 ;
}
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
return strcopy ( buffer , maxlen , ModelData [ index ][ Model_Path ]);
}
/**
* Gets the team for the specified model .
*
* @ param index Model index .
* @ return Team for the specified model , ModelTeam_Invalid on error .
*/
ModelTeam : ModelsGetTeam ( index )
{
// Validate index.
if ( ! ModelsIsValidIndex ( index ))
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
return ModelTeam_Invalid ;
2009-07-09 23:17:50 +02:00
}
2009-11-17 08:47:39 +01:00
return ModelData [ index ][ Model_Team ];
2009-07-09 23:17:50 +02:00
}
/**
2009-11-17 08:47:39 +01:00
* Gets the access setting for the specified model .
*
* @ param index Model index .
* @ return Access setting for the specified model , ModelAccess_Invalid
* on error .
2009-07-09 23:17:50 +02:00
*/
2009-11-17 08:47:39 +01:00
ModelAccess : ModelsGetAccess ( index )
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
// Validate index.
if ( ! ModelsIsValidIndex ( index ))
{
return ModelAccess_Invalid ;
}
2009-07-09 23:17:50 +02:00
2009-11-17 08:47:39 +01:00
return ModelData [ index ][ Model_Access ];
}
/**
* Gets the access flag for the specified access setting .
*
* @ param access Access setting to convert .
* @ return Access flag , or 0 on error .
*/
ModelsGetAccessFlag ( ModelAccess : access )
{
switch ( access )
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
case ModelAccess_Public :
{
return MODEL_ACCESS_PUBLIC ;
}
case ModelAccess_Admins :
{
return MODEL_ACCESS_ADMINS ;
}
case ModelAccess_Hidden :
{
return MODEL_ACCESS_HIDDEN ;
}
case ModelAccess_MotherZombies :
{
return MODEL_ACCESS_MOTHER_ZOMBIES ;
}
case ModelAccess_Group :
{
return MODEL_ACCESS_GROUP ;
}
2009-07-09 23:17:50 +02:00
}
2009-11-17 08:47:39 +01:00
// Invalid access flag.
return 0 ;
}
/**
* Gets the group for the specified model .
*
* @ param index Model index .
* @ param buffer Destination string buffer .
* @ param maxlen Size of buffer .
* @ return Number of cells written , or - 1 on error .
*/
ModelsGetGroup ( index , String : buffer [], maxlen )
{
// Validate index.
if ( ! ModelsIsValidIndex ( index ))
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
return - 1 ;
}
return strcopy ( buffer , maxlen , ModelData [ index ][ Model_Group ]);
}
/**
* Gets the full model file path for the specified model .
*
* @ param index Model index .
* @ param buffer Destination string buffer .
* @ param maxlen Size of buffer .
* @ return Number of cells written , or - 1 on error .
*/
ModelsGetFullPath ( index , String : buffer [], maxlen )
{
decl String : path [ PLATFORM_MAX_PATH ];
decl String : name [ 64 ];
ModelsGetPath ( index , path , sizeof ( path ));
ModelsGetName ( index , name , sizeof ( name ));
buffer [ 0 ] = 0 ;
StrCat ( buffer , maxlen , path );
StrCat ( buffer , maxlen , name );
StrCat ( buffer , maxlen , " .mdl " );
}
/**
* Converts the specified string to a team setting .
*
* @ param team String to convert .
* @ return Team setting , or ModelTeam_Invalid on error .
*/
ModelTeam : ModelsStringToTeam ( const String : team [])
{
if ( StrEqual ( team , " zombies " , false ))
{
return ModelTeam_Zombies ;
}
else if ( StrEqual ( team , " humans " , false ))
{
return ModelTeam_Humans ;
}
return ModelTeam_Invalid ;
}
/**
* Converts the specified class team ID to a team setting .
*
* @ param teamid Class team ID .
* @ return Team setting , or ModelTeam_Invalid on error .
*/
ModelTeam : ModelsTeamIdToTeam ( teamid )
{
switch ( teamid )
{
case ZR_CLASS_TEAM_ZOMBIES :
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
return ModelTeam_Zombies ;
2009-07-09 23:17:50 +02:00
}
2009-11-17 08:47:39 +01:00
case ZR_CLASS_TEAM_HUMANS :
2009-07-09 23:17:50 +02:00
{
2009-11-17 08:47:39 +01:00
return ModelTeam_Humans ;
2009-07-09 23:17:50 +02:00
}
}
2009-11-17 08:47:39 +01:00
return ModelTeam_Invalid ;
}
/**
* Converts the specified string to a access setting .
*
* @ param access String to convert .
* @ return Access setting , or ModelAccess_Invalid on error .
*/
ModelAccess : ModelsStringToAccess ( const String : access [])
{
if ( StrEqual ( access , " public " , false ))
{
return ModelAccess_Public ;
}
else if ( StrEqual ( access , " admins " , false ))
{
return ModelAccess_Admins ;
}
else if ( StrEqual ( access , " hidden " , false ))
{
return ModelAccess_Hidden ;
}
else if ( StrEqual ( access , " motherzombies " , false ))
{
return ModelAccess_MotherZombies ;
}
else if ( StrEqual ( access , " group " , false ))
{
return ModelAccess_Group ;
}
return ModelAccess_Invalid ;
2009-10-26 01:42:15 +01:00
}