From c6cebc68a7dea401ab8fdef559b0ebffbbd84336 Mon Sep 17 00:00:00 2001
From: jenz <jenz@jenz.jenz>
Date: Sat, 29 Jun 2024 00:26:24 +0200
Subject: [PATCH] initial release to simulate clients from ze server on another
 server to get us advertised on gametracker again hehe

---
 .../python/move_text_to_simulate_server.py    |  10 +
 .../scripting/simulate_ze_server.sp           | 212 ++++++++++++++++++
 .../systemctl/move_to_simulate.service        |  10 +
 3 files changed, 232 insertions(+)
 create mode 100644 simulate_ze_server/python/move_text_to_simulate_server.py
 create mode 100644 simulate_ze_server/scripting/simulate_ze_server.sp
 create mode 100644 simulate_ze_server/systemctl/move_to_simulate.service

diff --git a/simulate_ze_server/python/move_text_to_simulate_server.py b/simulate_ze_server/python/move_text_to_simulate_server.py
new file mode 100644
index 00000000..bdec0cbf
--- /dev/null
+++ b/simulate_ze_server/python/move_text_to_simulate_server.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python3
+def main():
+    with open("/home/gameservers/css_ze/cstrike/addons/sourcemod/logs/simulate.txt", "r") as file:
+        lines = [line.rstrip() for line in file]
+
+    with open('/home/gameservers/css_simulate_ze/cstrike/addons/sourcemod/logs/simulate.txt', 'w') as fp:
+        fp.write('\n'.join(lines))
+
+if __name__ == '__main__':
+    main()
diff --git a/simulate_ze_server/scripting/simulate_ze_server.sp b/simulate_ze_server/scripting/simulate_ze_server.sp
new file mode 100644
index 00000000..2c4b94f2
--- /dev/null
+++ b/simulate_ze_server/scripting/simulate_ze_server.sp
@@ -0,0 +1,212 @@
+#pragma semicolon 1
+
+#include <sourcemod>
+#include <sdktools>
+
+int server_ports[2] = {27015, 27075}; //server ports: ze server, simulated server.
+int g_iServerPort;
+int g_iClientIndexUserIDMapping[MAXPLAYERS + 1];
+
+//----------------------------------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------------------------------
+public Plugin myinfo =
+{
+	name         = "info from the ze server",
+	author       = "jenz",
+	description  = "just copies maps, hostname and client names info from the ze server",
+	version      = "1.0.0"
+};
+
+public void OnPluginEnd()
+{
+	for(int i = 1; i <= MaxClients; i++)
+	{
+		if(IsValidClient(i) && IsFakeClient(i))
+		{
+			KickClient(i, "Disconnect by user.");
+		}
+	}
+}
+
+public void OnPluginStart()
+{
+    g_iServerPort = GetConVarInt(FindConVar("hostport"));
+    if (g_iServerPort == server_ports[0])
+    {
+        CreateTimer(15.0, update_info_over_txt, _, TIMER_REPEAT);
+    }
+    else if (g_iServerPort == server_ports[1])
+    {
+        CreateTimer(GetRandomFloat(5.0, 25.0), read_info_over_txt);
+    }
+}
+
+public Action read_info_over_txt(Handle timer, any data)
+{
+    new Handle:fileHandle = OpenFile("/addons/sourcemod/logs/simulate.txt", "r");
+    char lineBuffer[2048];
+    int count = 0;
+
+    int current_ClientUserID_iteration[MAXPLAYERS + 1];
+
+    bool already_created_fakeClient = false;
+
+    while(!IsEndOfFile(fileHandle) && ReadFileLine(fileHandle, lineBuffer, sizeof(lineBuffer)))
+    {
+        TrimString(lineBuffer);
+        if (count == 0) //mapname
+        {
+            char mapname[256];
+            GetCurrentMap(mapname, sizeof(mapname));
+            if (!StrEqual(mapname, lineBuffer))
+            {
+                ForceChangeLevel(lineBuffer, "");
+                break;
+            }
+        }
+        else if (count == 1) //hostname
+        {
+            static Handle hHostName;
+            if((hHostName = FindConVar("hostname")) == INVALID_HANDLE)
+                return Plugin_Handled;
+            SetConVarString(hHostName, lineBuffer, false, false);
+        }
+        else //reading clientUserID, client frag and playername
+        {
+            //index 0 = ClientUserID, index 1 = client frags, index 2 = client user name.
+            char[][] sPart = new char[3][256];
+            ExplodeString(lineBuffer, " ", sPart, 3, 256);
+            int ClientUserID = StringToInt(sPart[0]);
+            
+            bool does_client_user_id_exist = false;
+            for (int i = 0; i <= MaxClients; i++)
+            {
+                //the clients on the simulate server will always first exist after CreateFakeClient so this approach should be safe.
+                if (IsValidClient(i) && !IsClientSourceTV(i) && g_iClientIndexUserIDMapping[i] == ClientUserID)
+                {
+                    does_client_user_id_exist = true;
+                    break;
+                }
+            }
+
+            if (!does_client_user_id_exist && !already_created_fakeClient)
+            {
+                //User ID is new, so lets create the FakeClient.
+                int iIndex = CreateFakeClient(sPart[2]);
+
+                if(iIndex < 1 || iIndex > MaxClients)
+                    break;
+
+                SetEntityFlags(iIndex, FL_CLIENT);
+                DispatchKeyValue(iIndex, "classname", "player");
+                DispatchSpawn(iIndex);
+
+                AdminId FakeAdmin = CreateAdmin();
+                SetAdminFlag(FakeAdmin, Admin_Custom6, true);
+                SetUserAdmin(iIndex, FakeAdmin, true);
+
+                g_iClientIndexUserIDMapping[iIndex] = ClientUserID;
+                already_created_fakeClient = true;
+            }
+
+            //updating the clients frags and username based on the ClientUserID.
+            for (int i = 0; i <= MaxClients; i++)
+            {
+                if (IsValidClient(i) && !IsClientSourceTV(i) && g_iClientIndexUserIDMapping[i] == ClientUserID)
+                {
+                    int clientFrag = StringToInt(sPart[1]);
+                    SetEntProp(i, Prop_Data, "m_iFrags", clientFrag);
+                    SetClientName(i, sPart[2]);
+                    //indicating that the client still exists right now.
+                    current_ClientUserID_iteration[i] = ClientUserID;
+                    break;
+                }
+            }
+        }
+        count++;
+    }
+    CloseHandle(fileHandle);
+
+    //kicking clients whos UserID is not connected on ze anymore.
+    for (int i = 0; i <= MaxClients; i++)
+    {
+        if (IsValidClient(i) && !IsClientSourceTV(i))
+        {
+            bool does_UserID_still_exist = false;
+            for (int j = 0; j <= MaxClients; j++)
+            {
+                //for every user in g_iClientIndexUserIDMapping does the userID exist in the current iteration still.
+                if (IsValidClient(j) && !IsClientSourceTV(j) && g_iClientIndexUserIDMapping[i] == current_ClientUserID_iteration[j])
+                {
+                    does_UserID_still_exist = true;
+                    break;
+                }
+            }
+            if (!does_UserID_still_exist)
+            {
+                g_iClientIndexUserIDMapping[i] = -1;
+                KickClient(i, "Disconnect by user.");
+            }
+        }
+    }
+    CreateTimer(GetRandomFloat(5.0, 25.0), read_info_over_txt);
+    return Plugin_Handled;
+}
+
+public Action update_info_over_txt(Handle timer, any data)
+{
+    //sending map name, hostname and player names to the simulate server from ze server.
+    char g_cMapname[256];
+    GetCurrentMap(g_cMapname, sizeof(g_cMapname));
+    
+    static Handle hHostName;
+    if((hHostName = FindConVar("hostname")) == INVALID_HANDLE)
+        return Plugin_Handled;
+    char hostname[256];
+    GetConVarString(hHostName, hostname, sizeof(hostname));
+
+    char msg[2048];
+    Format(msg, sizeof(msg), "%s\n%s\n", g_cMapname, hostname);
+    //cleaning the file so it only contains mapname and hostname
+    new Handle:testFile = OpenFile("/addons/sourcemod/logs/simulate.txt", "w");
+    WriteFileLine(testFile, msg);
+    CloseHandle(testFile);
+
+    Format(msg, sizeof(msg), "");
+
+    for (int i = 0; i < MaxClients; i++)
+    {
+        if (IsValidClient(i) && !IsClientSourceTV(i))
+        {
+            int frags = GetClientFrags(i);
+            //GetClientUserId is the only consistent thing for fakeclients.
+            Format(msg, sizeof(msg), "%d %i %N\n", GetClientUserId(i), frags, i);
+
+            new Handle:testFile_l = OpenFile("/addons/sourcemod/logs/simulate.txt", "a");
+            WriteFileLine(testFile_l, msg);
+            CloseHandle(testFile_l);
+        }
+    }
+    return Plugin_Handled;
+}
+
+//sending players over to the actual ze server if its the simulated server.
+public void OnClientPostAdminCheck(int client)
+{
+    if (g_iServerPort != server_ports[1])
+    {
+        return;
+    }
+    if (!IsFakeClient(client) && (!IsClientSourceTV(client)))
+    {
+        ClientCommand(client, "redirect ze.unloze.com:27015");
+    }
+}
+
+stock bool IsValidClient(int client)
+{
+    if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
+        return true;
+    return false;
+}
diff --git a/simulate_ze_server/systemctl/move_to_simulate.service b/simulate_ze_server/systemctl/move_to_simulate.service
new file mode 100644
index 00000000..00e6e4f5
--- /dev/null
+++ b/simulate_ze_server/systemctl/move_to_simulate.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Moves info from ze to the simulate server
+
+[Service]
+Type=simple
+User=gameservers
+WorkingDirectory=/home/gameservers/udp_director
+ExecStart=/home/gameservers/udp_director/move_text_to_simulate_server.py
+Restart=always
+RestartSec=10s