diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/Main.hx | 39 | ||||
| -rw-r--r-- | src/import.hx | 1 | ||||
| -rw-r--r-- | src/server/Main.hx | 24 | ||||
| -rw-r--r-- | src/tools/ArrayTools.hx | 116 | ||||
| -rw-r--r-- | src/utils/ArrayKeyValueReverseIterator.hx | 19 | ||||
| -rw-r--r-- | src/utils/ArrayReverseIterator.hx | 19 |
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--]; + } +} |
