tgui-panel: Soft ping (#66299)

* tgui-panel: Soft ping
This commit is contained in:
Aleksej Komarov
2022-04-19 17:35:43 +03:00
committed by GitHub
parent 7b1be157d7
commit fd0f398d6f
14 changed files with 90 additions and 33 deletions
+1
View File
@@ -80,6 +80,7 @@
/code/__DEFINES/chat.dm @stylemistake
/code/__DEFINES/tgui.dm @stylemistake
/code/controllers/subsystem/chat.dm @stylemistake
/code/controllers/subsystem/ping.dm @stylemistake
/code/controllers/subsystem/tgui.dm @stylemistake
/code/modules/tgchat @stylemistake
/code/modules/tgui @stylemistake
+1
View File
@@ -165,6 +165,7 @@
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
#define FIRE_PRIORITY_PING 10
#define FIRE_PRIORITY_IDLE_NPC 10
#define FIRE_PRIORITY_SERVER_MAINT 10
#define FIRE_PRIORITY_RESEARCH 10
+39
View File
@@ -0,0 +1,39 @@
/*!
* Copyright (c) 2022 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
SUBSYSTEM_DEF(ping)
name = "Ping"
priority = FIRE_PRIORITY_PING
wait = 4 SECONDS
flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
/datum/controller/subsystem/ping/stat_entry()
..("P:[GLOB.clients.len]")
/datum/controller/subsystem/ping/fire(resumed = FALSE)
// Prepare the new batch of clients
if (!resumed)
src.currentrun = GLOB.clients.Copy()
// De-reference the list for sanic speeds
var/list/currentrun = src.currentrun
while (currentrun.len)
var/client/client = currentrun[currentrun.len]
currentrun.len--
if (client?.tgui_panel?.is_ready())
// Send a soft ping
client.tgui_panel.window.send_message("ping/soft", list(
// Slightly less than the subsystem timer (somewhat arbitrary)
// to prevent incoming pings from resetting the afk state
"afk" = client.is_afk(3.5 SECONDS),
))
if (MC_TICK_CHECK)
return
+1
View File
@@ -545,6 +545,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
active_mousedown_item = null
SSambience.remove_ambience_client(src)
SSmouse_entered.hovers -= src
SSping.currentrun -= src
QDEL_NULL(view_size)
QDEL_NULL(void)
QDEL_NULL(tooltips)
+1 -1
View File
@@ -317,7 +317,7 @@
if(initialized)
send_full_update()
initialized = TRUE
if("pingReply")
if("ping/reply")
initialized = TRUE
if("suspend")
close(can_be_suspended = TRUE)
+1 -1
View File
@@ -325,7 +325,7 @@
// If not locked, handle these message types
switch(type)
if("ping")
send_message("pingReply", payload)
send_message("ping/reply", payload)
if("suspend")
close(can_be_suspended = TRUE)
if("close")
+1
View File
@@ -482,6 +482,7 @@
#include "code\controllers\subsystem\pathfinder.dm"
#include "code\controllers\subsystem\persistence.dm"
#include "code\controllers\subsystem\persistent_paintings.dm"
#include "code\controllers\subsystem\ping.dm"
#include "code\controllers\subsystem\points_of_interest.dm"
#include "code\controllers\subsystem\profiler.dm"
#include "code\controllers\subsystem\radiation.dm"
+1 -1
View File
@@ -4,4 +4,4 @@
* @license MIT
*/
export const CONNECTION_LOST_AFTER = 15000;
export const CONNECTION_LOST_AFTER = 20000;
+9 -4
View File
@@ -4,7 +4,7 @@
* @license MIT
*/
import { pingSuccess } from '../ping/actions';
import { pingSoft, pingSuccess } from '../ping/actions';
import { connectionLost, connectionRestored, roundRestarted } from './actions';
import { selectGame } from './selectors';
import { CONNECTION_LOST_AFTER } from './constants';
@@ -19,6 +19,7 @@ const withTimestamp = action => ({
export const gameMiddleware = store => {
let lastPingedAt;
setInterval(() => {
const state = store.getState();
if (!state) {
@@ -34,15 +35,19 @@ export const gameMiddleware = store => {
store.dispatch(withTimestamp(connectionRestored()));
}
}, 1000);
return next => action => {
const { type, payload, meta } = action;
if (type === pingSuccess.type) {
lastPingedAt = meta.now;
const { type } = action;
if (type === pingSuccess.type || type === pingSoft.type) {
lastPingedAt = Date.now();
return next(action);
}
if (type === roundRestarted.type) {
return next(withTimestamp(action));
}
return next(action);
};
};
+15 -15
View File
@@ -6,20 +6,20 @@
import { createAction } from 'common/redux';
export const pingSuccess = createAction(
'ping/success',
ping => {
const now = Date.now();
const roundtrip = (now - ping.sentAt) * 0.5;
return {
payload: {
lastId: ping.id,
roundtrip,
},
meta: { now },
};
}
);
export const pingReply = createAction('ping/reply');
/**
* Soft ping from the server.
* It's intended to send periodic server-side metadata about the client,
* e.g. its AFK status.
*/
export const pingSoft = createAction('ping/soft');
export const pingSuccess = createAction('ping/success', (ping) => ({
payload: {
lastId: ping.id,
roundtrip: (Date.now() - ping.sentAt) * 0.5,
},
}));
export const pingFail = createAction('ping/fail');
export const pingReply = createAction('ping/reply');
@@ -4,7 +4,6 @@
* @license MIT
*/
export const PING_INTERVAL = 2500;
export const PING_TIMEOUT = 2000;
export const PING_MAX_FAILS = 3;
export const PING_QUEUE_SIZE = 8;
+16 -9
View File
@@ -4,14 +4,14 @@
* @license MIT
*/
import { pingFail, pingSuccess } from './actions';
import { PING_INTERVAL, PING_QUEUE_SIZE, PING_TIMEOUT } from './constants';
import { pingFail, pingReply, pingSoft, pingSuccess } from './actions';
import { PING_QUEUE_SIZE, PING_TIMEOUT } from './constants';
export const pingMiddleware = store => {
let initialized = false;
let index = 0;
let interval;
const pings = [];
const sendPing = () => {
for (let i = 0; i < PING_QUEUE_SIZE; i++) {
const ping = pings[i];
@@ -25,20 +25,26 @@ export const pingMiddleware = store => {
Byond.sendMessage('ping', { index });
index = (index + 1) % PING_QUEUE_SIZE;
};
return next => action => {
const { type, payload } = action;
if (!initialized) {
initialized = true;
interval = setInterval(sendPing, PING_INTERVAL);
sendPing();
}
if (type === 'roundrestart') {
// Stop pinging because dreamseeker is currently reconnecting.
// Topic calls in the middle of reconnect will crash the connection.
clearInterval(interval);
if (type === pingSoft.type) {
const { afk } = payload;
// On each soft ping where client is not flagged as afk,
// initiate a new ping.
if (!afk) {
sendPing();
}
return next(action);
}
if (type === 'pingReply') {
if (type === pingReply.type) {
const { index } = payload;
const ping = pings[index];
// Received a timed out ping
@@ -48,6 +54,7 @@ export const pingMiddleware = store => {
pings[index] = null;
return next(pingSuccess(ping));
}
return next(action);
};
};
+3
View File
@@ -10,6 +10,7 @@ import { PING_MAX_FAILS, PING_ROUNDTRIP_BEST, PING_ROUNDTRIP_WORST } from './con
export const pingReducer = (state = {}, action) => {
const { type, payload } = action;
if (type === pingSuccess.type) {
const { roundtrip } = payload;
const prevRoundtrip = state.roundtripAvg || roundtrip;
@@ -23,6 +24,7 @@ export const pingReducer = (state = {}, action) => {
networkQuality,
};
}
if (type === pingFail.type) {
const { failCount = 0 } = state;
const networkQuality = clamp01(state.networkQuality
@@ -38,5 +40,6 @@ export const pingReducer = (state = {}, action) => {
}
return nextState;
}
return state;
};
+1 -1
View File
@@ -134,7 +134,7 @@ export const backendMiddleware = store => {
}
if (type === 'ping') {
Byond.sendMessage('pingReply');
Byond.sendMessage('ping/reply');
return;
}