diff options
Diffstat (limited to 'game')
| -rw-r--r-- | game/client/gui/bother.cc | 8 | ||||
| -rw-r--r-- | game/client/gui/bother.hh | 4 | ||||
| -rw-r--r-- | game/client/gui/play_menu.cc | 92 | ||||
| -rw-r--r-- | game/client/gui/window_title.cc | 11 | ||||
| -rw-r--r-- | game/client/session.cc | 6 | ||||
| -rw-r--r-- | game/server/sessions.cc | 24 | ||||
| -rw-r--r-- | game/server/status.cc | 6 | ||||
| -rw-r--r-- | game/shared/protocol.cc | 20 | ||||
| -rw-r--r-- | game/shared/protocol.hh | 11 |
9 files changed, 124 insertions, 58 deletions
diff --git a/game/client/gui/bother.cc b/game/client/gui/bother.cc index 1bb7097..e87b9ff 100644 --- a/game/client/gui/bother.cc +++ b/game/client/gui/bother.cc @@ -2,6 +2,8 @@ #include "client/gui/bother.hh"
+#include "core/version.hh"
+
#include "shared/protocol.hh"
#include "client/globals.hh"
@@ -29,10 +31,12 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) gui::BotherResponseEvent event;
event.identity = identity;
event.is_server_unreachable = false;
- event.protocol_version = packet.version;
event.num_players = packet.num_players;
event.max_players = packet.max_players;
event.motd = packet.motd;
+ event.game_version_major = packet.game_version_major;
+ event.game_version_minor = packet.game_version_minor;
+ event.game_version_patch = packet.game_version_patch;
globals::dispatcher.trigger(event);
enet_peer_disconnect(packet.peer, protocol::CHANNEL);
@@ -89,7 +93,7 @@ void gui::bother::update_late(void) if(0 < enet_host_service(bother_host, &enet_event, 0)) {
if(enet_event.type == ENET_EVENT_TYPE_CONNECT) {
protocol::StatusRequest packet;
- packet.version = protocol::VERSION;
+ packet.game_version_major = version::major;
protocol::send(enet_event.peer, protocol::encode(packet));
return;
}
diff --git a/game/client/gui/bother.hh b/game/client/gui/bother.hh index fc5bab4..75e56d1 100644 --- a/game/client/gui/bother.hh +++ b/game/client/gui/bother.hh @@ -5,9 +5,11 @@ namespace gui struct BotherResponseEvent final {
unsigned int identity;
bool is_server_unreachable;
- std::uint32_t protocol_version;
std::uint16_t num_players;
std::uint16_t max_players;
+ std::uint32_t game_version_major;
+ std::uint32_t game_version_minor;
+ std::uint32_t game_version_patch;
std::string motd;
};
} // namespace gui
diff --git a/game/client/gui/play_menu.cc b/game/client/gui/play_menu.cc index 5b9887e..ad85141 100644 --- a/game/client/gui/play_menu.cc +++ b/game/client/gui/play_menu.cc @@ -10,6 +10,8 @@ #include "core/utils/string.hh"
+#include "core/version.hh"
+
#include "shared/protocol.hh"
#include "client/gui/bother.hh"
@@ -25,9 +27,8 @@ constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration;
constexpr static std::string_view DEFAULT_SERVER_NAME = "Voxelius Server";
constexpr static std::string_view SERVERS_TXT = "servers.txt";
-constexpr static std::string_view WARNING_TOAST = "[!]";
-constexpr static std::size_t MAX_SERVER_ITEM_NAME = 24;
+constexpr static std::size_t MAX_SERVER_ITEM_NAME = 18;
enum class item_status : unsigned int {
UNKNOWN = 0x0000U,
@@ -43,10 +44,12 @@ struct ServerStatusItem final { std::uint16_t port;
// Things pulled from bother events
- std::uint32_t protocol_version;
std::uint16_t num_players;
std::uint16_t max_players;
std::string motd;
+ std::uint16_t game_version_major;
+ std::uint16_t game_version_minor;
+ std::uint16_t game_version_patch;
// Unique identifier that monotonically
// grows with each new server added and
@@ -69,9 +72,6 @@ static std::string str_status_init; static std::string str_status_ping;
static std::string str_status_fail;
-static std::string str_outdated_client;
-static std::string str_outdated_server;
-
static std::string input_itemname;
static std::string input_hostname;
static std::string input_password;
@@ -106,11 +106,13 @@ static void add_new_server(void) {
auto item = new ServerStatusItem();
item->port = protocol::PORT;
- item->protocol_version = protocol::VERSION;
item->max_players = UINT16_MAX;
item->num_players = UINT16_MAX;
item->identity = next_identity;
item->status = item_status::UNKNOWN;
+ item->game_version_major = 0U;
+ item->game_version_minor = 0U;
+ item->game_version_patch = 0U;
next_identity += 1U;
@@ -202,9 +204,6 @@ static void on_language_set(const gui::LanguageSetEvent& event) str_status_init = gui::language::resolve("play_menu.status.init");
str_status_ping = gui::language::resolve("play_menu.status.ping");
str_status_fail = gui::language::resolve("play_menu.status.fail");
-
- str_outdated_client = gui::language::resolve("play_menu.outdated_client");
- str_outdated_server = gui::language::resolve("play_menu.outdated_server");
}
static void on_bother_response(const gui::BotherResponseEvent& event)
@@ -212,18 +211,22 @@ static void on_bother_response(const gui::BotherResponseEvent& event) for(auto item : servers_deque) {
if(item->identity == event.identity) {
if(event.is_server_unreachable) {
- item->protocol_version = 0U;
item->num_players = UINT16_MAX;
item->max_players = UINT16_MAX;
item->motd = str_status_fail;
item->status = item_status::FAILURE;
+ item->game_version_major = 0U;
+ item->game_version_minor = 0U;
+ item->game_version_patch = 0U;
}
else {
- item->protocol_version = event.protocol_version;
item->num_players = event.num_players;
item->max_players = event.max_players;
item->motd = event.motd;
item->status = item_status::REACHED;
+ item->game_version_major = event.game_version_major;
+ item->game_version_minor = event.game_version_minor;
+ item->game_version_patch = event.game_version_patch;
}
break;
@@ -264,30 +267,50 @@ static void layout_server_item(ServerStatusItem* item) if(item->status == item_status::REACHED) {
auto stats = std::format("{}/{}", item->num_players, item->max_players);
- auto stats_width = ImGui::CalcTextSize(stats.c_str(), stats.c_str() + stats.size()).x;
- auto stats_pos = ImVec2(cursor.x + item_width - stats_width - padding.x, cursor.y + padding.y);
+ auto stats_size = ImGui::CalcTextSize(stats.c_str(), stats.c_str() + stats.size());
+ auto stats_pos = ImVec2(cursor.x + item_width - stats_size.x - padding.x, cursor.y + padding.y);
draw_list->AddText(stats_pos, ImGui::GetColorU32(ImGuiCol_TextDisabled), stats.c_str(), stats.c_str() + stats.size());
- if(item->protocol_version != protocol::VERSION) {
- auto warning_size = ImGui::CalcTextSize(WARNING_TOAST.data(), WARNING_TOAST.data() + WARNING_TOAST.size());
- auto warning_pos = ImVec2(stats_pos.x - warning_size.x - padding.x - 4.0f * globals::gui_scale, cursor.y + padding.y);
- auto warning_end = ImVec2(warning_pos.x + warning_size.x, warning_pos.y + warning_size.y);
- draw_list->AddText(warning_pos, ImGui::GetColorU32(ImGuiCol_DragDropTarget), WARNING_TOAST.data(),
- WARNING_TOAST.data() + WARNING_TOAST.size());
+ auto major_version_mismatch = item->game_version_major != version::major;
+ auto minor_version_mismatch = item->game_version_minor != version::minor;
+ auto patch_version_mismatch = item->game_version_patch != version::patch;
- if(ImGui::IsMouseHoveringRect(warning_pos, warning_end)) {
- ImGui::BeginTooltip();
+ ImU32 version_color;
- if(item->protocol_version < protocol::VERSION) {
- ImGui::TextUnformatted(str_outdated_server.c_str(), str_outdated_server.c_str() + str_outdated_server.size());
- }
- else {
- ImGui::TextUnformatted(str_outdated_client.c_str(), str_outdated_client.c_str() + str_outdated_client.size());
- }
+ if(major_version_mismatch || minor_version_mismatch || patch_version_mismatch) {
+ version_color = ImGui::GetColorU32(major_version_mismatch ? ImGuiCol_PlotLinesHovered : ImGuiCol_DragDropTarget);
+ }
+ else {
+ version_color = ImGui::GetColorU32(ImGuiCol_PlotHistogram);
+ }
- ImGui::EndTooltip();
- }
+ ImGui::PushFont(globals::font_unscii8, 4.0f);
+
+ std::string version_toast;
+
+ if(item->game_version_major < 16U) {
+ // Pre v16.x.x servers didn't send minor and patch versions
+ // and also used a different versioning scheme; post v16 the
+ // major version became the protocol version and the semver lost the tweak part
+ version_toast = std::string("0.0.1");
+ }
+ else {
+ version_toast = std::format("{}.{}.{}", item->game_version_major, item->game_version_minor, item->game_version_patch);
}
+
+ auto version_size = ImGui::CalcTextSize(version_toast.c_str(), version_toast.c_str() + version_toast.size());
+ auto version_pos = ImVec2(stats_pos.x - version_size.x - padding.x - 4.0f * globals::gui_scale,
+ cursor.y + padding.y + 0.5f * (stats_size.y - version_size.y));
+ auto version_end = ImVec2(version_pos.x + version_size.x, version_pos.y + version_size.y);
+
+ auto outline_pos = ImVec2(version_pos.x - 2U * globals::gui_scale, version_pos.y - 2U * globals::gui_scale);
+ auto outline_end = ImVec2(version_end.x + 2U * globals::gui_scale, version_end.y + 2U * globals::gui_scale);
+ auto outline_thickness = math::max<float>(1.0f, 0.5f * static_cast<float>(globals::gui_scale));
+
+ draw_list->AddRect(outline_pos, outline_end, version_color, 0.0f, 0, outline_thickness);
+ draw_list->AddText(version_pos, version_color, version_toast.c_str(), version_toast.c_str() + version_toast.size());
+
+ ImGui::PopFont();
}
ImU32 motd_color = {};
@@ -460,11 +483,13 @@ void gui::play_menu::init(void) auto item = new ServerStatusItem();
item->port = protocol::PORT;
- item->protocol_version = protocol::VERSION;
item->max_players = UINT16_MAX;
item->num_players = UINT16_MAX;
item->identity = next_identity;
item->status = item_status::UNKNOWN;
+ item->game_version_major = version::major;
+ item->game_version_minor = version::minor;
+ item->game_version_patch = version::patch;
next_identity += 1U;
@@ -543,6 +568,11 @@ void gui::play_menu::layout(void) ImGui::EndTabItem();
}
+ if(ImGui::BeginTabItem("debug###play_menu.debug.child")) {
+ ImGui::ShowStyleEditor();
+ ImGui::EndTabItem();
+ }
+
ImGui::EndTabBar();
}
diff --git a/game/client/gui/window_title.cc b/game/client/gui/window_title.cc index 6e2387c..2f96205 100644 --- a/game/client/gui/window_title.cc +++ b/game/client/gui/window_title.cc @@ -10,14 +10,5 @@ void gui::window_title::update(void)
{
- std::string title;
-
- if(globals::sound_ctx && globals::sound_dev) {
- title = std::format("Voxelius {}: {}", version::semver, splash::get());
- }
- else {
- title = std::format("Voxelius {}: {} [NOSOUND]", version::semver, splash::get());
- }
-
- glfwSetWindowTitle(globals::window, title.c_str());
+ glfwSetWindowTitle(globals::window, std::format("Voxelius {}: {}", version::semver, splash::get()).c_str());
}
diff --git a/game/client/session.cc b/game/client/session.cc index 907b789..ce3d616 100644 --- a/game/client/session.cc +++ b/game/client/session.cc @@ -6,6 +6,8 @@ #include "core/math/crc64.hh"
+#include "core/version.hh"
+
#include "shared/entity/head.hh"
#include "shared/entity/player.hh"
#include "shared/entity/transform.hh"
@@ -284,11 +286,13 @@ void session::disconnect(std::string_view reason) void session::send_login_request(void)
{
protocol::LoginRequest packet;
- packet.version = protocol::VERSION;
+ packet.game_version_major = version::major;
packet.voxel_registry_checksum = world::voxel_registry::get_checksum();
packet.item_registry_checksum = world::item_registry::get_checksum();
packet.password_hash = server_password_hash;
packet.username = client_game::username.get();
+ packet.game_version_minor = version::minor;
+ packet.game_version_patch = version::patch;
protocol::send(session::peer, protocol::encode(packet));
diff --git a/game/server/sessions.cc b/game/server/sessions.cc index 2ab9d74..6758648 100644 --- a/game/server/sessions.cc +++ b/game/server/sessions.cc @@ -12,6 +12,8 @@ #include "core/utils/string.hh"
+#include "core/version.hh"
+
#include "shared/entity/factory.hh"
#include "shared/entity/head.hh"
#include "shared/entity/player.hh"
@@ -42,6 +44,8 @@ private: config::Unsigned sessions::max_players(8U, 1U, 128U);
unsigned int sessions::num_players = 0U;
+static config::Boolean strict_version_matching(true);
+
static emhash8::HashMap<std::string, Session*> username_map;
static emhash8::HashMap<std::uint64_t, Session*> identity_map;
static std::vector<DimensionListener> dimension_listeners;
@@ -49,20 +53,36 @@ static std::vector<Session> sessions_vector; static void on_login_request_packet(const protocol::LoginRequest& packet)
{
- if(packet.version > protocol::VERSION) {
+ if(packet.game_version_major > version::major) {
protocol::Disconnect response;
response.reason = "protocol.outdated_server";
protocol::send(packet.peer, protocol::encode(response));
return;
}
- if(packet.version < protocol::VERSION) {
+ if(packet.game_version_minor < version::minor) {
protocol::Disconnect response;
response.reason = "protocol.outdated_client";
protocol::send(packet.peer, protocol::encode(response));
return;
}
+ if(strict_version_matching.get_value()) {
+ if(packet.game_version_minor > version::minor || packet.game_version_patch > version::patch) {
+ protocol::Disconnect response;
+ response.reason = "protocol.outdated_server";
+ protocol::send(packet.peer, protocol::encode(response));
+ return;
+ }
+
+ if(packet.game_version_minor < version::minor || packet.game_version_patch < version::patch) {
+ protocol::Disconnect response;
+ response.reason = "protocol.outdated_client";
+ protocol::send(packet.peer, protocol::encode(response));
+ return;
+ }
+ }
+
// FIXME: calculate voxel registry checksum ahead of time
// instead of figuring it out every time a new player connects
if(packet.voxel_registry_checksum != world::voxel_registry::get_checksum()) {
diff --git a/game/server/status.cc b/game/server/status.cc index ba1d59d..0edd0a0 100644 --- a/game/server/status.cc +++ b/game/server/status.cc @@ -4,6 +4,8 @@ #include "core/config/number.hh"
+#include "core/version.hh"
+
#include "shared/protocol.hh"
#include "shared/splash.hh"
@@ -13,10 +15,12 @@ static void on_status_request_packet(const protocol::StatusRequest& packet)
{
protocol::StatusResponse response;
- response.version = protocol::VERSION;
+ response.game_version_major = version::major;
response.max_players = sessions::max_players.get_value();
response.num_players = sessions::num_players;
response.motd = splash::get();
+ response.game_version_minor = version::minor;
+ response.game_version_patch = version::patch;
protocol::send(packet.peer, protocol::encode(response));
}
diff --git a/game/shared/protocol.cc b/game/shared/protocol.cc index 576502e..7115807 100644 --- a/game/shared/protocol.cc +++ b/game/shared/protocol.cc @@ -21,7 +21,7 @@ ENetPacket* protocol::encode(const protocol::StatusRequest& packet, enet_uint32 {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::StatusRequest::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
return write_buffer.to_packet(flags);
}
@@ -29,10 +29,12 @@ ENetPacket* protocol::encode(const protocol::StatusResponse& packet, enet_uint32 {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::StatusResponse::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
write_buffer.write<std::uint16_t>(packet.max_players);
write_buffer.write<std::uint16_t>(packet.num_players);
write_buffer.write<std::string_view>(packet.motd);
+ write_buffer.write<std::uint32_t>(packet.game_version_minor);
+ write_buffer.write<std::uint32_t>(packet.game_version_patch);
return write_buffer.to_packet(flags);
}
@@ -40,11 +42,13 @@ ENetPacket* protocol::encode(const protocol::LoginRequest& packet, enet_uint32 f {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::LoginRequest::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
write_buffer.write<std::uint64_t>(packet.voxel_registry_checksum);
write_buffer.write<std::uint64_t>(packet.item_registry_checksum);
write_buffer.write<std::uint64_t>(packet.password_hash);
write_buffer.write<std::string_view>(packet.username.substr(0, protocol::MAX_USERNAME));
+ write_buffer.write<std::uint32_t>(packet.game_version_minor);
+ write_buffer.write<std::uint32_t>(packet.game_version_patch);
return write_buffer.to_packet(flags);
}
@@ -268,26 +272,30 @@ void protocol::decode(entt::dispatcher& dispatcher, const ENetPacket* packet, EN switch(id) {
case protocol::StatusRequest::ID:
status_request.peer = peer;
- status_request.version = read_buffer.read<std::uint32_t>();
+ status_request.game_version_major = read_buffer.read<std::uint32_t>();
dispatcher.trigger(status_request);
break;
case protocol::StatusResponse::ID:
status_response.peer = peer;
- status_response.version = read_buffer.read<std::uint32_t>();
+ status_response.game_version_major = read_buffer.read<std::uint32_t>();
status_response.max_players = read_buffer.read<std::uint16_t>();
status_response.num_players = read_buffer.read<std::uint16_t>();
status_response.motd = read_buffer.read<std::string>();
+ status_response.game_version_minor = read_buffer.read<std::uint32_t>();
+ status_response.game_version_patch = read_buffer.read<std::uint32_t>();
dispatcher.trigger(status_response);
break;
case protocol::LoginRequest::ID:
login_request.peer = peer;
- login_request.version = read_buffer.read<std::uint32_t>();
+ login_request.game_version_major = read_buffer.read<std::uint32_t>();
login_request.voxel_registry_checksum = read_buffer.read<std::uint64_t>();
login_request.item_registry_checksum = read_buffer.read<std::uint64_t>();
login_request.password_hash = read_buffer.read<std::uint64_t>();
login_request.username = read_buffer.read<std::string>();
+ login_request.game_version_minor = read_buffer.read<std::uint32_t>();
+ login_request.game_version_patch = read_buffer.read<std::uint32_t>();
dispatcher.trigger(login_request);
break;
diff --git a/game/shared/protocol.hh b/game/shared/protocol.hh index 3133275..f0bdff6 100644 --- a/game/shared/protocol.hh +++ b/game/shared/protocol.hh @@ -14,7 +14,6 @@ constexpr static std::size_t MAX_USERNAME = 64; constexpr static std::size_t MAX_SOUNDNAME = 1024;
constexpr static std::uint16_t TICKRATE = 60;
constexpr static std::uint16_t PORT = 43103;
-constexpr static std::uint32_t VERSION = 15;
constexpr static std::uint8_t CHANNEL = 0;
} // namespace protocol
@@ -107,22 +106,26 @@ ENetPacket* make_dimension_info(const world::Dimension* dimension); } // namespace protocol::utils
struct protocol::StatusRequest final : public protocol::Base<0x0000> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
};
struct protocol::StatusResponse final : public protocol::Base<0x0001> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
std::uint16_t max_players;
std::uint16_t num_players;
std::string motd;
+ std::uint32_t game_version_minor { UINT32_MAX }; // added in v16.x.x
+ std::uint32_t game_version_patch { UINT32_MAX };
};
struct protocol::LoginRequest final : public protocol::Base<0x0002> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
std::uint64_t voxel_registry_checksum;
std::uint64_t item_registry_checksum;
std::uint64_t password_hash;
std::string username;
+ std::uint32_t game_version_minor; // added in v16.x.x
+ std::uint32_t game_version_patch;
};
struct protocol::LoginResponse final : public protocol::Base<0x0003> {
|
