Merge pull request #486 from alliedmodders/csgo-safety
Add initial version of safety checks for CS:GO to attempt to avoid user GSLT bans.
This commit is contained in:
		
						commit
						19bcc8417d
					
				| @ -132,5 +132,18 @@ | |||||||
| 	 * passed. You can disable this feature by setting the value to "0". | 	 * passed. You can disable this feature by setting the value to "0". | ||||||
| 	 */ | 	 */ | ||||||
| 	"SlowScriptTimeout"	"8" | 	"SlowScriptTimeout"	"8" | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * Per "http://blog.counter-strike.net/index.php/server_guidelines/", certain plugin | ||||||
|  | 	 * functionality will trigger all of the game server owner's Game Server Login Tokens | ||||||
|  | 	 * (GSLTs) to get banned when executed on a Counter-Strike: Global Offensive game server. | ||||||
|  | 	 * | ||||||
|  | 	 * Enabling this option will block plugins from using functionality that is known to cause this. | ||||||
|  | 	 * This option only has any effect on CS:GO. Note that this does NOT guarantee that you cannot | ||||||
|  | 	 * receive a ban. | ||||||
|  | 	 * | ||||||
|  | 	 * Disable this option at your own risk. | ||||||
|  | 	 */ | ||||||
|  | 	"FollowCSGOServerGuidelines"	"yes" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -137,6 +137,57 @@ void CHalfLife2::OnSourceModAllInitialized_Post() | |||||||
| { | { | ||||||
| 	InitLogicalEntData(); | 	InitLogicalEntData(); | ||||||
| 	InitCommandLine(); | 	InitCommandLine(); | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | 	m_CSGOBadList.init(); | ||||||
|  | 	m_CSGOBadList.add("m_iItemDefinitionIndex"); | ||||||
|  | 	m_CSGOBadList.add("m_iEntityLevel"); | ||||||
|  | 	m_CSGOBadList.add("m_iItemIDHigh"); | ||||||
|  | 	m_CSGOBadList.add("m_iItemIDLow"); | ||||||
|  | 	m_CSGOBadList.add("m_iAccountID"); | ||||||
|  | 	m_CSGOBadList.add("m_iEntityQuality"); | ||||||
|  | 	m_CSGOBadList.add("m_bInitialized"); | ||||||
|  | 	m_CSGOBadList.add("m_szCustomName"); | ||||||
|  | 	m_CSGOBadList.add("m_iAttributeDefinitionIndex"); | ||||||
|  | 	m_CSGOBadList.add("m_iRawValue32"); | ||||||
|  | 	m_CSGOBadList.add("m_iRawInitialValue32"); | ||||||
|  | 	m_CSGOBadList.add("m_nRefundableCurrency"); | ||||||
|  | 	m_CSGOBadList.add("m_bSetBonus"); | ||||||
|  | 	m_CSGOBadList.add("m_OriginalOwnerXuidLow"); | ||||||
|  | 	m_CSGOBadList.add("m_OriginalOwnerXuidHigh"); | ||||||
|  | 	m_CSGOBadList.add("m_nFallbackPaintKit"); | ||||||
|  | 	m_CSGOBadList.add("m_nFallbackSeed"); | ||||||
|  | 	m_CSGOBadList.add("m_flFallbackWear"); | ||||||
|  | 	m_CSGOBadList.add("m_nFallbackStatTrak"); | ||||||
|  | 	m_CSGOBadList.add("m_iCompetitiveRanking"); | ||||||
|  | 	m_CSGOBadList.add("m_nActiveCoinRank"); | ||||||
|  | 	m_CSGOBadList.add("m_nMusicID"); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ConfigResult CHalfLife2::OnSourceModConfigChanged(const char *key, const char *value, | ||||||
|  | 	ConfigSource source, char *error, size_t maxlength) | ||||||
|  | { | ||||||
|  | 	if (strcasecmp(key, "FollowCSGOServerGuidelines") == 0) | ||||||
|  | 	{ | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | 		if (strcasecmp(value, "no") == 0) | ||||||
|  | 		{ | ||||||
|  | 			m_bFollowCSGOServerGuidelines = false; | ||||||
|  | 			return ConfigResult_Accept; | ||||||
|  | 		} | ||||||
|  | 		else if (strcasecmp(value, "yes") == 0) | ||||||
|  | 		{ | ||||||
|  | 			m_bFollowCSGOServerGuidelines = true; | ||||||
|  | 			return ConfigResult_Accept; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return ConfigResult_Reject; | ||||||
|  | #else | ||||||
|  | 		return ConfigResult_Accept; | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ConfigResult_Ignore; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CHalfLife2::InitLogicalEntData() | void CHalfLife2::InitLogicalEntData() | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ | |||||||
| #include <sh_string.h> | #include <sh_string.h> | ||||||
| #include <sh_tinyhash.h> | #include <sh_tinyhash.h> | ||||||
| #include <am-utility.h> | #include <am-utility.h> | ||||||
|  | #include <am-hashset.h> | ||||||
| #include <am-hashmap.h> | #include <am-hashmap.h> | ||||||
| #include <sm_stringhashmap.h> | #include <sm_stringhashmap.h> | ||||||
| #include <sm_namehashset.h> | #include <sm_namehashset.h> | ||||||
| @ -152,6 +153,8 @@ public: | |||||||
| 	void OnSourceModAllInitialized(); | 	void OnSourceModAllInitialized(); | ||||||
| 	void OnSourceModAllInitialized_Post(); | 	void OnSourceModAllInitialized_Post(); | ||||||
| 	/*void OnSourceModAllShutdown();*/ | 	/*void OnSourceModAllShutdown();*/ | ||||||
|  | 	ConfigResult OnSourceModConfigChanged(const char *key, const char *value, | ||||||
|  | 		ConfigSource source, char *error, size_t maxlength) override; | ||||||
| public: //IGameHelpers
 | public: //IGameHelpers
 | ||||||
| 	SendProp *FindInSendTable(const char *classname, const char *offset); | 	SendProp *FindInSendTable(const char *classname, const char *offset); | ||||||
| 	bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info); | 	bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info); | ||||||
| @ -220,6 +223,16 @@ private: | |||||||
| 	CStack<CachedCommandInfo> m_CommandStack; | 	CStack<CachedCommandInfo> m_CommandStack; | ||||||
| 	Queue<DelayedKickInfo> m_DelayedKicks; | 	Queue<DelayedKickInfo> m_DelayedKicks; | ||||||
| 	void *m_pGetCommandLine; | 	void *m_pGetCommandLine; | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | public: | ||||||
|  | 	bool CanSetCSGOEntProp(const char *pszPropName) | ||||||
|  | 	{ | ||||||
|  | 		return !m_bFollowCSGOServerGuidelines || !m_CSGOBadList.has(pszPropName); | ||||||
|  | 	} | ||||||
|  | private: | ||||||
|  | 	ke::HashSet<ke::AString, detail::StringHashMapPolicy> m_CSGOBadList; | ||||||
|  | 	bool m_bFollowCSGOServerGuidelines = true; | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern CHalfLife2 g_HL2; | extern CHalfLife2 g_HL2; | ||||||
|  | |||||||
| @ -93,6 +93,15 @@ enum PropFieldType | |||||||
| 	PropField_String_T,			/**< Valid for Data fields.  Read only! */ | 	PropField_String_T,			/**< Valid for Data fields.  Read only! */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | inline bool CanSetPropName(const char *pszPropName) | ||||||
|  | { | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | 	return g_HL2.CanSetCSGOEntProp(pszPropName); | ||||||
|  | #else | ||||||
|  | 	return true; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
| inline edict_t *BaseEntityToEdict(CBaseEntity *pEntity) | inline edict_t *BaseEntityToEdict(CBaseEntity *pEntity) | ||||||
| { | { | ||||||
| 	IServerUnknown *pUnk = (IServerUnknown *)pEntity; | 	IServerUnknown *pUnk = (IServerUnknown *)pEntity; | ||||||
| @ -1351,6 +1360,10 @@ static cell_t SetEntProp(IPluginContext *pContext, const cell_t *params) | |||||||
| 	case Prop_Send: | 	case Prop_Send: | ||||||
| 		{ | 		{ | ||||||
| 			FIND_PROP_SEND(DPT_Int, "integer"); | 			FIND_PROP_SEND(DPT_Int, "integer"); | ||||||
|  | 			if (!CanSetPropName(prop)) | ||||||
|  | 			{ | ||||||
|  | 				return pContext->ThrowNativeError("Cannot set %s with \"FollowCSGOServerGuidelines\" option enabled.", prop); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			// This isn't in CS:S yet, but will be, doesn't hurt to add now, and will save us a build later
 | 			// This isn't in CS:S yet, but will be, doesn't hurt to add now, and will save us a build later
 | ||||||
| #if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS \ | #if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS \ | ||||||
| @ -1503,6 +1516,10 @@ static cell_t SetEntPropFloat(IPluginContext *pContext, const cell_t *params) | |||||||
| 	case Prop_Send: | 	case Prop_Send: | ||||||
| 		{ | 		{ | ||||||
| 			FIND_PROP_SEND(DPT_Float, "float"); | 			FIND_PROP_SEND(DPT_Float, "float"); | ||||||
|  | 			if (!CanSetPropName(prop)) | ||||||
|  | 			{ | ||||||
|  | 				return pContext->ThrowNativeError("Cannot set %s with \"FollowCSGOServerGuidelines\" option enabled.", prop); | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	default: | 	default: | ||||||
| @ -2088,6 +2105,11 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params) | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			if (!CanSetPropName(prop)) | ||||||
|  | 			{ | ||||||
|  | 				return pContext->ThrowNativeError("Cannot set %s with \"FollowCSGOServerGuidelines\" option enabled.", prop); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			if (bIsStringIndex) | 			if (bIsStringIndex) | ||||||
| 			{ | 			{ | ||||||
| 				offset += (element * (td->fieldSizeInBytes / td->fieldSize)); | 				offset += (element * (td->fieldSizeInBytes / td->fieldSize)); | ||||||
|  | |||||||
| @ -170,6 +170,15 @@ bool SDKTools::SDK_OnLoad(char *error, size_t maxlength, bool late) | |||||||
| 
 | 
 | ||||||
| 	InitSDKToolsAPI(); | 	InitSDKToolsAPI(); | ||||||
| 
 | 
 | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | 	m_bFollowCSGOServerGuidelines = true; | ||||||
|  | 	const char *pszValue = g_pSM->GetCoreConfigValue("FollowCSGOServerGuidelines"); | ||||||
|  | 	if (pszValue && strcasecmp(pszValue, "no") == 0) | ||||||
|  | 	{ | ||||||
|  | 		m_bFollowCSGOServerGuidelines = false; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -113,8 +113,10 @@ public: | |||||||
| 	void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax); | 	void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax); | ||||||
| public: | public: | ||||||
| 	bool HasAnyLevelInited() { return m_bAnyLevelInited; } | 	bool HasAnyLevelInited() { return m_bAnyLevelInited; } | ||||||
|  | 	bool ShouldFollowCSGOServerGuidelines() const { return m_bFollowCSGOServerGuidelines; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | 	bool m_bFollowCSGOServerGuidelines = false; | ||||||
| 	bool m_bAnyLevelInited = false; | 	bool m_bAnyLevelInited = false; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -144,6 +144,20 @@ static cell_t RemovePlayerItem(IPluginContext *pContext, const cell_t *params) | |||||||
| class CEconItemView; | class CEconItemView; | ||||||
| static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params) | static cell_t GiveNamedItem(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
|  | 	if (g_SdkTools.ShouldFollowCSGOServerGuidelines()) | ||||||
|  | 	{ | ||||||
|  | 		char *pWeaponName; | ||||||
|  | 		pContext->LocalToString(params[2], &pWeaponName); | ||||||
|  | 
 | ||||||
|  | 		// Don't allow knives other than weapon_knife,  weapon_knifegg, and wewapon_knife_t.
 | ||||||
|  | 		// Others follow pattern weapon_knife_*
 | ||||||
|  | 		size_t len = strlen(pWeaponName); | ||||||
|  | 		if (len >= 14 && strnicmp(pWeaponName, "weapon_knife_", 13) == 0 && !(pWeaponName[13] == 't' && pWeaponName[14] == '\0')) | ||||||
|  | 		{ | ||||||
|  | 			return pContext->ThrowNativeError("Blocked giving of %s due to core.cfg option FollowCSGOServerGuidelines", pWeaponName); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	static ValveCall *pCall = NULL; | 	static ValveCall *pCall = NULL; | ||||||
| 	if (!pCall) | 	if (!pCall) | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user