From cbe41bff80af2bfd926168f36f28c819d74d3408 Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Tue, 1 Nov 2022 13:41:39 +0200 Subject: [PATCH] Eventy integration - Fully integrated eventy - PongEvent and IRCEvent along with respective GenericSignal and PingSignal handlers added - Moved parsing of messages into Message class Debugging - Added dlog which always logs (for now, we should add a debug clause that enables it) --- dub.json | 1 + source/birchwood/client.d | 224 ++++++++++++++++++------------------ source/birchwood/messages.d | 125 ++++++++++++++++++++ 3 files changed, 235 insertions(+), 115 deletions(-) diff --git a/dub.json b/dub.json index 39aa5df..8760a63 100644 --- a/dub.json +++ b/dub.json @@ -4,6 +4,7 @@ ], "copyright": "Copyright © 2022, Tristan B. Kildaire", "dependencies": { + "dlog": "~>0.0.6", "eventy": "0.2.5" }, "description": "A sane IRC framework for the D language", diff --git a/source/birchwood/client.d b/source/birchwood/client.d index 162e36f..328a846 100644 --- a/source/birchwood/client.d +++ b/source/birchwood/client.d @@ -12,6 +12,14 @@ import birchwood.messages : Message; // TODO: Remove this import import std.stdio : writeln; +import dlog; + +__gshared Logger logger; +__gshared static this() +{ + logger = new DefaultLogger(); +} + public class BirchwoodException : Exception { @@ -146,9 +154,6 @@ public class Client this(ConnectionInfo connInfo) { this.connInfo = connInfo; - - /* Set the client inside IRCEvent so all can access it when handling events */ - IRCEvent.client = this; } ~this() @@ -156,16 +161,21 @@ public class Client //TODO: Do something here, tare downs } + private enum EventType : ulong + { + GENERIC_EVENT = 1, + PONG_EVENT + } + + + /* TODO: Move to an events.d class */ class IRCEvent : Event { - /* The client itself */ - private static __gshared Client client; - private Message msg; - this(ulong typeID, Message msg) + this(Message msg) { - super(typeID, null); + super(EventType.GENERIC_EVENT, null); this.msg = msg; } @@ -181,21 +191,52 @@ public class Client } } + /* TODO: make PongEvent (id 2 buit-in) */ + class PongEvent : Event + { + private string pingID; + + this(string pingID) + { + super(EventType.PONG_EVENT); + this.pingID = pingID; + } + + public string getID() + { + return pingID; + } + } + private void initEvents() { /* TODO: For now we just register one signal type for all messages */ - ulong signalDefault = 1; - engine.addQueue(signalDefault); - + /* Register all event types */ + engine.addQueue(EventType.GENERIC_EVENT); + engine.addQueue(EventType.PONG_EVENT); + + + /* Base signal with IRC client in it */ + abstract class BaseSignal : Signal + { + /* ICR client */ + private Client client; + + this(Client client, ulong[] eventIDs) + { + super(eventIDs); + this.client = client; + } + } /* TODO: We also add default signal handler which will just print stuff out */ - class SignalHandler1 : Signal + class GenericSignal : BaseSignal { - this() + this(Client client) { - super([1]); + super(client, [EventType.GENERIC_EVENT]); } public override void handler(Event e) @@ -204,16 +245,40 @@ public class Client IRCEvent ircEvent = cast(IRCEvent)e; assert(ircEvent); //Should never fail, unless some BOZO regged multiple handles for 1 - wait idk does eventy do that even mmm - writeln("IRCEvent (id): "~to!(string)(ircEvent.id)); - writeln("IRCEvent "~ircEvent.getMessage().toString()); + logger.log("IRCEvent (id): "~to!(string)(ircEvent.id)); + logger.log("IRCEvent "~ircEvent.getMessage().toString()); - writeln(IRCEvent.client); + logger.log(client); } } - Signal j = new SignalHandler1(); + Signal j = new GenericSignal(this); engine.addSignalHandler(j); + + /* TODO: Add Pong signal handler (make it id 2) */ + class PongSignal : BaseSignal + { + this(Client client) + { + super(client, [EventType.PONG_EVENT]); + } + + /* TODO: Implement me */ + public override void handler(Event e) + { + PongEvent pongEvent = cast(PongEvent)e; + assert(pongEvent); + + string messageToSend = "PONG "~pongEvent.getID(); + + client.sendMessage(messageToSend); + + logger.log("Ponged"); + } + } + + engine.addSignalHandler(new PongSignal(this)); } /** @@ -298,92 +363,6 @@ public class Client // return null; } - /* TODO: Implement me */ - /* TODO: This should be a static method in `birchwood.messages.Message` - * which geneartes a Message object for us - */ - private void parseReceivedMessage(string message) - { - /* TODO: testing */ - - /* From */ - string from; - - /* Command */ - string command; - - /* Params */ - string params; - - - - /* Check if there is a PREFIX (according to RFC 1459) */ - if(message[0] == ':') - { - /* prefix ends after first space (we fetch servername, host/user) */ - //TODO: make sure not -1 - long firstSpace = indexOf(message, ' '); - - /* TODO: double check the condition */ - if(firstSpace > 0) - { - from = message[1..firstSpace]; - - writeln("from: "~from); - - /* TODO: Find next space (what follows `from` is `' ' { ' ' }`) */ - ulong i = firstSpace; - for(; i < message.length; i++) - { - if(message[i] != ' ') - { - break; - } - } - - // writeln("Yo"); - - string rem = message[i..message.length]; - // writeln("Rem: "~rem); - long idx = indexOf(rem, " "); //TOOD: -1 check - - /* Extract the command */ - command = rem[0..idx]; - writeln("command: "~command); - - /* Params are everything till the end */ - i = idx; - for(; i < rem.length; i++) - { - if(rem[i] != ' ') - { - break; - } - } - params = rem[i..rem.length]; - writeln("params: "~params); - } - else - { - //TODO: handle - writeln("Malformed message start after :"); - assert(false); - } - - - } - - import birchwood.messages; - - Message msg = new Message(from, command, message); - - /* TODO: Set static in IRCEvent field for access to Client (and hence socket) */ - /* TODO: This should be done in reeive handler me thinks, or rather - shouild return something not yet TRIGGER an event */ - Event eTest = new IRCEvent(1, msg); - engine.push(eTest); - } - /* TODO: Spawn a thread worker that reacts */ /** @@ -406,6 +385,8 @@ public class Client /* Lock the receieve queue */ recvQueueLock.lock(); + /* Message being analysed */ + Message curMsg; /* Search for a PING */ ubyte[] pingMessage; @@ -443,24 +424,34 @@ public class Client * - we can cache or remember stuff when we get 353 */ + /* If we found a PING */ if(pingMessage.length > 0) { - writeln("Found a ping: "~cast(string)pingMessage); - string ogMessage = cast(string)pingMessage; - long idxSigStart = indexOf(ogMessage, ":")+1; - long idxSigEnd = lastIndexOf(ogMessage, '\r'); + /* Decode the message and parse it */ + curMsg = Message.parseReceivedMessage(decodeMessage(pingMessage)); + logger.log("Found a ping: "~curMsg.toString()); - string pingID = ogMessage[idxSigStart..idxSigEnd]; + // string ogMessage = cast(string)pingMessage; + // long idxSigStart = indexOf(ogMessage, ":")+1; + // long idxSigEnd = lastIndexOf(ogMessage, '\r'); + + // string pingID = ogMessage[idxSigStart..idxSigEnd]; + string pingID = curMsg.getMessage(); // this.socket.send(encodeMessage("PONG "~pingID)); + // string messageToSend = "PONG "~pingID; - string messageToSend = "PONG "~pingID; + // sendMessage(messageToSend); - sendMessage(messageToSend); + // logger.log("Ponged"); + + /* TODO: Implement */ + Event pongEvent = new PongEvent(pingID); + engine.push(pongEvent); } /* Now let's go message by message */ @@ -473,12 +464,15 @@ public class Client recvQueue.linearRemoveElement(recvQueue.front()); - writeln("Normal message: "~messageNormal); + // writeln("Normal message: "~messageNormal); /* TODO: Parse message and call correct handler */ - parseReceivedMessage(messageNormal); + curMsg = Message.parseReceivedMessage(messageNormal); + + Event ircEvent = new IRCEvent(curMsg); + engine.push(ircEvent); } @@ -553,8 +547,8 @@ public class Client private void processMessage(ubyte[] message) { // import std.stdio; - // writeln("Message length: "~to!(string)(message.length)); - // writeln("InterpAsString: "~cast(string)message); + // logger.log("Message length: "~to!(string)(message.length)); + // logger.log("InterpAsString: "~cast(string)message); receiveQ(message); diff --git a/source/birchwood/messages.d b/source/birchwood/messages.d index a3aa67c..1dced00 100644 --- a/source/birchwood/messages.d +++ b/source/birchwood/messages.d @@ -1,5 +1,31 @@ module birchwood.messages; +import dlog; + +import std.string; + +// TODO: Before release we should remove this import +import std.stdio : writeln; + +/* TODO: We could move these all to `package.d` */ + +/* Static is redundant as module is always static , gshared needed */ +/* Apparebky works without gshared, that is kinda sus ngl */ +__gshared Logger logger; +/** +* source/birchwood/messages.d(10,8): Error: variable `birchwood.messages.logger` is a thread-local class and cannot have a static initializer. Use `static this()` to initialize instead. +* +* It is complaining that it wopuld static init per thread, static this() for module is required but that would +* do a module init per thread, so __gshared static this() is needed, we want one global init - a single logger +* variable and also class init +*/ + +__gshared static this() +{ + logger = new DefaultLogger(); +} + + /** * Message types */ @@ -16,8 +42,107 @@ public class Message this.message = message; } + public static Message parseReceivedMessage(string message) + { + /* TODO: testing */ + + /* From */ + string from; + + /* Command */ + string command; + + /* Params */ + string params; + + + + /* Check if there is a PREFIX (according to RFC 1459) */ + if(message[0] == ':') + { + /* prefix ends after first space (we fetch servername, host/user) */ + //TODO: make sure not -1 + long firstSpace = indexOf(message, ' '); + + /* TODO: double check the condition */ + if(firstSpace > 0) + { + from = message[1..firstSpace]; + + logger.log("from: "~from); + + /* TODO: Find next space (what follows `from` is `' ' { ' ' }`) */ + ulong i = firstSpace; + for(; i < message.length; i++) + { + if(message[i] != ' ') + { + break; + } + } + + // writeln("Yo"); + + string rem = message[i..message.length]; + // writeln("Rem: "~rem); + long idx = indexOf(rem, " "); //TOOD: -1 check + + /* Extract the command */ + command = rem[0..idx]; + logger.log("command: "~command); + + /* Params are everything till the end */ + i = idx; + for(; i < rem.length; i++) + { + if(rem[i] != ' ') + { + break; + } + } + params = rem[i..rem.length]; + logger.log("params: "~params); + } + else + { + //TODO: handle + logger.log("Malformed message start after :"); + assert(false); + } + + + } + /* In this case it is only ` ` */ + else + { + + long firstSpace = indexOf(message, " "); //TODO: Not find check + + command = message[0..firstSpace]; + + ulong pos = firstSpace; + for(; pos < message.length; pos++) + { + if(message[pos] != ' ') + { + break; + } + } + + params = message[pos..message.length]; + + } + + return new Message(from, command, params); + } + public override string toString() { return "(from: "~from~", command: "~command~", message: `"~message~"`)"; } + + public string getMessage() + { + return message; + } } \ No newline at end of file