#pragma semicolon 1

#include <sourcemod>
#include <basecomm>
#include <RevEmuAPI>

#pragma newdecls required

/* CONVARS */
ConVar g_hCvar_BlockSpoof;
ConVar g_hCvar_BlockAdmin;
ConVar g_hCvar_BlockVoice;

/* DATABASE */
Database g_hDatabase;

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "NoSteamManager_RevEmu",
	author       = "zaCade + Neon",
	description  = "Manage No-Steam clients, denying admin access, ect.",
	version      = "2.0.0"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int errorSize)
{
	CreateNative("NSM_IsPlayerSteam", Native_IsPlayerSteam);
	CreateNative("NSM_GetPlayerType", Native_GetPlayerType);

	RegPluginLibrary("NoSteamManager");
	return APLRes_Success;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	g_hCvar_BlockSpoof = CreateConVar("sm_nosteam_block_spoof", "1", "Kick nosteamers that use authenticated steamids.",       FCVAR_NONE, true, 0.0, true, 1.0);
	g_hCvar_BlockAdmin = CreateConVar("sm_nosteam_block_admin", "1", "Should people marked as nosteam be blocked from admin?", FCVAR_NONE, true, 0.0, true, 1.0);
	g_hCvar_BlockVoice = CreateConVar("sm_nosteam_block_voice", "1", "Should people marked as nosteam be blocked from voice?", FCVAR_NONE, true, 0.0, true, 1.0);

	AddMultiTargetFilter("@steam", Filter_Steam, "Steam Players", false);
	AddMultiTargetFilter("@nosteam", Filter_NoSteam, "No-Steam Players", false);

	RegConsoleCmd("sm_nosteam", Command_DisplaySteamStats, "Shows the number of Steam and No-Steam players");
	RegConsoleCmd("sm_steam", Command_DisplaySteamStats, "Shows the number of Steam and No-Steam players");

	AutoExecConfig();
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnConfigsExecuted()
{
	if(!g_hCvar_BlockSpoof.BoolValue)
		return;

	Database.Connect(SQL_OnDatabaseConnect, "NoSteamManager");
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginEnd()
{
	RemoveMultiTargetFilter("@steam", Filter_Steam);
	RemoveMultiTargetFilter("@nosteam", Filter_NoSteam);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Command_DisplaySteamStats(int client, int args)
{
	char aBuf[1024];
	char aBuf2[MAX_NAME_LENGTH];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i))
		{
			if(!RevEmu_IsPlayerSteam(i))
			{
				GetClientName(i, aBuf2, sizeof(aBuf2));
				StrCat(aBuf, sizeof(aBuf), aBuf2);
				StrCat(aBuf, sizeof(aBuf), ", ");
			}
		}
	}

	if(strlen(aBuf))
	{
		aBuf[strlen(aBuf) - 2] = 0;
		ReplyToCommand(client, "[SM] No-Steam clients online: %s", aBuf);
	}
	else
		ReplyToCommand(client, "[SM] No-Steam clients online: none");

	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public bool Filter_Steam(const char[] sPattern, Handle hClients)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i))
		{
			if(RevEmu_IsPlayerSteam(i))
				PushArrayCell(hClients, i);
		}
	}
	return true;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public bool Filter_NoSteam(const char[] sPattern, Handle hClients)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i))
		{
			if(!RevEmu_IsPlayerSteam(i))
				PushArrayCell(hClients, i);
		}
	}
	return true;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientAuthorized(int client, const char[] sAuthID)
{
	if(!g_hCvar_BlockSpoof.BoolValue || !g_hDatabase)
		return;

	if(IsFakeClient(client) || IsClientSourceTV(client))
		return;

	char sQuery[512];
	Format(sQuery, sizeof(sQuery), "SELECT * FROM connections WHERE auth='%s'", sAuthID);

	g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, GetClientSerial(client), DBPrio_Low);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action OnClientPreAdminCheck(int client)
{
	if(!g_hCvar_BlockAdmin.BoolValue)
		return Plugin_Continue;

	if(IsFakeClient(client) || IsClientSourceTV(client))
		return Plugin_Continue;

	if(!RevEmu_IsPlayerSteam(client))
	{
		char sConnectionType[32];
		RevEmu_GetPlayerType(client, sConnectionType, sizeof(sConnectionType));

		LogMessage("%L has a illegitimate connection type: '%s', denying admin.", client, sConnectionType);
		NotifyPostAdminCheck(client);
		return Plugin_Handled;
	}
	else return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPostAdminCheck(int client)
{
	if(!g_hCvar_BlockVoice.BoolValue)
		return;

	if(IsFakeClient(client) || IsClientSourceTV(client))
		return;

	if(!RevEmu_IsPlayerSteam(client))
	{
		char sConnectionType[32];
		RevEmu_GetPlayerType(client, sConnectionType, sizeof(sConnectionType));

		LogMessage("%L has a illegitimate connection type: '%s', muting client.", client, sConnectionType);
		BaseComm_SetClientMute(client, true);
		return;
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
{
	if(!db || strlen(error))
	{
		LogError("Database error: %s", error);
		return;
	}

	g_hDatabase = db;

	char sQuery[512];
	Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS connections (`auth` varchar(32), `type` varchar(32), `address` varchar(16), PRIMARY KEY (`auth`))");

	g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, _, DBPrio_High);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[] error, any data)
{
	if(!db || strlen(error))
	{
		LogError("Query error: %s", error);
		return;
	}

	int client;
	if ((client = GetClientFromSerial(data)) == 0)
		return;

	char sAuthID[32];
	GetClientAuthId(client, AuthId_Steam2, sAuthID, sizeof(sAuthID));

	char sAddress[16];
	GetClientIP(client, sAddress, sizeof(sAddress));

	char sConnectionType[32];
	RevEmu_GetPlayerType(client, sConnectionType, sizeof(sConnectionType));

	if(results.RowCount && results.FetchRow())
	{
		int iFieldNum;
		char sResultAddress[16];
		char sResultConnectionType[32];

		results.FieldNameToNum("address", iFieldNum);
		results.FetchString(iFieldNum, sResultAddress, sizeof(sResultAddress));

		results.FieldNameToNum("type", iFieldNum);
		results.FetchString(iFieldNum, sResultConnectionType, sizeof(sResultConnectionType));

		delete results;

		if(!RevEmu_IsPlayerSteam(client))
		{
			if(!StrEqual(sConnectionType, sResultConnectionType, false) && StrEqual(sResultConnectionType, "SteamLegit", false))
			{
				if(StrEqual(sAddress, sResultAddress, false))
				{
					LogMessage("%L tried to join with a legitimate steamid while having a illegitimate connection type: '%s'. Allowing connection, IPs match. (Known: %s)", client, sConnectionType, sAddress);
				}
				else
				{
					LogAction(client, -1, "\"%L\" tried to join with a legitimate steamid while having a illegitimate connection type: '%s'. Refusing connection, IPs dont match. (Known: %s | Current: %s)", client, sConnectionType, sResultAddress, sAddress);
					KickClient(client, "Trying to join with a legitimate steamid while having a illegitimate connection.");
					return;
				}
			}
		}
	}

	char sQuery[512];
	Format(sQuery, sizeof(sQuery), "INSERT INTO connections (auth, type, address) VALUES ('%s', '%s', '%s') ON DUPLICATE KEY UPDATE type='%s', address='%s';", sAuthID, sConnectionType, sAddress, sConnectionType, sAddress);

	g_hDatabase.Query(SQL_OnQueryCompleted, sQuery, _, DBPrio_Low);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_IsPlayerSteam(Handle hPlugin, int numParams)
{
	int client = GetNativeCell(1);

	if (client < 1 || client > MaxClients)
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client index %d is invalid", client);
	}
	else if (!IsClientConnected(client))
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected", client);
	}
	else if (IsFakeClient(client))
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is a bot", client);
	}

	return RevEmu_IsPlayerSteam(client);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_GetPlayerType(Handle hPlugin, int numParams)
{
	int client = GetNativeCell(1);
	int length = GetNativeCell(3);

	if (client < 1 || client > MaxClients)
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client index %d is invalid", client);
	}
	else if (!IsClientConnected(client))
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected", client);
	}
	else if (IsFakeClient(client))
	{
		return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is a bot", client);
	}

	char[] sPlayerType = new char[length + 1];
	RevEmu_GetPlayerType(client, sPlayerType, length + 1);

	return SetNativeString(2, sPlayerType, length + 1);
}