Skip to content

Commit

Permalink
Relay Ping Browser (#5777)
Browse files Browse the repository at this point in the history
# About the pull request

This PR adds a way to ping the relays and also easily connect to them.
You can access this panel by clicking the ping near the top right of
chat. The ping reported here will likely be inflated from what you will
actually experience or see reported by TGChat (its highly dependent on
how fast your computer runs the JavaScript, and is based only on a
single ping per connection), but should still give an accurate relative
difference between the different connections.

When the component is loaded, it starts a timer to begin the pings in 1
second. Whenever the component is unloaded, it indicates this to the
ping javascript which will cause it to early return on any pending
operations.

The configs are added to examples in relays.txt. You can include it in
config.txt.

# Explain why it's good for the game

This should give more visibility to the relays, provide an easy way to
use them, and give more of an idea the differences in connection speeds
to each relay.

# Testing Photographs and Procedure
Random values during testing:

![image](https://github.com/cmss13-devs/cmss13/assets/76988376/1a67bf8d-bc45-4fe8-888c-2360e6d54656)

An actual test example (with last forced to time out):

![image](https://github.com/cmss13-devs/cmss13/assets/76988376/4bcf24b9-e942-4936-a2a5-d6be3b4756f1)

Now with gauges:

![image](https://github.com/cmss13-devs/cmss13/assets/76988376/62d903fe-0311-48fc-90d5-9c07d388650a)

Now with flex layout:
![layout
rework](https://github.com/cmss13-devs/cmss13/assets/76988376/c9621ad4-9396-48b5-ae15-e415f53e3b06)

# Changelog
:cl: Drathek
ui: Added the relay ping browser accessed by the tgchat ping to test and
use alternative connections to the server
ui: Added onConfirmChange prop to Button.Confirm component.
config: Added CONNECTION_RELAY_PING and CONNECTION_RELAY_CON in the
relays.txt config that is optionally included in config.txt
/:cl:
  • Loading branch information
Drulikar committed Feb 24, 2024
1 parent 481680a commit c24ffb7
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 6 deletions.
7 changes: 5 additions & 2 deletions code/controllers/configuration/config_entry.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#define KEY_MODE_TEXT 0
#define KEY_MODE_TYPE 1
#define KEY_MODE_TEXT_UNALTERED 2

/datum/config_entry
var/name //read-only, this is determined by the last portion of the derived entry type
Expand Down Expand Up @@ -153,15 +154,17 @@
var/key_value = null

if(key_pos || value_mode == VALUE_MODE_FLAG)
key_name = lowertext(copytext(str_val, 1, key_pos))
key_name = copytext(str_val, 1, key_pos)
if(key_mode != KEY_MODE_TEXT_UNALTERED)
key_name = lowertext(key_name)
if(key_pos)
key_value = copytext(str_val, key_pos + length(str_val[key_pos]))
var/new_key
var/new_value
var/continue_check_value
var/continue_check_key
switch(key_mode)
if(KEY_MODE_TEXT)
if(KEY_MODE_TEXT, KEY_MODE_TEXT_UNALTERED)
new_key = key_name
continue_check_key = new_key
if(KEY_MODE_TYPE)
Expand Down
11 changes: 11 additions & 0 deletions code/controllers/configuration/entries/general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,14 @@ This maintains a list of ip addresses that are able to bypass topic filtering.
/datum/config_entry/flag/guest_ban

/datum/config_entry/flag/auto_profile

/// Relay Ping Browser configuration
/datum/config_entry/keyed_list/connection_relay_ping
splitter = "|"
key_mode = KEY_MODE_TEXT_UNALTERED
value_mode = VALUE_MODE_TEXT

/datum/config_entry/keyed_list/connection_relay_con
splitter = "|"
key_mode = KEY_MODE_TEXT_UNALTERED
value_mode = VALUE_MODE_TEXT
52 changes: 52 additions & 0 deletions code/modules/tgui_panel/ping_relay.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
GLOBAL_DATUM_INIT(relays_panel, /datum/ping_relay_tgui, new)

/datum/tgui_panel/proc/ping_relays()
GLOB.relays_panel.tgui_interact(client.mob)

/datum/ping_relay_tgui/tgui_interact(mob/user, datum/tgui/ui)
var/list/relay_ping_conf = CONFIG_GET(keyed_list/connection_relay_ping)
if(!length(relay_ping_conf))
to_chat(user, "There are no relays configured to test.")
return

ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PingRelaysPanel", "Relay Pings")
ui.open()
ui.set_autoupdate(FALSE)

/datum/ping_relay_tgui/ui_state(mob/user)
return GLOB.always_state

/datum/ping_relay_tgui/ui_static_data(mob/user)
var/list/data = list()
var/list/relay_names = list()
var/list/relay_pings = list()
var/list/relay_cons = list()

var/list/relay_ping_conf = CONFIG_GET(keyed_list/connection_relay_ping)
var/list/relay_con_conf = CONFIG_GET(keyed_list/connection_relay_con)
for(var/key in relay_ping_conf)
// assumption: keys are the same in both configs
relay_names += key
relay_pings += relay_ping_conf[key]
relay_cons += relay_con_conf[key]

data["relay_names"] = relay_names
data["relay_pings"] = relay_pings
data["relay_cons"] = relay_cons
return data

/datum/ping_relay_tgui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return

var/mob/user = ui.user

switch(action)
if("connect")
to_chat(user, "Now connecting via [params["desc"]]. Please wait...");
user << link(params["url"])
ui.close()
return
3 changes: 3 additions & 0 deletions code/modules/tgui_panel/tgui_panel.dm
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
if(type == "telemetry")
analyze_telemetry(payload)
return TRUE
if(type == "act/ping_relays")
ping_relays()
return TRUE

/**
* public
Expand Down
1 change: 1 addition & 0 deletions colonialmarines.dme
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,7 @@
#include "code\modules\tgui_input\text.dm"
#include "code\modules\tgui_panel\audio.dm"
#include "code\modules\tgui_panel\external.dm"
#include "code\modules\tgui_panel\ping_relay.dm"
#include "code\modules\tgui_panel\telemetry.dm"
#include "code\modules\tgui_panel\tgui_panel.dm"
#include "code\modules\tooltip\tooltip.dm"
Expand Down
1 change: 1 addition & 0 deletions config/example/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# $include dbconfig.txt
# $include resources.txt
# $include icon_source.txt
# $include relays.txt

## Server name: This appears at the top of the screen in-game. In this case it will read "tgstation: station_name" where station_name is the randomly generated name of the station for the round. Remove the # infront of SERVERNAME and replace 'tgstation' with the name of your choice
# SERVERNAME spacestation13
Expand Down
22 changes: 22 additions & 0 deletions config/example/relays.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Settings controlling the relay ping browser accessed via PingIndicator in TGChat
## Pings are performed by timing how long it takes to download favicon.ico from the PingURL

## Connection Relays: Name must match both ping and connect pairs
## CONNECTION_RELAY_PING Name|PingURL
## CONNECTION_RELAY_CON Name|ConnectURL
CONNECTION_RELAY_PING Direct|play.cm-ss13.com:8998
CONNECTION_RELAY_CON Direct|play.cm-ss13.com:1400
CONNECTION_RELAY_PING United Kingdom, London|uk.cm-ss13.com:8998
CONNECTION_RELAY_CON United Kingdom, London|uk.cm-ss13.com:1400
CONNECTION_RELAY_PING France, Gravelines|eu-w.cm-ss13.com:8998
CONNECTION_RELAY_CON France, Gravelines|eu-w.cm-ss13.com:1400
CONNECTION_RELAY_PING Poland, Warsaw|eu-e.cm-ss13.com:8998
CONNECTION_RELAY_CON Poland, Warsaw|eu-e.cm-ss13.com:1400
CONNECTION_RELAY_PING Oregon, Hillsboro|us-w.cm-ss13.com:8998
CONNECTION_RELAY_CON Oregon, Hillsboro|us-w.cm-ss13.com:1400
CONNECTION_RELAY_PING Virginia, Vint Hill|us-e.cm-ss13.com:8998
CONNECTION_RELAY_CON Virginia, Vint Hill|us-e.cm-ss13.com:1400
CONNECTION_RELAY_PING Singapore|asia-se.cm-ss13.com:8998
CONNECTION_RELAY_CON Singapore|asia-se.cm-ss13.com:1400
CONNECTION_RELAY_PING Australia, Sydney|aus.cm-ss13.com:8998
CONNECTION_RELAY_CON Australia, Sydney|aus.cm-ss13.com:1400
1 change: 1 addition & 0 deletions tgui/docs/component-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ A button with an extra confirmation step, using native button component.
- See inherited props: [Button](#button)
- `confirmContent: string` - Text to display after first click; defaults to "Confirm?"
- `confirmColor: string` - Color to display after first click; defaults to "bad"
- `onConfirmChange: function` - Called when the clickedOnce state changes: When the element is clicked the first time or unfocused.

### `Button.Input`

Expand Down
108 changes: 108 additions & 0 deletions tgui/packages/common/ping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Adapted pinging library based on:
* @file https://www.jsdelivr.com/package/npm/ping.js
* @copyright 2021 Alfred Gutierrez
* @license MIT
*/

/**
* Creates a Ping instance.
* @returns {Ping}
* @constructor
*/
export class Ping {
constructor(opt) {
this.opt = opt || {};
this.favicon = this.opt.favicon || '/favicon.ico';
this.timeout = this.opt.timeout || 10000;
this.logError = this.opt.logError || false;
this.abort = false;
}

/**
* Pings source after a delay and triggers a callback when completed.
* @param source Source of the website or server, including protocol and port.
* @param callback Callback function to trigger when completed. Returns error and ping value.
* @param delay Optional number of milliseconds to wait before starting.
*/
ping(source, callback, delay = 1000) {
this.abort = false;
let timer;
if (delay > 0) {
timer = setTimeout(() => {
if (this.abort) {
return;
}
this.pingNow(source, callback);
}, delay);
return;
}
this.pingNow(source, callback);
}

/**
* Pings source immediately and triggers a callback when completed.
* @param source Source of the website or server, including protocol and port.
* @param callback Callback function to trigger when completed. Returns error and ping value.
*/
pingNow(source, callback) {
let self = this;
self.abort = false;
self.wasSuccess = false;
self.img = new Image();
self.img.onload = (e) => {
self.wasSuccess = true;
pingCheck.call(self, e);
};
self.img.onerror = (e) => {
self.wasSuccess = false;
pingCheck.call(self, e);
};

let timer;
let start = new Date();

if (self.timeout) {
timer = setTimeout(() => {
self.wasSuccess = false;
pingCheck.call(self, undefined);
}, self.timeout);
}

/**
* Times ping and triggers callback.
*/
const pingCheck = function (e) {
if (timer) {
clearTimeout(timer);
}
if (this.abort) {
return;
}
let pong = new Date() - start;

if (typeof callback === 'function') {
// When operating in timeout mode, the timeout callback doesn't pass [event] as e.
// Notice [this] instead of [self], since .call() was used with context
if (!this.wasSuccess) {
if (self.logError) {
console.error('error loading resource: ' + e.error);
}
return callback(e ? 'Error' : 'Timed Out', pong);
}
return callback(null, pong);
} else {
throw new Error('Callback is not a function.');
}
};

self.img.src = source + self.favicon + '?' + +new Date(); // Trigger image load with cache buster
}

/**
* Aborts any pending ping request.
*/
cancel() {
this.abort = true;
}
}
19 changes: 15 additions & 4 deletions tgui/packages/tgui-panel/ping/PingIndicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

import { Color } from 'common/color';
import { toFixed } from 'common/math';
import { useSelector } from 'tgui/backend';
import { Box } from 'tgui/components';
import { useSelector, useBackend } from 'tgui/backend';
import { Button, Box } from 'tgui/components';
import { selectPing } from './selectors';

export const PingIndicator = (props) => {
const { act } = useBackend();
const ping = useSelector(selectPing);
const color = Color.lookup(ping.networkQuality, [
new Color(220, 40, 40),
Expand All @@ -19,9 +20,19 @@ export const PingIndicator = (props) => {
]);
const roundtrip = ping.roundtrip ? toFixed(ping.roundtrip) : '--';
return (
<div className="Ping">
<Button
lineHeight="15px"
width="50px"
className="Ping"
color="transparent"
hover
py="0.125em" // Override what light theme does to this
px="0.25em" // Override what light theme does to this
tooltip="Ping relays"
tooltipPosition="bottom-start"
onClick={() => act('ping_relays')}>
<Box className="Ping__indicator" backgroundColor={color} />
{roundtrip}
</div>
</Button>
);
};
4 changes: 4 additions & 0 deletions tgui/packages/tgui/components/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ export class ButtonConfirm extends Component {
} else {
window.removeEventListener('click', this.handleClick);
}
if (this.props.onConfirmChange) {
this.props.onConfirmChange(clickedOnce);
}
}

render() {
Expand All @@ -183,6 +186,7 @@ export class ButtonConfirm extends Component {
color,
content,
onClick,
onConfirmChange,
...rest
} = this.props;
return (
Expand Down
Loading

0 comments on commit c24ffb7

Please sign in to comment.