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) && fs_parent_dir(aBuf) == 0)
43 {
44 str_append(aBuf, "/../../../DDNet-Server.app/Contents/MacOS/");
45 str_append(aBuf, PLAT_SERVER_EXEC);
46 }
47#endif
48 // No / in binary path means to search in $PATH, so it is expected that the file can't be opened. Just try executing anyway.
49 if(str_find(haystack: aBuf, needle: "/") == nullptr || fs_is_file(path: aBuf))
50 {
51 m_Process = process_execute(file: aBuf, window_state: EShellExecuteWindowState::BACKGROUND, arguments: vpArgumentsWithAuth.data(), num_arguments: vpArgumentsWithAuth.size());
52 if(m_Process != INVALID_PROCESS)
53 {
54 GameClient()->m_Menus.ForceRefreshLanPage();
55 return true;
56 }
57 else
58 {
59 Client()->AddWarning(Warning: SWarning(Localize(pStr: "Server could not be started")));
60 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
61 return false;
62 }
63 }
64 else
65 {
66 Client()->AddWarning(Warning: SWarning(Localize(pStr: "Server executable not found, can't run server")));
67 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
68 return false;
69 }
70#endif
71}
72
73void CLocalServer::KillServer()
74{
75#if defined(CONF_PLATFORM_ANDROID)
76 ExecuteAndroidServerCommand("shutdown");
77 GameClient()->m_Menus.ForceRefreshLanPage();
78#else
79 if(m_Process != INVALID_PROCESS && process_kill(process: m_Process))
80 {
81 m_Process = INVALID_PROCESS;
82 GameClient()->m_Menus.ForceRefreshLanPage();
83 }
84#endif
85 mem_zero(block: m_aRconPassword, size: sizeof(m_aRconPassword));
86}
87
88bool CLocalServer::IsServerRunning()
89{
90#if defined(CONF_PLATFORM_ANDROID)
91 return IsAndroidServerRunning();
92#else
93 if(m_Process != INVALID_PROCESS && !process_is_alive(process: m_Process))
94 {
95 KillServer();
96 }
97 return m_Process != INVALID_PROCESS;
98#endif
99}
100
101void CLocalServer::RconAuthIfPossible()
102{
103 if(!IsServerRunning() ||
104 m_aRconPassword[0] == '\0' ||
105 !net_addr_is_local(addr: &Client()->ServerAddress()))
106 {
107 return;
108 }
109 Client()->RconAuth(pUsername: DEFAULT_SAVED_RCON_USER, pPassword: m_aRconPassword, Dummy: g_Config.m_ClDummy);
110}
111