/*
 - в реальном времени можно только включить/выключить плагин, все остальные настройки только в текстовом файле и перезагрузкой плагина.
 2.1
 - фикс утечки памяти
 - фикс Exception reported: Client index 0 is invalid Line 217 (клиенты иногда оключались до того, как на них придут данные)
 - update sm-ripext ( v.1.0.4 unofficial) https://hlmod.ru/resources/rest-in-pawn.468/
 - фикс на реальное имя (боле не выводит пустую строчку, если имя не найдено)
 - цвета
 - выделение рамкой
 - мелкая "оптимизация" кода
 
 
 2.0
 - обновленная версия плагина
*/
#pragma semicolon 1
#include <sourcemod>
#include <ripext>
#include <sdktools>
#include <csgo_colors>

#define PLUGIN_VERSION "2.3"

public Plugin myinfo = {
	name = "MF Connect Info", 
	author = "merk (fixed by GANZ)", 
	description = "Кириллическая информация о подключившемся игроке", 
	version = PLUGIN_VERSION, 
	url = "https://vk.com/merk26"
}

//vars

HTTPClient httpClient;
Handle _cVarHOST, _cVarHideStandartMsgConnect, _cVarHideStandartMsgDisconnect, _cVarEnable, _cVarTimerValue, _cVarSteamStatus, 
_cVarGeoData, _cVarConnectLog, _cVarWelcomeSound, _cVarSteamID, _cVarDisconnectMsg, _cVarSoundNotificationConnected, 
_cVarSoundNotificationDisconnected, _cVarRealName, _cVarVACBan, _cVarIP, _cVarProvider, _cVarLines, _cVarAdminsIgnor;

bool b_hide_connect, b_hide_disconnect, b_enable, showSteamStatus, showClientLocation, showClientRealName, 
b_disconnected_msg, showIP, showProvider, hideAdmins, showLines;

int showClientSteamID, showVACBan, writeLog;

char g_IP[MAXPLAYERS + 1][25], s_host[128], s_sound_welcome[158], s_sound_connected[158], s_sound_disconnected[158], s_Port[8], prefix[63];

public void OnPluginStart() {
	
	//получаем порт
	GetConVarString(FindConVar("hostport"), s_Port, sizeof(s_Port));
	
	_cVarEnable = CreateConVar("mf_enable", "1", "Вкл/Выкл плагин", _, true, 0.0, true, 1.0);
	_cVarHOST = CreateConVar("mf_host", "http://con.m-app.ml", "Адрес для подключения к Web-серверу");
	_cVarHideStandartMsgConnect = CreateConVar("mf_hide_msg1", "1", "Скрыть стандартное сообщение о подключении", _, true, 0.0, true, 1.0);
	_cVarHideStandartMsgDisconnect = CreateConVar("mf_hide_msg2", "1", "Скрыть стандартное сообщение об отключении", _, true, 0.0, true, 1.0);
	_cVarDisconnectMsg = CreateConVar("mf_disconnect_msg", "1", "Показывать новое сообщение об отключении", _, true, 0.0, true, 1.0);
	_cVarIP = CreateConVar("mf_ip", "0", "Показывать IP адрес игрока", _, true, 0.0, true, 1.0);
	_cVarProvider = CreateConVar("mf_provider", "0", "Показывать название провайдера", _, true, 0.0, true, 1.0);
	_cVarSteamID = CreateConVar("mf_steamid", "1", "Показывать SteamID игрока 0-3 (0 - не показывать, 1 - AuthId_Steam2, 2 - AuthId_Steam3, 3 - AuthId_SteamID64)", _, true, 0.0, true, 3.0);
	_cVarTimerValue = CreateConVar("mf_protected", "16.0", "Время через которое (после старта карты) начнут отображаться сообщения (для защиты от перегрузок при массовом реконнекте)", _, true, 15.0, true, 150.0);
	_cVarSteamStatus = CreateConVar("mf_status_steam", "1", "Показывать информацию о статусе лицензии Steam/No-steam", _, true, 0.0, true, 1.0);
	_cVarRealName = CreateConVar("mf_client_real_name", "1", "Показывать настоящее имя игрока (если указано в Steam)", _, true, 0.0, true, 1.0);
	_cVarVACBan = CreateConVar("mf_vac_bans", "1", "Показывать информацию о VAC бане у Steam игроков 0-2 (0 - не показывать, 1 - отдельной строкой, 2 - найденный бан после SteamID)", _, true, 0.0, true, 2.0);
	_cVarGeoData = CreateConVar("mf_geo_data", "1", "Показывать гео информацию", _, true, 0.0, true, 1.0);
	_cVarConnectLog = CreateConVar("mf_writelog", "1", "Запись подключений в лог файл 0-2 (0 - не записывать, 1 - записывать разделяя по дате, 2 - записывать в один файл)", _, true, 0.0, true, 2.0);
	_cVarWelcomeSound = CreateConVar("mf_welcomesound", "atomhost/hello.mp3", "Звук приветствия для ВОШЕДШЕГО игрока; \"off\" - выкл");
	_cVarSoundNotificationConnected = CreateConVar("mf_entersound", "atomhost/enter.mp3", "Звук уведомления для ВСЕХ игроков о подключении нового игрока; \"off\" - выкл");
	_cVarSoundNotificationDisconnected = CreateConVar("mf_exitsound", "atomhost/exit.mp3", "Звук уведомления для ВСЕХ игроков при отключении игрока; \"off\" - выкл");
	_cVarAdminsIgnor = CreateConVar("mf_admins_ignor", "1", "Не показывать информацию, если игрок админ (нужен флаг d или z)", _, true, 0.0, true, 1.0);
	_cVarLines = CreateConVar("mf_lines", "1", "Выделять блок линиями и добавлять префикс", _, true, 0.0, true, 1.0);
	
	AutoExecConfig(true, "mf_connect_info_2");
	LoadTranslations("mf_connect_info_2.phrases");
	
	HookConVarChange(_cVarEnable, OnConVarChanged);
	HookEvent("player_connect", player_connect, EventHookMode_Pre);
	HookEvent("player_disconnect", player_disconnect, EventHookMode_Pre);
	
	//pree hook for CS:S latest only
	//HookEvent("player_connect_client", player_connect, EventHookMode_Pre);
	
	//создаем папку для логов
	char dir[255];
	BuildPath(Path_SM, dir, sizeof(dir), "logs/connect");
	NewFolder(dir);
}

public void OnConVarChanged(Handle convar, const char[] oldValue, const char[] newValue) {
	if (convar == _cVarEnable)
		b_enable = GetConVarBool(convar);
}

//загружаем конфиги из файла
public void OnConfigsExecuted() {
	
	//открываем соединение с сервером
	GetConVarString(_cVarHOST, s_host, sizeof(s_host));
	httpClient = new HTTPClient(s_host);
	
	GetConVarString(_cVarWelcomeSound, s_sound_welcome, sizeof(s_sound_welcome));
	GetConVarString(_cVarSoundNotificationConnected, s_sound_connected, sizeof(s_sound_connected));
	GetConVarString(_cVarSoundNotificationDisconnected, s_sound_disconnected, sizeof(s_sound_disconnected));
	
	char buf[158];
	if (!StrEqual(s_sound_welcome, "off", false)) {
		Format(buf, 158, "sound/%s", s_sound_welcome);
		AddFileToDownloadsTable(buf);
		PrecacheSound(s_sound_welcome, true);
	}
	if (!StrEqual(s_sound_connected, "off", false)) {
		Format(buf, 158, "sound/%s", s_sound_connected);
		AddFileToDownloadsTable(buf);
		PrecacheSound(s_sound_connected, true);
	}
	if (!StrEqual(s_sound_disconnected, "off", false)) {
		Format(buf, 158, "sound/%s", s_sound_disconnected);
		AddFileToDownloadsTable(buf);
		PrecacheSound(s_sound_disconnected, true);
	}
	
	b_hide_connect = GetConVarBool(_cVarHideStandartMsgConnect);
	b_hide_disconnect = GetConVarBool(_cVarHideStandartMsgDisconnect);
	showSteamStatus = GetConVarBool(_cVarSteamStatus);
	showClientLocation = GetConVarBool(_cVarGeoData);
	writeLog = GetConVarInt(_cVarConnectLog);
	b_disconnected_msg = GetConVarBool(_cVarDisconnectMsg);
	showClientSteamID = GetConVarInt(_cVarSteamID);
	showClientRealName = GetConVarBool(_cVarRealName);
	showVACBan = GetConVarInt(_cVarVACBan);
	showIP = GetConVarBool(_cVarIP);
	showProvider = GetConVarBool(_cVarProvider);
	hideAdmins = GetConVarBool(_cVarAdminsIgnor);
	showLines = GetConVarBool(_cVarLines);
	
	if (showLines)
		Format(prefix, 63, "%t", "prefix");
}

public void OnMapStart() {
	//выключаем плагин, чтобы он не засыпал сервер запросами при массовом коннекте, а затем включаем по таймеру
	b_enable = false;
	CreateTimer(GetConVarFloat(_cVarTimerValue), t_star_connect_info);
}

//включаем плагин обратно
public Action t_star_connect_info(Handle timer) {
	if (GetConVarBool(_cVarEnable))
		b_enable = true;
	return Plugin_Stop;
}

//игрок приконнектился
public Action player_connect(Handle event, const char[] name, bool silent) {
	if (b_hide_connect) {
		char s_steam[64], s_name[64], s_address[32];
		GetEventString(event, "name", s_name, sizeof(s_name));
		GetEventString(event, "networkid", s_steam, sizeof(s_steam));
		GetEventString(event, "address", s_address, sizeof(s_address));
		LogToGame("\"%s<%i><%s><>\" connected, address \"%s\"", s_name, GetEventInt(event, "userid"), s_steam, s_address);
		return Plugin_Handled;
	}
	return Plugin_Continue;
}

public Action player_disconnect(Handle event, const char[] name, bool silent) {
	int client = GetClientOfUserId(GetEventInt(event, "userid"));
	
	if (!client || IsFakeClient(client))
		return Plugin_Continue;
	char s_reason[64];
	GetEventString(event, "reason", s_reason, sizeof(s_reason));
	
	if (b_disconnected_msg) {
		
		if (StrEqual(s_reason, "Disconnect", false))
			CGOPrintToChatAll(" %t", "player_disconnect", prefix, client);
		else
			CGOPrintToChatAll(" %t", "player_dropped", prefix, client);
		
		if (!StrEqual(s_sound_disconnected, "off", false))
			EmitSoundToAll(s_sound_disconnected); // играем звук выхода
	}
	if (b_hide_disconnect) {
		char s_steam[64], s_name[64];
		GetEventString(event, "name", s_name, sizeof(s_name));
		GetEventString(event, "networkid", s_steam, sizeof(s_steam));
		LogToGame("\"%s<%i><%s><>\" disconnected (reason \"%s\")", s_name, GetEventInt(event, "userid"), s_steam, s_reason);
		return Plugin_Handled;
	}
	else
		return Plugin_Continue;
}

public void OnClientPutInServer(client) {
	//глушим недоразумения в зачатке
	if (!b_enable || IsFakeClient(client) || !GetClientIP(client, g_IP[client], 25))
		return;
	
	//играем приветствие
	if (!StrEqual(s_sound_welcome, "off", false) && IsClientInGame(client))
		EmitSoundToClient(client, s_sound_welcome);
	
	char client_id[30], q[1024];
	GetClientAuthId(client, AuthId_SteamID64, client_id, 30);
	Format(q, sizeof(q), "api.php?client_id=%s&client_ip=%s&server_port=%s&version=%s", client_id, g_IP[client], s_Port, PLUGIN_VERSION);
	httpClient.Get(q, OnDataReceived, GetClientUserId(client));
}

//обрабатываем р-ат запроса
public void OnDataReceived(HTTPResponse response, any value) {
	
	int client = GetClientOfUserId(value);
	
	if (!client)
		return;
	
	if (hideAdmins && (GetUserFlagBits(client) & (ADMFLAG_BAN | ADMFLAG_ROOT)))
		return;
	if (showLines)
		CGOPrintToChatAll(" %t", "line_up");
	
	if (!response || response.Status != HTTPStatus_OK || response.Data == null) {
		CGOPrintToChatAll(" %t", "connected", prefix, client, "off");
		CGOPrintToChatAll(" %t", "server_error", prefix);
		if (showLines) CGOPrintToChatAll(" %t", "line_down");
		return;
	}
	
	JSONObject client_data = view_as<JSONObject>(response.Data);
	
	char error[250], client_real_name[250], client_location[250], client_steamid64[250], client_provider[250];
	bool client_steam, client_vac_banned;
	
	client_data.GetString("error", error, sizeof(error));
	client_data.GetString("client_real_name", client_real_name, sizeof(client_real_name));
	client_data.GetString("client_location", client_location, sizeof(client_location));
	client_data.GetString("client_steamid64", client_steamid64, sizeof(client_steamid64));
	if (showProvider)
		client_data.GetString("client_provider", client_provider, sizeof(client_provider));
	
	client_steam = client_data.GetBool("client_steam");
	client_vac_banned = client_data.GetBool("client_vac_baned");
	
	if (strcmp(error, ""))
		CGOPrintToChatAll(" %s%s", prefix, error);
	
	//играем уведомление для игроков
	if (!StrEqual(s_sound_connected, "off", false)) {
		for (new i = 1; i <= MaxClients; i++) {
			if (IsClientInGame(i) && !IsFakeClient(i) && i != client)
				EmitSoundToClient(i, s_sound_connected);
		}
	}
	
	//печатаем стим и статус лицензии если нужно
	if (showSteamStatus)
		CGOPrintToChatAll(" %t", "connected", prefix, client, client_steam ? "steam" : "no_steam");
	else
		CGOPrintToChatAll(" %t", "connected", prefix, client, "off");
	
	//имя
	if (showClientRealName && strlen(client_real_name) > 1)
		CGOPrintToChatAll(" %t", "user_name", prefix, client_real_name);
	
	//стимид
	if (showClientSteamID != 0) {
		char client_steam_id[32];
		switch (showClientSteamID) {
			case 1:
			GetClientAuthId(client, AuthId_Steam2, client_steam_id, sizeof(client_steam_id));
			case 2:
			GetClientAuthId(client, AuthId_Steam3, client_steam_id, sizeof(client_steam_id));
			case 3:
			GetClientAuthId(client, AuthId_SteamID64, client_steam_id, sizeof(client_steam_id));
		}
		
		//VAC бан
		if (showVACBan == 2 && client_steam && client_vac_banned)
			CGOPrintToChatAll(" %t", "steam_id", prefix, client_steam_id, "vac_ban");
		else
			CGOPrintToChatAll(" %t", "steam_id", prefix, client_steam_id, "off");
	}
	
	//ип
	if (showIP)
		CGOPrintToChatAll(" %t", "ip", prefix, g_IP[client]);
	
	//провайдер
	if (showProvider && strlen(client_provider) > 1)
		CGOPrintToChatAll(" %t", "provider", prefix, client_provider);
	
	//VAC бан
	if (showVACBan == 1 && client_steam)
		CGOPrintToChatAll(" %t", client_vac_banned ? "vac_banned" : "vac_banned_no", prefix);
	
	//геоданные
	if (showClientLocation && strlen(client_location) > 1)
		CGOPrintToChatAll(" %t", "location", prefix, client_location);
	
	if (showLines)
		CGOPrintToChatAll(" %t", "line_down");
	
	//лог
	if (writeLog != 0) {
		char steamid[32], file[PLATFORM_MAX_PATH];
		GetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid));
		
		switch (writeLog) {
			case 1: {
				char s_date1[21], s_date2[3], s_dir[PLATFORM_MAX_PATH];
				
				FormatTime(s_date1, sizeof(s_date1), "%Y-%m", -1);
				FormatTime(s_date2, sizeof(s_date2), "%d", -1);
				BuildPath(Path_SM, s_dir, sizeof(s_dir), "logs/connect/%s", s_date1);
				NewFolder(s_dir);
				BuildPath(Path_SM, file, sizeof(file), "logs/connect/%s/day-%s.log", s_date1, s_date2);
			}
			case 2: BuildPath(Path_SM, file, sizeof(file), "logs/connect/mf_connect.log");
		}
		//LogToFileEx(file, "%s - %N - %s (steam: %b)", client_steamid64, client, g_IP[client], client_steam);
		LogToFileEx(file, "%s - lic: %b, %s (%s), %N - %s", g_IP[client], client_steam, steamid, client_steamid64, client, client_location);
	}
}

public NewFolder(char[] dir) {
	if (!DirExists(dir)) {
		CreateDirectory(dir, 480);
	}
}

public void OnMapEnd() {
	//закрываем соединение с сервером
	if (httpClient != INVALID_HANDLE) CloseHandle(httpClient);
}