Skip to content

Commit

Permalink
Host MANY servers with a single config var.
Browse files Browse the repository at this point in the history
Now you don't have to create separate configs for your 4 casual and 4 ranked servers - just one is enough for all 8.
  • Loading branch information
geneotech committed Jun 25, 2024
1 parent 11ff6cd commit 2db0ecd
Show file tree
Hide file tree
Showing 26 changed files with 485 additions and 196 deletions.
13 changes: 12 additions & 1 deletion hypersomnia/default_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@
"webrtc_udp_mux": false,
"webrtc_port_range_begin": 9000,
"webrtc_port_range_end": 9020,
"server_name": "${MY_NICKNAME}'s server",
"server_name": "", // Will be "Nickname's server" by default.
"notified_server_list": "masterserver.hypersomnia.xyz:8430",
"suppress_new_community_server_webhook": false,
"allow_nat_traversal": true,
Expand Down Expand Up @@ -363,6 +363,17 @@
"sync_all_external_arenas_on_startup": false
},

/*
For spawning multiple server instances within a single process.
Applies only to the dedicated server.

If both 0, will just create a single instance according to the config.
For casual server instances, "ranked" fields will just be reset.
*/

"num_ranked_servers": 0,
"num_casual_servers": 0,

// Private vars aren't known to any clients.

"server_private": {
Expand Down
4 changes: 4 additions & 0 deletions src/application/config_json_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ struct config_json_table {
damage_indication_settings damage_indication;

server_listen_input server_start;

uint16_t num_ranked_servers = 0;
uint16_t num_casual_servers = 0;

server_vars server;
server_private_vars server_private;
augs::dedicated_server_input dedicated_server;
Expand Down
1 change: 0 additions & 1 deletion src/application/gui/browse_servers_gui.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once
#include "augs/math/vec2.h"
#include "augs/misc/imgui/standard_window_mixin.h"
#include "application/setups/server/server_instance_type.h"
#include "augs/misc/timing/timer.h"
#include "3rdparty/yojimbo/netcode/netcode.h"
#include "augs/network/netcode_sockets.h"
Expand Down
8 changes: 5 additions & 3 deletions src/application/gui/start_server_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ as well as to test your skills in a laggy environment.

// auto& scope_cfg = into;

input_text("Server name", into_vars.server_name);
input_text_with_hint("Server name", "(default)", into_vars.server_name);

if (perform_arena_chooser(into_vars.arena)) {
into_vars.game_mode = "";
Expand Down Expand Up @@ -239,7 +239,7 @@ as well as to test your skills in a laggy environment.
show_help = true;
}

enum_radio(instance_type, true);
enum_radio(type, true);
ImGui::Separator();
}

Expand All @@ -255,7 +255,9 @@ as well as to test your skills in a laggy environment.
ImGui::Separator();

{
auto scope = maybe_disabled_cols({}, !is_nickname_valid_characters(into_vars.server_name));
const bool valid = into_vars.server_name.empty() || is_nickname_valid_characters(into_vars.server_name);

auto scope = maybe_disabled_cols({}, !valid);

if (ImGui::Button("Launch!")) {
result = true;
Expand Down
4 changes: 2 additions & 2 deletions src/application/gui/start_server_gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include "application/setups/server/server_listen_input.h"
#include "augs/math/vec2.h"
#include "augs/misc/imgui/standard_window_mixin.h"
#include "application/setups/server/server_instance_type.h"
#include "application/setups/server/dedicated_or_integrated.h"
#include "augs/misc/timing/timer.h"

struct server_vars;
Expand All @@ -16,7 +16,7 @@ class start_server_gui_state : public standard_window_mixin<start_server_gui_sta

port_type previous_chosen_port = DEFAULT_GAME_PORT_V;

server_instance_type instance_type = server_instance_type::INTEGRATED;
dedicated_or_integrated type = dedicated_or_integrated::INTEGRATED;

bool is_steam_client = false;
bool show_help = false;
Expand Down
116 changes: 116 additions & 0 deletions src/application/main/dedicated_server_worker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#pragma once

struct dedicated_server_worker_input {
std::unique_ptr<server_setup> server_ptr;
augs::path_type appimage_path;
self_update_settings self_update;
std::function<void(server_vars)> write_vars_to_disk;
std::string instance_label;
std::string instance_log_label;
};

inline work_result dedicated_server_worker(
const dedicated_server_worker_input& in,
std::function<bool()> should_interrupt
) {
auto& server = *in.server_ptr;

const auto run_result = [&]() {
std::future<self_update_result> availability_check;

server_network_info server_stats;
network_profiler network_performance;

while (server.is_running()) {
const auto zoom = 1.f;

if (should_interrupt()) {
LOG("Interrupt was requested.");
return work_result::SUCCESS;
}

server.advance(
{
vec2i(),
input_settings(),
zoom,
nat_detection_result(),
network_performance,
server_stats
},
solver_callbacks()
);

if (server.should_write_vars_to_disk_once()) {
if (in.write_vars_to_disk != nullptr) {
in.write_vars_to_disk(server.get_current_vars());
}
}

if (server.should_check_for_updates_once()) {
LOG("Launching an async check for updates.");

auto settings = in.self_update;

/* Give it a little longer, it's async anyway. */
settings.update_connection_timeout_secs = 10;
auto appimage_path = in.appimage_path;

availability_check = launch_async(
[appimage_path, settings]() {
const bool only_check_update_availability_and_quit = true;

return check_and_apply_updates(
appimage_path,
only_check_update_availability_and_quit,
settings
);
}
);
}

if (valid_and_is_ready(availability_check)) {
LOG("Finished the async check for updates.");

using update_result = self_update_result_type;

const auto result = availability_check.get();

if (result.type == update_result::UPDATE_AVAILABLE) {
return work_result::RELAUNCH_AND_UPDATE_DEDICATED_SERVER;
}
else {
LOG("The dedicated server is up to date.");
}
}

server.sleep_until_next_tick();
}

if (server.server_restart_requested()) {
return work_result::RELAUNCH_DEDICATED_SERVER;
}

return work_result::SUCCESS;
}();

LOG("Quitting %x server instance with: %x", in.instance_label, ::describe_work_result(run_result));

return run_result;
}

inline auto make_server_worker(
dedicated_server_worker_input&& in,
std::function<bool()> should_interrupt,
std::function<void(work_result)> on_instance_exit
) {
return [
in_moved = dedicated_server_worker_input(std::move(in)),
should_interrupt,
on_instance_exit
]() {
LOG_THREAD_PREFFIX() = in_moved.instance_log_label;

on_instance_exit(dedicated_server_worker(in_moved, should_interrupt));
};
}
23 changes: 14 additions & 9 deletions src/application/main/self_updater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ namespace fs = std::filesystem;
self_update_result check_and_apply_updates(
const augs::path_type& current_appimage_path,
const bool only_check_availability_and_quit,
const augs::image* imgui_atlas_image,
const self_update_settings& http_settings,
augs::window_settings window_settings,
bool headless
const augs::image* imgui_atlas_image,
std::optional<augs::window_settings> window_settings
) {
(void)window_settings;
(void)imgui_atlas_image;

bool headless = !window_settings.has_value();

#if HEADLESS
headless = true;
#else
Expand Down Expand Up @@ -221,9 +222,11 @@ self_update_result check_and_apply_updates(

const auto window_size = vec2i(600, line_height * num_lines);

window_settings.size = window_size;
window_settings.fullscreen = false;
window_settings.border = false;
if (window_settings) {
window_settings->size = window_size;
window_settings->fullscreen = false;
window_settings->border = false;
}

std::optional<augs::window> window;
std::optional<augs::graphics::renderer_backend> renderer_backend;
Expand All @@ -239,11 +242,13 @@ self_update_result check_and_apply_updates(
#if !HEADLESS
else {
try {
window.emplace(window_settings);
ensure(window_settings.has_value());

window.emplace(*window_settings);
const auto disp = window->get_display();

window_settings.position = vec2i { disp.w / 2 - window_size.x / 2, disp.h / 2 - window_size.y / 2 };
window->apply(window_settings);
window_settings->position = vec2i { disp.w / 2 - window_size.x / 2, disp.h / 2 - window_size.y / 2 };
window->apply(*window_settings);

renderer_backend.emplace();

Expand Down
5 changes: 2 additions & 3 deletions src/application/main/self_updater.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ namespace augs {
self_update_result check_and_apply_updates(
const augs::path_type& current_appimage_path,
bool only_check_availability_and_quit,
const augs::image* imgui_atlas_image,
const self_update_settings& settings,
augs::window_settings window_settings,
bool headless
const augs::image* imgui_atlas_image = nullptr,
std::optional<augs::window_settings> window_settings = std::nullopt
);
5 changes: 5 additions & 0 deletions src/application/network/client_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class client_adapter {
std::array<uint8_t, yojimbo::KeyBytes> privateKey = {};
game_connection_config connection_config;
GameAdapter adapter;
yojimbo::DefaultAllocator yojimbo_allocator;
yojimbo::Client client;
client_auxiliary_command_callback_type auxiliary_command_callback;
send_packet_override_type send_packet_override;
Expand Down Expand Up @@ -52,6 +53,10 @@ class client_adapter {
receive_packet_override_type
);

auto& get_allocator() {
return yojimbo_allocator;
}

resolve_address_result connect(const client_connect_string&);

template <class H>
Expand Down
4 changes: 2 additions & 2 deletions src/application/network/client_adapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ bool client_adapter::send_payload(

auto allocate_block = [&](const std::size_t requested_size) {
allocated_size = requested_size;
allocated_block = (uint8_t*)YOJIMBO_ALLOCATE(yojimbo::GetDefaultAllocator(), requested_size);;
allocated_block = (uint8_t*)YOJIMBO_ALLOCATE(yojimbo_allocator, requested_size);;

return allocated_block;
};
Expand All @@ -125,7 +125,7 @@ bool client_adapter::send_payload(
);

if (translation_result && allocated_block != nullptr) {
new_message->AttachBlock( yojimbo::GetDefaultAllocator(), allocated_block, allocated_size );
new_message->AttachBlock( yojimbo_allocator, allocated_block, allocated_size );
send_it();
return true;
}
Expand Down
6 changes: 2 additions & 4 deletions src/application/network/net_message_readwrite.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
template <class T>
constexpr bool is_block_message_v = std::is_base_of_v<only_block_message, T>;

template <class F>
decltype(auto) replay_serialized_net_message(std::vector<std::byte>& demo_bytes, F&& callback) {
template <class A, class F>
decltype(auto) replay_serialized_net_message(A& allocator, std::vector<std::byte>& demo_bytes, F&& callback) {
using Id = type_in_list_id<server_message_variant>;

auto ar = augs::cref_memory_stream(demo_bytes);
Expand All @@ -19,8 +19,6 @@ decltype(auto) replay_serialized_net_message(std::vector<std::byte>& demo_bytes,
throw augs::stream_read_error("message type (%x) is out of range! It should be less than %x.", id.get_index(), Id::max_index_v);
}

auto& allocator = yojimbo::GetDefaultAllocator();

return id.dispatch(
[&](auto* e) -> decltype(auto) {
using net_message_type = remove_cptr<decltype(e)>;
Expand Down
4 changes: 2 additions & 2 deletions src/application/network/network_adapters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ server_adapter::server_adapter(
connection_config(in),
adapter(this),
server(
yojimbo::GetDefaultAllocator(),
yojimbo_allocator,
privateKey.data(),
yojimbo::Address(in.ip.c_str(), in.port),
connection_config,
Expand Down Expand Up @@ -274,7 +274,7 @@ client_adapter::client_adapter(
connection_config(),
adapter(nullptr),
client(
yojimbo::GetDefaultAllocator(),
yojimbo_allocator,
preferred_binding_port ? yojimbo::Address("0.0.0.0", *preferred_binding_port) : yojimbo::Address("0.0.0.0"),
connection_config,
adapter,
Expand Down
1 change: 1 addition & 0 deletions src/application/network/server_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class server_adapter {
std::array<uint8_t, yojimbo::KeyBytes> privateKey = {};
game_connection_config connection_config;
GameAdapter adapter;
yojimbo::DefaultAllocator yojimbo_allocator;
yojimbo::Server server;
auxiliary_command_callback_type auxiliary_command_callback;
send_packet_override_type send_packet_override;
Expand Down
2 changes: 1 addition & 1 deletion src/application/setups/client/client_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ void client_setup::demo_replay_server_messages_from(const demo_step& step) {
};

try {
const auto result = ::replay_serialized_net_message(serialized_bytes, replay_message);
const auto result = ::replay_serialized_net_message(adapter->get_allocator(), serialized_bytes, replay_message);

if (result == message_handler_result::ABORT_AND_DISCONNECT) {
disconnect();
Expand Down
10 changes: 10 additions & 0 deletions src/application/setups/server/dedicated_or_integrated.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

enum class dedicated_or_integrated {
// GEN INTROSPECTOR enum class dedicated_or_integrated
INTEGRATED,
DEDICATED,

COUNT
// END GEN INTROSPECTOR
};
10 changes: 0 additions & 10 deletions src/application/setups/server/server_instance_type.h

This file was deleted.

Loading

0 comments on commit 2db0ecd

Please sign in to comment.