// ====================================
//  ███╗   ███╗ ██████╗ ███╗   ██╗
//  ████╗ ████║██╔═══██╗████╗  ██║
//  ██╔████╔██║██║   ██║██╔██╗ ██║
//  ██║╚██╔╝██║██║   ██║██║╚██╗██║
//  ██║ ╚═╝ ██║╚██████╔╝██║ ╚████║
//  ╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═══╝
// MONITOR
// JMI MONITOR CONNECT
// ====================================
// Дата изменения: 24.07.2026
// Время: 13:40
// Автор: JuggerMi
// Версия: 1.0
// Список изменений:
//   - Создан плагин: отправляет список игроков сервера в мониторинг сайта
//   - Работает в обход блокировки внешних запросов (анти-DDoS модули)
//   - Отправка обычным HTTP-запросом с личным токеном сервера
//   - Плагин НЕ имеет доступа к базе сайта — только отправка своих данных
//   - Ничего не меняет в игре: только читает список игроков
// ====================================
// ЗАЧЕМ НУЖЕН:
// Мониторинг узнаёт игроков внешним запросом к серверу. Многие модули
// защиты (анти-DDoS, фильтры запросов) этот запрос блокируют — тогда на
// сайте видно количество игроков, но не их ники. Этот плагин решает
// задачу с другой стороны: сервер сам отправляет список на сайт.
//
// БЕЗОПАСНОСТЬ:
// Плагин отправляет данные обычным веб-запросом и предъявляет токен,
// выданный сайтом именно вашему серверу. Токен позволяет обновлять
// ТОЛЬКО список игроков вашего сервера. Доступа к базе сайта, чужим
// серверам или настройкам у плагина нет. Код открыт — можете проверить
// или изменить его под себя.
//
// УСТАНОВКА:
//   1. Возьмите ТОКЕН на странице своего сервера в мониторинге
//      (кнопка "Показать токен" — видна владельцу сервера).
//   2. Впишите токен и адрес сайта в настройках ниже.
//   3. Скомпилируйте и положите .amxx в addons/amxmodx/plugins,
//      добавьте строку в plugins.ini.
// ====================================

#include <amxmodx>
#include <sockets>

// ------------------ НАСТРОЙКИ ------------------
// Токен вашего сервера (выдаётся на сайте, в карточке сервера)
new const API_TOKEN[] = "ВСТАВЬТЕ_СЮДА_ТОКЕН"

// Адрес сайта мониторинга (без http:// и без слэша в конце)
new const API_HOST[]  = "jmiplay.com"

// Путь к приёмнику на сайте (менять не нужно)
new const API_PATH[]  = "/api_players.php"

// Порт сайта: 80 — обычный HTTP. Если сайт только по HTTPS, всё равно
// оставьте 80: приёмник доступен и так (или попросите админа сайта).
#define API_PORT        80

// Как часто отправлять список (секунд). Реком.: 20-30
#define SEND_EVERY      25.0

// Отправлять ли ботов? 0 = нет, 1 = да
#define SEND_BOTS       1

// Диагностика: 1 = писать результат в лог AMXX. Потом можно поставить 0.
#define DEBUG           1
// -----------------------------------------------

new const VERSION[] = "1.0"

new g_MapStart
new g_Body[3072]        // тело запроса держим глобально: экономим стек
new g_Post[3584]

public plugin_init()
{
	register_plugin("JMI Monitor Connect", VERSION, "JuggerMi")
	g_MapStart = get_systime()

	set_task(SEND_EVERY, "task_send", _, _, _, "b")
	set_task(8.0, "task_send")

	#if DEBUG
	log_amx("[MONITOR] Плагин запущен. Сайт: %s%s", API_HOST, API_PATH)
	#endif
}

// Собираем список игроков и отправляем на сайт
public task_send()
{
	new players[32], num
	#if SEND_BOTS
	get_players(players, num, "h")     // все, кроме HLTV
	#else
	get_players(players, num, "ch")    // без ботов
	#endif

	// Формат: ник|счёт|время|команда|бот — по одному игроку на строку
	g_Body[0] = EOS
	new line[160], sent

	for (new i = 0; i < num; i++)
	{
		new id = players[i]
		if (!is_user_connected(id))
			continue

		new name[32], team[8]
		get_user_name(id, name, charsmax(name))
		switch (get_user_team(id)) { case 1: copy(team, charsmax(team), "T"); case 2: copy(team, charsmax(team), "CT"); default: copy(team, charsmax(team), "SPEC"); }

		new frags = get_user_frags(id)
		new ptime = floatround(get_user_time(id, 1))
		new isbot = _:is_user_bot(id)

		// вертикальная черта в нике сломала бы разбор — заменяем
		replace_all(name, charsmax(name), "|", "/")
		replace_all(name, charsmax(name), "^n", " ")

		formatex(line, charsmax(line), "%s%s|%d|%d|%s|%d", sent ? "^n" : "", name, frags, ptime, team, isbot)

		if (strlen(g_Body) + strlen(line) > 2900)
			break

		add(g_Body, charsmax(g_Body), line)
		sent++
	}

	send_to_site(sent)
}

// Отправка данных обычным HTTP-запросом
send_to_site(count)
{
	new mapname[64]
	get_mapname(mapname, charsmax(mapname))

	// тело запроса
	new data[3328]
	formatex(data, charsmax(data), "token=%s&map=%s&maxpl=%d&map_start=%d&players=%s",
		API_TOKEN, mapname, get_maxplayers(), g_MapStart, g_Body)

	new err, sock = socket_open(API_HOST, API_PORT, SOCKET_TCP, err)
	if (sock <= 0)
	{
		#if DEBUG
		log_amx("[MONITOR] Не удалось соединиться с сайтом (код %d)", err)
		#endif
		return
	}

	formatex(g_Post, charsmax(g_Post),
		"POST %s HTTP/1.0^nHost: %s^nContent-Type: application/x-www-form-urlencoded^nContent-Length: %d^nConnection: close^n^n%s",
		API_PATH, API_HOST, strlen(data), data)

	socket_send(sock, g_Post, strlen(g_Post))

	#if DEBUG
	new reply[128]
	socket_recv(sock, reply, charsmax(reply))
	log_amx("[MONITOR] Отправлено игроков: %d. Ответ сайта: %s", count, reply)
	#endif

	socket_close(sock)
}
