// Copyright Chris Josten 2020 // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) import std.array; import std.algorithm; import std.datetime; import std.exception; import std.format; import std.random; import std.range; import vibe.vibe; immutable size_t BUF_SIZE = 2048; struct Staat { bool verbondenMetKerstman = false; long trommelenVerbodenTot = 0; } shared Staat staat; shared ManualEvent staatGebeurtenis; shared Task kerstmanTaak; __gshared TaskMutex mutex; shared static this() { staatGebeurtenis = createSharedManualEvent(); mutex = new TaskMutex(); } struct TrommelCommando {}; @trusted void kerstezelProtocolLuister(TCPConnection verbinding) nothrow { try { auto lock = scopedMutexLock(mutex); new Sessie(verbinding).voerUit(); } catch (Exception e) { logException(e, "Kon mutex niet verkrijgen"); } } class UnicodeUitzondering : Exception { mixin basicExceptionCtors; } class ProtocolUitzondering : Exception { mixin basicExceptionCtors; } class Sessie { private TCPConnection l_verbinding; private bool blijfBezig = true; private SysTime trommelenVerbodenTot; private bool trommelenVerboden = false; @safe this(TCPConnection verbinding) nothrow { this.l_verbinding = verbinding; } @trusted public void voerUit() nothrow { logInfo("Nieuwe sessie probeert te verbinden"); try { handGeven(); logInfo("Sessie is verbonden"); staat.verbondenMetKerstman = true; kerstmanTaak = Task.getThis(); staatGebeurtenis.emit(); while(true) { auto tijdsduur = trommelenVerboden ? min(trommelenVerbodenTot - Clock.currTime, seconds(30)) : seconds(30); bool received = receiveTimeout(tijdsduur, (TrommelCommando com) { logInfo("Trommelen"); auto now = Clock.currTime(); if (trommelenVerboden) return; l_verbinding.write("trommel\n"); l_verbinding.flush(); trommelenVerboden = true; trommelenVerbodenTot = now + seconds(30); staat.trommelenVerbodenTot = trommelenVerbodenTot.toUnixTime; staatGebeurtenis.emit(); ubyte[] read = l_verbinding.readLine(BUF_SIZE, "\n"); enforce!ProtocolUitzondering(read.equal("oké"), "Kerstman deed geen erkenning, maar deed %s".format(read)); } ); if (Clock.currTime() >= trommelenVerbodenTot) { trommelenVerboden = false; staatGebeurtenis.emit(); } if (!received) { l_verbinding.write("ping\n"); l_verbinding.flush(); ubyte[] read = l_verbinding.readLine(BUF_SIZE, "\n"); enforce!ProtocolUitzondering(read.equal("pong"), "Kerstman pongde niet terug, maar deed %s".format(read)); } } } catch (ProtocolUitzondering u) { logException(u, "Protocoluitzondering"); } catch (Exception e) { logException(e, "Andere uitzondering"); } finally { logInfo("Sessie verbroken"); staat.verbondenMetKerstman = false; staatGebeurtenis.emit(); sluit(); } } @safe private void handGeven () { // Maak een begroeting string begroeting2 = "e gaat 'ie?\n"; // Maak het irritant om het met de hand te doen :P int aantal = cast(int) (rndGen.front % 200) + 100; logInfo("Aantal: %d".format(aantal)); rndGen.popFront(); string begroeting = "ho".replicate(aantal) ~ begroeting2; l_verbinding.write(begroeting); l_verbinding.flush(); // Wacht op een antwoord in de vorm van (aantal * "go" + "ed!") ubyte[] data = l_verbinding.readLine(BUF_SIZE, "\n"); string expected = "go".replicate(aantal) ~ "ed"; enforce!ProtocolUitzondering(equal(data, expected), "Ongeldige handdruk"); } @safe private void sluit() nothrow { try { l_verbinding.close(); } catch(Exception e) { } } }