aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2024-08-14 19:54:25 +0300
committerRblSb <msrblsb@gmail.com>2024-08-14 19:54:25 +0300
commit38cc0a1d9b4b146af7110c681389378fd26761fa (patch)
treee4a0a35e4a518508c4127fdb368f388ce130e2a0 /src
parent097cf610e664c922c1c618b11a582bb6a1d959f8 (diff)
Seamless reconnection for first 5 seconds
Diffstat (limited to 'src')
-rw-r--r--src/client/Main.hx39
-rw-r--r--src/import.hx1
-rw-r--r--src/server/Main.hx24
-rw-r--r--src/tools/ArrayTools.hx116
-rw-r--r--src/utils/ArrayKeyValueReverseIterator.hx19
-rw-r--r--src/utils/ArrayReverseIterator.hx19
6 files changed, 206 insertions, 12 deletions
diff --git a/src/client/Main.hx b/src/client/Main.hx
index cb74ad8..8cdd914 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -41,7 +41,9 @@ class Main {
final filters:Array<{regex:EReg, replace:String}> = [];
var personal = new Client("Unknown", 0);
var isConnected = false;
+ var gotInitialConnection = false;
var disabledReconnection = false;
+ var disconnectNotification:Null<Timer>;
var ws:WebSocket;
final player:Player;
var onTimeGet:Timer;
@@ -125,17 +127,29 @@ class Main {
ws = new WebSocket('$protocol//$host$colonPort$path');
ws.onmessage = onMessage;
ws.onopen = () -> {
+ disconnectNotification?.stop();
+ disconnectNotification = null;
chatMessageConnected();
+ gotInitialConnection = true;
isConnected = true;
}
+ // if initial connection refused, or server/client is offline
ws.onclose = () -> {
- // if initial connection refused
- // or server/client offline
- if (isConnected) chatMessageDisconnected();
isConnected = false;
- player.pause();
+ var notificationDelay = gotInitialConnection ? 5000 : 0;
+ if (disabledReconnection) notificationDelay = 0;
+
+ if (disconnectNotification == null) {
+ disconnectNotification = Timer.delay(() -> {
+ if (isConnected) return;
+ chatMessageDisconnected();
+ player.pause();
+ }, notificationDelay);
+ }
+
if (disabledReconnection) return;
- Timer.delay(openWebSocket, 2000);
+ final reconnectionDelay = gotInitialConnection ? 1000 : 2000;
+ Timer.delay(openWebSocket, reconnectionDelay);
}
}
@@ -803,23 +817,34 @@ class Main {
}
function chatMessageConnected():Void {
+ final msgBuf = ge("#messagebuffer");
+ if (isLastMessageConnectionStatus()) {
+ msgBuf.removeChild(msgBuf.lastChild);
+ }
final div = document.createDivElement();
div.className = "server-msg-reconnect";
div.textContent = Lang.get("msgConnected");
- final msgBuf = ge("#messagebuffer");
msgBuf.appendChild(div);
scrollChatToEnd();
}
function chatMessageDisconnected():Void {
+ final msgBuf = ge("#messagebuffer");
+ if (isLastMessageConnectionStatus()) {
+ msgBuf.removeChild(msgBuf.lastChild);
+ }
final div = document.createDivElement();
div.className = "server-msg-disconnect";
div.textContent = Lang.get("msgDisconnected");
- final msgBuf = ge("#messagebuffer");
msgBuf.appendChild(div);
scrollChatToEnd();
}
+ function isLastMessageConnectionStatus():Bool {
+ final msgBuf = ge("#messagebuffer");
+ return msgBuf.lastElementChild?.className.startsWith("server-msg");
+ }
+
public static function serverMessage(text:String, isText = true, withTimestamp = true):Void {
final div = document.createDivElement();
final time = Date.now().toString().split(" ")[1];
diff --git a/src/import.hx b/src/import.hx
index 5517d1e..75f7907 100644
--- a/src/import.hx
+++ b/src/import.hx
@@ -1,2 +1,3 @@
using Lambda;
using StringTools;
+using tools.ArrayTools;
diff --git a/src/server/Main.hx b/src/server/Main.hx
index 1560f53..2b1aafa 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -33,6 +33,7 @@ class Main {
static inline var VIDEO_SKIP_DELAY = 1000;
static inline var FLASHBACKS_COUNT = 50;
static inline var FLASHBACK_DIST = 30;
+ static inline var EMPTY_ROOM_CALLBACK_DELAY = 5000;
final rootDir = '$__dirname/..';
@@ -56,6 +57,14 @@ class Main {
final messages:Array<Message> = [];
final flashbacks:Array<FlashbackItem> = [];
final logger:Logger;
+ /**
+ Stop video timer after `EMPTY_ROOM_CALLBACK_DELAY` in case
+ if server loses connection to all clients for a moment.
+
+ This allows seamless reconnection without rewinds
+ to stopped server time.
+ **/
+ var emptyRoomCallbackTimer:Null<Timer>;
static function main():Void {
new Main({
@@ -426,6 +435,7 @@ class Main {
switch (data.type) {
case Connected:
if (!internal) return;
+ emptyRoomCallbackTimer?.stop();
if (clients.length == 1 && videoList.length > 0) {
if (videoTimer.isPaused()) videoTimer.play();
}
@@ -457,8 +467,12 @@ class Main {
if (videoTimer.isPaused()) videoTimer.play();
}
if (clients.length == 0) {
- if (waitVideoStart != null) waitVideoStart.stop();
- videoTimer.pause();
+ emptyRoomCallbackTimer?.stop();
+ emptyRoomCallbackTimer = Timer.delay(() -> {
+ if (clients.length > 0) return;
+ waitVideoStart?.stop();
+ videoTimer.pause();
+ }, EMPTY_ROOM_CALLBACK_DELAY);
}
Timer.delay(() -> {
if (clients.exists(i -> i.name == client.name)) return;
@@ -987,12 +1001,12 @@ class Main {
return false;
}
- var waitVideoStart:Timer;
+ var waitVideoStart:Null<Timer>;
var loadedClientsCount = 0;
function restartWaitTimer():Void {
videoTimer.stop();
- if (waitVideoStart != null) waitVideoStart.stop();
+ waitVideoStart?.stop();
waitVideoStart = Timer.delay(startVideoPlayback, VIDEO_START_MAX_DELAY);
}
@@ -1004,7 +1018,7 @@ class Main {
}
function startVideoPlayback():Void {
- if (waitVideoStart != null) waitVideoStart.stop();
+ waitVideoStart?.stop();
loadedClientsCount = 0;
broadcast({type: VideoLoaded});
videoTimer.start();
diff --git a/src/tools/ArrayTools.hx b/src/tools/ArrayTools.hx
new file mode 100644
index 0000000..63b4839
--- /dev/null
+++ b/src/tools/ArrayTools.hx
@@ -0,0 +1,116 @@
+package tools;
+
+import utils.ArrayKeyValueReverseIterator;
+import utils.ArrayReverseIterator;
+
+class ArrayTools {
+ public static function last<T>(arr:Array<T>):Null<T> {
+ return arr[arr.length - 1];
+ }
+
+ public static function min<T:Float>(arr:Array<T>, ?maxValue:T):T {
+ var min = arr[0] ?? maxValue;
+ for (value in arr) if (value < min) min = value;
+ return min;
+ }
+
+ public static function max<T:Float>(arr:Array<T>, ?minValue:T):T {
+ var max = arr[0] ?? minValue;
+ for (value in arr) if (value > max) max = value;
+ return max;
+ }
+
+ public static function indexOfMax<T:Float>(arr:Array<T>, ?minValue:T):Int {
+ if (arr.length == 0) return -1;
+ var max = arr[0] ?? minValue;
+ var maxIndex = 0;
+ for (i in 1...arr.length) {
+ if (arr[i] > max) {
+ maxIndex = i;
+ max = arr[i];
+ }
+ }
+ return maxIndex;
+ }
+
+ public static function sum<T:Float>(arr:Array<T>):T {
+ var total:T = cast 0;
+ for (value in arr) total += value;
+ return total;
+ }
+
+ public static function shuffle<T>(arr:Array<T>):Void {
+ for (i => a in arr) {
+ final n = Std.random(arr.length);
+ final b = arr[n];
+ arr[i] = b;
+ arr[n] = a;
+ }
+ }
+
+ public static inline function reversed<T>(arr:Array<T>) {
+ return new ArrayReverseIterator(arr);
+ }
+
+ /** Key-value reversed array iterator **/
+ public static inline function reversedKV<T>(arr:Array<T>) {
+ return new ArrayKeyValueReverseIterator(arr);
+ }
+
+ public static inline function findMin<T>(
+ arr:Array<T>, f:(item:T) -> Float, maxValue:Float
+ ):Null<T> {
+ var result:Null<T> = null;
+ for (item in arr) {
+ final dist = f(item);
+ if (dist > maxValue) continue;
+ maxValue = dist;
+ result = item;
+ }
+ return result;
+ }
+
+ extern overload public static inline function inlineFind<T>(it:Array<T>, f:(item:T) -> Bool):Null<T> {
+ var result:Null<T> = null;
+ for (v in it) {
+ if (f(v)) {
+ result = v;
+ break;
+ }
+ }
+ return result;
+ }
+
+ extern overload public static inline function inlineFind<T>(it:Iterable<T>, f:(item:T) -> Bool):Null<T> {
+ var result:Null<T> = null;
+ for (v in it) {
+ if (f(v)) {
+ result = v;
+ break;
+ }
+ }
+ return result;
+ }
+
+ extern overload public static inline function inlineExists<T>(it:Array<T>, f:(item:T) -> Bool):Bool {
+ var result = false;
+ for (v in it) {
+ if (f(v)) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+ }
+
+ extern overload public static inline function inlineExists<T>(it:Iterable<T>, f:(item:T) -> Bool):Bool {
+ var result = false;
+ for (v in it) {
+ if (f(v)) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/utils/ArrayKeyValueReverseIterator.hx b/src/utils/ArrayKeyValueReverseIterator.hx
new file mode 100644
index 0000000..ad84e73
--- /dev/null
+++ b/src/utils/ArrayKeyValueReverseIterator.hx
@@ -0,0 +1,19 @@
+package utils;
+
+class ArrayKeyValueReverseIterator<T> {
+ final arr:Array<T>;
+ var i:Int;
+
+ public inline function new(arr:Array<T>) {
+ this.arr = arr;
+ this.i = this.arr.length - 1;
+ }
+
+ public inline function hasNext() {
+ return i > -1;
+ }
+
+ public inline function next() {
+ return {value: arr[i], key: i--};
+ }
+}
diff --git a/src/utils/ArrayReverseIterator.hx b/src/utils/ArrayReverseIterator.hx
new file mode 100644
index 0000000..9976c6a
--- /dev/null
+++ b/src/utils/ArrayReverseIterator.hx
@@ -0,0 +1,19 @@
+package utils;
+
+class ArrayReverseIterator<T> {
+ final arr:Array<T>;
+ var i:Int;
+
+ public inline function new(arr:Array<T>) {
+ this.arr = arr;
+ this.i = this.arr.length - 1;
+ }
+
+ public inline function hasNext() {
+ return i > -1;
+ }
+
+ public inline function next() {
+ return arr[i--];
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage