Skip to content

Commit

Permalink
display real ping in serverlist
Browse files Browse the repository at this point in the history
  • Loading branch information
siecvi committed Jul 27, 2024
1 parent 982b68a commit 07bc15e
Show file tree
Hide file tree
Showing 9 changed files with 726 additions and 42 deletions.
64 changes: 46 additions & 18 deletions Resources/Scripts/Gui/MainScreen/MainMenu.as
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ namespace spades {
MainScreenServerListLoadingView@ loadingView;
MainScreenServerListErrorView@ errorView;
bool loading = false, loaded = false;


ServerListModel@ currentServerListModel;
int serverListUpdateTimer = 5;

private ConfigItem cg_protocolVersion("cg_protocolVersion", "3");
private ConfigItem cg_lastQuickConnectHost("cg_lastQuickConnectHost", "127.0.0.1");
private ConfigItem cg_serverlistSort("cg_serverlistSort", "16385");
Expand All @@ -96,6 +99,17 @@ namespace spades {

float contentsLeft = (sw - contentsWidth) * 0.5F;
float footerPos = sh - 50.0F;
float headerPos = 246.0F;
float headerHeight = 24.0F;

// adjust based on screen width
float scaleF = Min(sw / maxContentsWidth, 1.0F);
float itemOffsetX = 2.0F;
float slotsOffsetX = 300.0F * scaleF;
float mapNameOffsetX = 370.0F * scaleF;
float gameModeOffsetX = 520.0F * scaleF;
float protocolOffsetX = 640.0F * scaleF;
float pingOffsetX = 680.0F * scaleF;

{
spades::ui::Button button(Manager);
Expand Down Expand Up @@ -210,51 +224,45 @@ namespace spades {
serverList.Bounds = AABB2(contentsLeft, 270.0F, contentsWidth, footerPos - 284.0F);
AddChild(serverList);
}

{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 2.0F, 246.0F, 260.0F - 2.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + itemOffsetX, headerPos, slotsOffsetX - itemOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Server Name");
@header.Activated = spades::ui::EventHandler(this.SortServerListByName);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 260.0F, 246.0F, 70.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + slotsOffsetX, headerPos, mapNameOffsetX - slotsOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Slots");
@header.Activated = spades::ui::EventHandler(this.SortServerListByNumPlayers);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 330.0F, 246.0F, 140.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + mapNameOffsetX, headerPos, gameModeOffsetX - mapNameOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Map Name");
@header.Activated = spades::ui::EventHandler(this.SortServerListByMapName);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 470.0F, 246.0F, 100.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + gameModeOffsetX, headerPos, protocolOffsetX - gameModeOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Game Mode");
@header.Activated = spades::ui::EventHandler(this.SortServerListByGameMode);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 570.0F, 246.0F, 60.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + protocolOffsetX, headerPos, pingOffsetX - protocolOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Ver.");
@header.Activated = spades::ui::EventHandler(this.SortServerListByProtocol);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 630.0F, 246.0F, 60.0F, 24.0F);
header.Text = _Tr("MainScreen", "Loc.");
@header.Activated = spades::ui::EventHandler(this.SortServerListByCountry);
AddChild(header);
}
{
ServerListHeader header(Manager);
header.Bounds = AABB2(contentsLeft + 690.0F, 246.0F, contentsWidth - 690.0F + 2.0F, 24.0F);
header.Bounds = AABB2(contentsLeft + pingOffsetX, headerPos, contentsWidth - pingOffsetX + itemOffsetX, headerHeight);
header.Text = _Tr("MainScreen", "Ping");
@header.Activated = spades::ui::EventHandler(this.SortServerListByPing);
AddChild(header);
Expand Down Expand Up @@ -328,7 +336,7 @@ namespace spades {
UpdateServerList();
}

private void UpdateServerList() {
private void UpdateServerList(bool refresh = true) {
string key = "";
switch (cg_serverlistSort.IntValue & 0xFFF) {
case 0: key = "Ping"; break;
Expand Down Expand Up @@ -373,12 +381,23 @@ namespace spades {
list2.insertLast(item);
}

ServerListModel model(Manager, list2);
// If we are updating the list in real time, try not to replace the
// model
if (currentServerListModel !is null and !refresh and currentServerListModel.list.length == list2.length) {
currentServerListModel.ReplaceList(list2);
return;
}

ServerListModel model(Manager, helper, list2);
@serverList.Model = model;
@model.ItemActivated = ServerListItemEventHandler(this.ServerListItemActivated);
@model.ItemDoubleClicked = ServerListItemEventHandler(this.ServerListItemDoubleClicked);
@model.ItemRightClicked = ServerListItemEventHandler(this.ServerListItemRightClicked);
serverList.ScrollToTop();

@currentServerListModel = model;

if (refresh)
serverList.ScrollToTop();
}

private void CheckServerList() {
Expand All @@ -400,6 +419,15 @@ namespace spades {
loadingView.Visible = false;
UpdateServerList();
}

if ((cg_serverlistSort.IntValue & 0xfff) == 0 and loaded) {
// Ping (RTT) is updated in real-time
if (serverListUpdateTimer == 0) {
UpdateServerList(false);
serverListUpdateTimer = 5;
}
--serverListUpdateTimer;
}
}

private void OnAddressChanged(spades::ui::UIElement@ sender) {
Expand Down Expand Up @@ -432,7 +460,7 @@ namespace spades {
UpdateServerList();
}
private void OnFilterTextChanged(spades::ui::UIElement@ sender) {
UpdateServerList();
UpdateServerList();
}

private void OnRefreshServerListPressed(spades::ui::UIElement@ sender) { LoadServerList(); }
Expand Down
91 changes: 70 additions & 21 deletions Resources/Scripts/Gui/MainScreen/ServerList.as
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,33 @@
namespace spades {

class ServerListItem : spades::ui::ButtonBase {
MainScreenHelper@ helper;
MainScreenServerItem@ item;
FlagIconRenderer@ flagIconRenderer;
ServerListItem(spades::ui::UIManager@ manager, MainScreenServerItem@ item) {
ServerListItem(spades::ui::UIManager@ manager, MainScreenHelper@ helper, MainScreenServerItem@ item) {
super(manager);
@this.helper = helper;
@this.item = item;
@flagIconRenderer = FlagIconRenderer(manager.Renderer);
}
void Render() {
Renderer@ r = Manager.Renderer;
Vector2 pos = ScreenPosition;
Vector2 size = Size;

// adjust based on screen width
float maxContentsWidth = 750.0F;
float scaleF = Min(Manager.ScreenWidth / maxContentsWidth, 1.0F);

float itemOffsetX = 2.0F;
float itemOffsetY = 2.0F;
float flagIconOffsetX = (itemOffsetX + 12.0F) * scaleF;
float nameOffsetX = (itemOffsetX + 24.0F) * scaleF;
float slotsOffsetX = 330.0F * scaleF;
float mapNameOffsetX = 370.0F * scaleF;
float gameModeOffsetX = 580.0F * scaleF;
float protocolOffsetX = 640.0F * scaleF;
float pingOffsetX = size.x - itemOffsetX;

Vector4 bgcolor = Vector4(1.0F, 1.0F, 1.0F, 0.0F);
Vector4 fgcolor = Vector4(1.0F, 1.0F, 1.0F, 1.0F);
Expand All @@ -43,11 +59,12 @@ namespace spades {
fgcolor = Vector4(220, 220, 0, 255) / 255.0F;
}

if (Pressed and Hover)
if (Pressed and Hover) {
bgcolor.w = 0.3F;
else if (Hover)
} else if (Hover) {
bgcolor.w = 0.15F;

}

r.ColorNP = bgcolor;
r.DrawImage(null, AABB2(pos.x + 1.0F, pos.y + 1.0F, size.x, size.y));

Expand All @@ -57,8 +74,12 @@ namespace spades {
fgcolor.w *= 0.5F;
}

// Draw server country flag icon
r.ColorNP = col;
flagIconRenderer.DrawIcon(item.Country, pos + Vector2(flagIconOffsetX, itemOffsetY + (size.y * 0.5F)));

// Draw server name
Font.Draw(item.Name, pos + Vector2(4.0F, 2.0F), 1.0F, fgcolor);
Font.Draw(item.Name, pos + Vector2(nameOffsetX, itemOffsetY), 1.0F, fgcolor);

// Draw server slots
string playersStr = ToString(item.NumPlayers) + "/" + ToString(item.MaxPlayers);
Expand All @@ -69,41 +90,66 @@ namespace spades {
playersCol = Vector4(1.0F, 1.0F, 0.7F, col.w);
else if (item.NumPlayers == 0)
playersCol = Vector4(0.7F, 0.7F, 1.0F, col.w);
Font.Draw(playersStr, pos + Vector2(279.0F - Font.Measure(playersStr).x * 0.5F, 2.0F), 1.0F, playersCol);
Font.Draw(playersStr, pos + Vector2(slotsOffsetX - Font.Measure(playersStr).x * 0.5F, itemOffsetY), 1.0F, playersCol);

// Draw map name
Font.Draw(item.MapName, pos + Vector2(330.0F, 2.0F), 1.0F, col);
Font.Draw(item.MapName, pos + Vector2(mapNameOffsetX, itemOffsetY), 1.0F, col);

// Draw server gamemode
Font.Draw(item.GameMode, pos + Vector2(470.0F, 2.0F), 1.0F, col);
Font.Draw(item.GameMode, pos + Vector2(gameModeOffsetX - Font.Measure(item.GameMode).x * 0.5F, itemOffsetY), 1.0F, col);

// Draw server protocol
Font.Draw(item.Protocol, pos + Vector2(570.0F, 2.0F), 1.0F, col);

// Draw server country flag icon
flagIconRenderer.DrawIcon(item.Country, pos + Vector2(640.0F, size.y * 0.5F));
Font.Draw(item.Protocol, pos + Vector2(protocolOffsetX, itemOffsetY), 1.0F, col);

// Draw server ping
string pingStr = ToString(item.Ping);
Vector4 pingCol(Min((2.0F * item.Ping) / 300.0F, 1.0F), Min((2.0F * (300 - item.Ping)) / 300.0F, 1.0F), 0.1F, col.w);
Font.Draw(pingStr, pos + Vector2(710.0F - Font.Measure(pingStr).x, 2.0F), 1.0F, pingCol);
int ping = helper.GetServerPing(item.Address);
string pingStr = (ping == -1) ? "?" : ToString(ping);

Vector4 pingCol = Vector4(1.0F, 1.0F, 1.0F, col.w);
if (ping != -1) {
float ratio = (2.0F * ping) / 300.0F;
pingCol.x = Clamp(ratio - 1.0F, 0.0F, 1.0F);
pingCol.y = Clamp(1.0F - pingCol.x, 0.0F, ratio);
pingCol.z = Clamp(1.0F - ratio, 0.0F, ratio);
}

Font.Draw(pingStr, pos + Vector2(pingOffsetX - Font.Measure(pingStr).x, itemOffsetY), 1.0F, pingCol);
}
}

funcdef void ServerListItemEventHandler(ServerListModel@ sender, MainScreenServerItem@ item);

class ServerListModel : spades::ui::ListViewModel {
spades::ui::UIManager@ manager;
MainScreenHelper@ helper;
MainScreenServerItem @[] @list;
ServerListItem@[]@ itemElements;

ServerListItemEventHandler@ ItemActivated;
ServerListItemEventHandler@ ItemDoubleClicked;
ServerListItemEventHandler@ ItemRightClicked;

ServerListModel(spades::ui::UIManager@ manager, MainScreenServerItem @[] @list) {
ServerListModel(spades::ui::UIManager@ manager, MainScreenHelper@ helper, MainScreenServerItem@[]@ list) {
@this.manager = manager;
@this.helper = helper;
@this.list = list;

@this.itemElements = array<ServerListItem@>();
for (uint i = list.length; i > 0; --i)
itemElements.insertLast(null);
}

void ReplaceList(MainScreenServerItem@[]@ list) {
Assert(list.length == this.list.length);
@this.list = list;

// Kinda dirty hack
for (uint i = 0, count = list.length; i < count; ++i) {
if (itemElements[i] !is null)
@itemElements[i].item = list[i];
}
}

int NumRows {
get { return int(list.length); }
}
Expand All @@ -123,11 +169,14 @@ namespace spades {
ItemRightClicked(this, item.item);
}
spades::ui::UIElement@ CreateElement(int row) {
ServerListItem i(manager, list[row]);
@i.Activated = spades::ui::EventHandler(this.OnItemClicked);
@i.DoubleClicked = spades::ui::EventHandler(this.OnItemDoubleClicked);
@i.RightClicked = spades::ui::EventHandler(this.OnItemRightClicked);
return i;
if (itemElements[row] is null) {
ServerListItem i(manager, helper, list[row]);
@i.Activated = spades::ui::EventHandler(this.OnItemClicked);
@i.DoubleClicked = spades::ui::EventHandler(this.OnItemDoubleClicked);
@i.RightClicked = spades::ui::EventHandler(this.OnItemRightClicked);
@itemElements[row] = i;
}
return itemElements[row];
}
void RecycleElement(spades::ui::UIElement@ elem) {}
}
Expand Down
45 changes: 45 additions & 0 deletions Sources/Core/ENetUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright (c) 2017 yvt
This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <Core/Strings.h>
#include <enet/enet.h>

inline bool operator==(const ENetAddress &a, const ENetAddress &b) {
return a.host == b.host && a.port == b.port;
}

namespace spades {
template <> std::string ToString<ENetAddress>(const ENetAddress &v) {
return Format("{0}:{1}", v.host, v.port);
}
}

namespace std {
template <> struct hash<ENetAddress> {
using argument_type = ENetAddress;
using result_type = std::size_t;

result_type operator()(argument_type const &x) const noexcept {
return hash<enet_uint32>{}(x.host) ^ (hash<enet_uint16>{}(x.port) << 1);
}
};
}
9 changes: 7 additions & 2 deletions Sources/Gui/MainScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,17 @@ namespace spades {
timeToStartInitialization = 0.2F;
if (timeToStartInitialization > 0.0F) {
DrawStartupScreen();

timeToStartInitialization -= dt;
if (timeToStartInitialization <= 0.0F)
if (timeToStartInitialization <= 0.0F) {
DoInit(); // do init
return;
} else {
return;
}
}

helper->Update();

ScopedPrivilegeEscalation privilege;
static ScriptFunction func("MainScreenUI", "void RunFrame(float)");
ScriptContextHandle c = func.Prepare();
Expand Down
Loading

0 comments on commit 07bc15e

Please sign in to comment.