kerstezel-ober/source/kerstezelprotocol.d
2020-12-28 21:43:03 +01:00

147 lines
3.8 KiB
D

// 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) {
}
}
}