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