Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline mode #424

Draft
wants to merge 4 commits into
base: minor
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct Settings {
General_Map,
General_AuthKey,
General_Private,
General_Offline,
General_Port,
General_MaxCars,
General_LogChat,
Expand Down
8 changes: 8 additions & 0 deletions src/LuaAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) {
beammp_lua_error("set invalid argument [2] expected boolean");
}
break;
case 8: // Offline play
if (NewValue.is<bool>()) {
Application::Settings.set(Settings::Key::General_Offline, NewValue.as<bool>());
beammp_info(std::string("Set `Offline` to ") + (Application::Settings.getAsBool(Settings::Key::General_Offline) ? "true" : "false"));
} else {
beammp_lua_error("set invalid argument [2] expected boolean");
}
break;
default:
beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this.");
break;
Expand Down
2 changes: 2 additions & 0 deletions src/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Settings::Settings() {
{ General_Map, std::string("/levels/gridmap_v2/info.json") },
{ General_AuthKey, std::string("") },
{ General_Private, true },
{ General_Offline, false },
{ General_Port, 30814 },
{ General_MaxCars, 1 },
{ General_LogChat, true },
Expand All @@ -47,6 +48,7 @@ Settings::Settings() {
{ { "General", "Map" }, { General_Map, READ_WRITE } },
{ { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } },
{ { "General", "Private" }, { General_Private, READ_ONLY } },
{ { "General", "Offline" }, { General_Offline, READ_ONLY } },
{ { "General", "Port" }, { General_Port, READ_ONLY } },
{ { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } },
{ { "General", "LogChat" }, { General_LogChat, READ_ONLY } },
Expand Down
5 changes: 5 additions & 0 deletions src/TConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ static constexpr std::string_view StrDebug = "Debug";
static constexpr std::string_view EnvStrDebug = "BEAMMP_DEBUG";
static constexpr std::string_view StrPrivate = "Private";
static constexpr std::string_view EnvStrPrivate = "BEAMMP_PRIVATE";
static constexpr std::string_view StrOffline = "Offline";
static constexpr std::string_view EnvStrOffline = "BEAMMP_OFFLINE";
static constexpr std::string_view StrPort = "Port";
static constexpr std::string_view EnvStrPort = "BEAMMP_PORT";
static constexpr std::string_view StrMaxCars = "MaxCars";
Expand Down Expand Up @@ -132,6 +134,7 @@ void TConfig::FlushToFile() {
SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log");
data["General"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug);
data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private);
data["General"][StrOffline.data()] = Application::Settings.getAsBool(Settings::Key::General_Offline);
SetComment(data["General"][StrInformationPacket.data()].comments(), " Whether to allow unconnected clients to get the public server information without joining");
data["General"][StrInformationPacket.data()] = Application::Settings.getAsBool(Settings::Key::General_InformationPacket);
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
Expand Down Expand Up @@ -246,6 +249,7 @@ void TConfig::ParseFromFile(std::string_view name) {
// Read into new Settings Singleton
TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug);
TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private);
TryReadValue(data, "General", StrOffline, EnvStrOffline, Settings::Key::General_Offline);
TryReadValue(data, "General", StrInformationPacket, EnvStrInformationPacket, Settings::Key::General_InformationPacket);
if (Env::Get(Env::Key::PROVIDER_PORT_ENV).has_value()) {
TryReadValue(data, "General", StrPort, Env::Get(Env::Key::PROVIDER_PORT_ENV).value(), Settings::Key::General_Port);
Expand Down Expand Up @@ -300,6 +304,7 @@ void TConfig::PrintDebug() {
}
beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false"));
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
beammp_debug(std::string(StrOffline) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Offline) ? "true" : "false"));
beammp_debug(std::string(StrInformationPacket) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false"));
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)));
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
Expand Down
7 changes: 6 additions & 1 deletion src/THeartbeatThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ void THeartbeatThread::operator()() {

nlohmann::json Doc;
bool Ok = false;

if (Application::Settings.getAsBool(Settings::Key::General_Offline)) {
beammp_warn("Server is in Offline Mode. Your server will not appear in the global server list. Direct connect will still work with LAN connections.");
return;
}
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
T = Http::POST(Url + Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } });

Expand Down Expand Up @@ -113,7 +118,6 @@ void THeartbeatThread::operator()() {
beammp_warn("Backend failed to respond to a heartbeat. Your server may temporarily disappear from the server list. This is not an error, and will likely resolve itself soon. Direct connect will still work.");
}
}

if (Ok && !isAuth && !Application::Settings.getAsBool(Settings::Key::General_Private)) {
if (Status == "2000") {
beammp_info(("Authenticated! " + Message));
Expand Down Expand Up @@ -145,6 +149,7 @@ std::string THeartbeatThread::GenerateCall() {
{ "port", std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)) },
{ "map", Application::Settings.getAsString(Settings::Key::General_Map) },
{ "private", Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false" },
{ "offline", Application::Settings.getAsBool(Settings::Key::General_Offline) ? "true" : "false" },
{ "version", Application::ServerVersionString() },
{ "clientversion", Application::ClientMinimumVersion().AsString() },
{ "name", Application::Settings.getAsString(Settings::Key::General_Name) },
Expand Down
53 changes: 31 additions & 22 deletions src/TNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,32 +362,41 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
return nullptr;
}

beammp_debug("Response from authentication backend: " + AuthResStr);
// If we are in "offline mode", we want to do something different for auth
if (Application::Settings.getAsBool(Settings::Key::General_Offline)) {
// skip auth altogether.
// this prevents players from getting kicked for not being logged in.

// TODO: authenticate using a different method here.

try {
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);

if (AuthRes["username"].is_string() && AuthRes["roles"].is_string()
&& AuthRes["guest"].is_boolean() && AuthRes["identifiers"].is_array()) {

Client->SetName(AuthRes["username"]);
Client->SetRoles(AuthRes["roles"]);
Client->SetIsGuest(AuthRes["guest"]);
for (const auto& ID : AuthRes["identifiers"]) {
auto Raw = std::string(ID);
auto SepIndex = Raw.find(':');
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
} else {
beammp_debug("Response from authentication backend: " + AuthResStr);

try {
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);

if (AuthRes["username"].is_string() && AuthRes["roles"].is_string()
&& AuthRes["guest"].is_boolean() && AuthRes["identifiers"].is_array()) {

Client->SetName(AuthRes["username"]);
Client->SetRoles(AuthRes["roles"]);
Client->SetIsGuest(AuthRes["guest"]);
for (const auto& ID : AuthRes["identifiers"]) {
auto Raw = std::string(ID);
auto SepIndex = Raw.find(':');
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
}
} else {
beammp_error("Invalid authentication data received from authentication backend");
ClientKick(*Client, "Invalid authentication data!");
return nullptr;
}
} else {
beammp_error("Invalid authentication data received from authentication backend");
ClientKick(*Client, "Invalid authentication data!");
} catch (const std::exception& e) {
beammp_errorf("Client sent invalid key. Error was: {}", e.what());
// TODO: we should really clarify that this was a backend response or parsing error
ClientKick(*Client, "Invalid key! Please restart your game.");
return nullptr;
}
} catch (const std::exception& e) {
beammp_errorf("Client sent invalid key. Error was: {}", e.what());
// TODO: we should really clarify that this was a backend response or parsing error
ClientKick(*Client, "Invalid key! Please restart your game.");
return nullptr;
}

beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
Expand Down