1#include "local_server.h"
2
3#include <base/fs.h>
4#include <base/mem.h>
5#include <base/secure.h>
6#include <base/str.h>
7
8#include <game/client/gameclient.h>
9#include <game/localization.h>
10
11#if defined(CONF_PLATFORM_ANDROID)
12#include <android/android_main.h>
13#else
14#include <base/process.h>
15#endif
16
17bool CLocalServer::RunServer(const std::vector<const char *> &vpArguments)
18{
19 secure_random_password(buffer: m_aRconPassword, length: sizeof(m_aRconPassword), pw_length: 16);
20 char aAuthCommand[64 + sizeof(m_aRconPassword)];
21 str_format(buffer: aAuthCommand, buffer_size: sizeof(aAuthCommand), format: "auth_add %s admin %s", DEFAULT_SAVED_RCON_USER, m_aRconPassword);
22
23 std::vector<const char *> vpArgumentsWithAuth = vpArguments;
24 vpArgumentsWithAuth.push_back(x: aAuthCommand);
25
26#if defined(CONF_PLATFORM_ANDROID)
27 if(StartAndroidServer(vpArgumentsWithAuth.data(), vpArgumentsWithAuth.size()))
28 {
29 GameClient()->m_Menus.ForceRefreshLanPage();
30 return true;
31 }
32 else
33 {
34 Client()->AddWarning(SWarning(Localize("Server could not be started. Make sure to grant the notification permission in the app settings so the server can run in the background.")));
35 mem_zero(m_aRconPassword, sizeof(m_aRconPassword));
36 return false;
37 }
38#else
39 char aBuf[IO_MAX_PATH_LENGTH];
40 Storage()->GetBinaryPath(PLAT_SERVER_EXEC, pBuffer: aBuf, BufferSize: sizeof(aBuf));
41#if defined(CONF_PLATFORM_MACOS)
42 if(!fs_is_file(aBuf))
43 {
44 fs_parent_dir(aBuf);
45 str_append(aBuf, "/../../../DDNet-Server.app/Contents/MacOS/");
46 str_append(aBuf, PLAT_SERVER_EXEC);
47 }
48#endif
49 // No / in binary path means to search in $PATH, so it is expected that the file can't be opened. Just try executing anyway.
50 if(str_find(haystack: aBuf, needle: "/") == nullptr || fs_is_file(path: aBuf))
51 {
52 m_Process = process_execute(file: aBuf, window_state: EShellExecuteWindowState::BACKGROUND, arguments: vpArgumentsWithAuth.data(), num_arguments: vpArgumentsWithAuth.size());
53 if(m_Process != INVALID_PROCESS)
54 {
55 GameClient()->m_Menus.ForceRefreshLanPage();
56 return true;
57 }
58 else
59 {
60 Client()->AddWarning(Warning: SWarning(Localize(pStr: "Server could not be started")));
61 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
62 return false;
63 }
64 }
65 else
66 {
67 Client()->AddWarning(Warning: SWarning(Localize(pStr: "Server executable not found, can't run server")));
68 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
69 return false;
70 }
71#endif
72}
73
74void CLocalServer::KillServer()
75{
76#if defined(CONF_PLATFORM_ANDROID)
77 ExecuteAndroidServerCommand("shutdown");
78 GameClient()->m_Menus.ForceRefreshLanPage();
79#else
80 if(m_Process != INVALID_PROCESS && process_kill(process: m_Process))
81 {
82 m_Process = INVALID_PROCESS;
83 GameClient()->m_Menus.ForceRefreshLanPage();
84 }
85#endif
86 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
87}
88
89bool CLocalServer::IsServerRunning()
90{
91#if defined(CONF_PLATFORM_ANDROID)
92 return IsAndroidServerRunning();
93#else
94 if(m_Process != INVALID_PROCESS && !process_is_alive(process: m_Process))
95 {
96 KillServer();
97 }
98 return m_Process != INVALID_PROCESS;
99#endif
100}
101
102void CLocalServer::RconAuthIfPossible()
103{
104 if(!IsServerRunning() ||
105 m_aRconPassword[0] == '\0' ||
106 !net_addr_is_local(addr: &Client()->ServerAddress()))
107 {
108 return;
109 }
110 Client()->RconAuth(pUsername: DEFAULT_SAVED_RCON_USER, pPassword: m_aRconPassword, Dummy: g_Config.m_ClDummy);
111}
112