From 27db732576a29c4b2922bed999a2595c101051ee Mon Sep 17 00:00:00 2001 From: wr34k Date: Thu, 5 Jul 2018 01:47:01 +0200 Subject: [PATCH] epoll(2) la ^_^ --- IRCUFC/fight.py | 4 +- README.md | 3 + assets/palmares.json | 2 +- irc/irc.py | 190 ++++++++++++++++++++++++++++--------------- irc/ircCommands.py | 2 +- irc/ircEvents.py | 9 +- main.py | 14 +++- 7 files changed, 148 insertions(+), 76 deletions(-) diff --git a/IRCUFC/fight.py b/IRCUFC/fight.py index 8e9e742..abfb136 100644 --- a/IRCUFC/fight.py +++ b/IRCUFC/fight.py @@ -217,9 +217,7 @@ class Fight(object): return elif attacker.nextAction[0] == 'block': - tmp = attacker - attacker = defender - defender = tmp + attacker, defender = defender, attacker attacker.advantage = True defender.advantage = False diff --git a/README.md b/README.md index 0c4fe69..6676b88 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,6 @@ If both fighters are attacking at the same time, then the one who will hit is ra Using 'block' with the right level (high, middle, low) will give you a great chance to block the next hit. If so, you won't loose HP, and you'll take the advantage for the next round.. Be careful though, if for instance you block high and your opponent attacks you low or middle, you'll have 100% chance of taking the hit (Except if the strike fails). Blocking chance depend on your stance. There's a chance for fighters to fall down. In this case, hits will be much less powerful, and your opponent will do huge damage. If both fighters are on the ground, then one of them will be above the other. (The second fighter to fall will be above. Then, the one with the advantage will be above.) You'll have to use the '**!action standup**' command in order to .. stand up :) + + +**Shoutout to arab for the epoll(2) idea, and for [araboly](https://github.com/lalbornoz/araboly) which remains the best IRC game out there!** diff --git a/assets/palmares.json b/assets/palmares.json index b5c1a6c..db85094 100644 --- a/assets/palmares.json +++ b/assets/palmares.json @@ -1 +1 @@ -{"wreek": {"wins": 2, "looses": 0}, "wr34k": {"wins": 0, "looses": 2}} \ No newline at end of file +{"wreek": {"wins": 2, "looses": 2}, "wr34k": {"wins": 2, "looses": 2}} \ No newline at end of file diff --git a/irc/irc.py b/irc/irc.py index 2da6809..0ccce6c 100644 --- a/irc/irc.py +++ b/irc/irc.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -import sys, socket, ssl, time, os, re +import sys, socket, ssl, time, os, re, select sys.dont_write_bytecode = True os.chdir(sys.path[0] or ".") @@ -14,25 +14,11 @@ from ircReload import recompile from ircCommands import IrcCommands -server = 'irc.underworld.no' -port = 9999 -channel = ('#IRCUFC', None) # (chan, key) -use_ssl = True - -nickname = 'IRCUFC' -username = 'McBuffer' -realname = '** WE FIGHTIN **' - -optkey= "!" - -DEBUG = True - - class IrcBot(object): - def __init__(self): + def __init__(self, nickname="IRCUFC", username="McBuffer", realname="WE FIGHTIN", server="irc.underworld.no", port=9999, use_ssl=True, channel=("#IRCUFC", None), optkey="!", DEBUG=True): self.sock = None - self.lag = False + self.ep = None self.last_cmd = {} self.flood_flag = {} @@ -45,7 +31,7 @@ class IrcBot(object): self.server = server self.port = port - self.ssl = use_ssl + self._ssl = use_ssl self.nick = nickname self.user = username @@ -55,75 +41,142 @@ class IrcBot(object): self.channel,self.chankey = channel + self.recvQueue = self.sendQueue = [] + + self.partialLine = '' def connect(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setblocking(0) try: - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if use_ssl: - self.sock = ssl.wrap_socket(self.sock) - self.sock.connect((server, port)) - except Exception as e: - self.log.error("Connexion failed", e) - return + self.sock.connect((self.server, self.port)) + except BlockingIOError: + pass + socketfd = self.sock.fileno() + self.ep = select.epoll() + self.ep.register(self.sock.fileno(), select.EPOLLIN | select.EPOLLOUT) + ready = False + while not ready: + events = self.ep.poll(1) + for _,evt in events: + if evt & select.EPOLLOUT: + ready = True + break + + if self._ssl: + self.sock = ssl.wrap_socket(self.sock, do_handshake_on_connect=False) + ready = False + while not ready: + events = self.ep.poll(1) + for _,evt in events: + if evt & select.EPOLLOUT: + try: + self.sock.do_handshake() + ready = True + except ssl.SSLWantReadError: + pass + + def init(self): + self.connect() + self.register() + self.run() def run(self): - self.connect() - self.register() - self.listen() + while True: + events = self.ep.poll(1) + for _,evt in events: + if evt & select.EPOLLIN: + self._recv() + elif evt & select.EPOLLOUT and len(self.sendQueue) > 0: + self._send() + + self.handle_events() def register(self): - self.raw("USER {} 0 * :{}".format(self.user, self.real)) - self.raw("NICK {}".format(self.nick)) + self.queue("USER {} 0 * :{}".format(self.user, self.real)) + self.queue("NICK {}".format(self.nick)) def updateNick(self): - self.raw("NICK {}".format(self.nick)) + self.queue("NICK {}".format(self.nick)) - def listen(self): + def _send(self): + while self.sendQueue: + for m in self.sendQueue: + self.log.info(">> {}".format(m.replace("\r\n", ""))) + + msg = self.sendQueue[0] + datasent = self.sock.send(msg.encode()) + if datasent < len(msg): + self.sendQueue[0] = msg[datasent:] + return False + else: + del self.sendQueue[0] + return True + + def _recv(self): + lines = [] while True: try: - if self.lag: - self.lag=False - data += self.sock.recv(1024).decode('utf-8', 'ignore') - else: - data = self.sock.recv(1024).decode('utf-8', 'ignore') - - for line in [x.split() for x in data.split("\r\n") if len(x.split()) > 1]: - # self.log.info("<< {}".format(' '.join(line))) - - if line[0][1:] == 'ING': - ircEvents.eventPING(self, line) - - else: - try: - if hasattr(ircEvents, "event{}".format(line[1].upper())): - getattr(ircEvents, "event{}".format(line[1].upper()))(self, line) - except Exception as e: - self.log.error("Error in getattr()", e) - pass - - except (UnicodeDecodeError, UnicodeEncodeError): - pass - - except KeyboardInterrupt: - self.log.warn("^C, Exiting...") + newLines = self.sock.recv(512) + except BlockingIOError: + self.recvQueue = lines return + except ssl.SSLWantReadError: + self.recvQueue = lines + return + if not newLines: + lines += [None] + break + elif len(newLines) == 0: + break + else: + newLines = str(newLines, 'utf-8', 'ignore') + if newLines[-2:] == "\r\n": + msgs = (self.partialLine + newLines).split("\r\n")[:-1] + self.partialLine = "" + else: + msgs = (self.partialLine + newLines).split("\r\n") + if len(msgs) > 1: + self.partialLine = msgs[-1] + msgs = msgs[:-1] + lines += msgs - except Exception as e: - self.log.error("Exception in listen()", e) - pass + self.recvQueue = lines + for m in self.recvQueue(): + self.log.info("<< {}".format(m)) + + def handle_events(self): + while self.recvQueue: + data = self.recvQueue[0] + self.log.info("<< {}".format(data)) + line = data.split(" ") + if line[0][1:] == 'ING': + ircEvents.eventPING(self, line) + else: + try: + if hasattr(ircEvents, "event{}".format(line[1].upper())): + getattr(ircEvents, "event{}".format(line[1].upper()))(self, line) + except Exception as e: + self.log.error("Error in getattr()", e) + + del self.recvQueue[0] def join(self): - self.raw("JOIN {} {}".format(self.channel, self.chankey)) if self.chankey else self.raw("JOIN {}".format(self.channel)) + self.queue("JOIN {} {}".format(self.channel, self.chankey)) if self.chankey else self.queue("JOIN {}".format(self.channel)) - def raw(self, msg, timeout=None): + def queue(self, msg): msg = msg.replace("\r", "") msg = msg.replace("\n", "") - self.log.info(">> " + msg) - self.sock.send(bytes(msg + "\r\n", 'utf-8')) - if timeout: - time.sleep(timeout) + if len(msg) > (512 - len("\r\n")): + for i in range(0, len(msg), 512 - len("\r\n")): + m = msg[i:512 - len("\r\n")] + m = m + "\r\n" + self.sendQueue += [m] + else: + msg = msg + "\r\n" + self.sendQueue += [msg] def isAdmin(self, ident): ret = False @@ -134,6 +187,8 @@ class IrcBot(object): def handle_msg(self, chan, admin, nick, user, host, msg): + if len(msg) == 0: + return args = msg.split() if admin: if args[0] == '{}reload'.format(self.optkey): @@ -152,7 +207,8 @@ class IrcBot(object): def privmsg(self, chan, msg): if chan not in self.timeouts: self.timeouts[chan] = {'last_cmd': time.time(), 'burst': 0, 'timeout': 0} - self.raw("PRIVMSG {} :{}".format(chan, msg), self.timeouts[chan]['timeout']) + self.queue("PRIVMSG {} :{}".format(chan, msg)) + time.sleep(self.timeouts[chan]['timeout']) self.editTimeouts(chan) diff --git a/irc/ircCommands.py b/irc/ircCommands.py index 13c7d54..8a95a08 100644 --- a/irc/ircCommands.py +++ b/irc/ircCommands.py @@ -52,7 +52,7 @@ class IrcCommands(object): elif args[0].lower() == 'join': self.IRC.join(" ".join(args[1:])) else: - self.IRC.raw(" ".join(args)) + self.IRC.queue(" ".join(args)) if cmd == 'cancel' and self.fight.state != 'inactive': self.IRC.privmsg(chan, "Fight cancelled.") diff --git a/irc/ircEvents.py b/irc/ircEvents.py index 1866c8f..738f451 100644 --- a/irc/ircEvents.py +++ b/irc/ircEvents.py @@ -3,7 +3,7 @@ import time def eventPING(IRC, line): - IRC.raw("PONG {}".format(line[1])) + IRC.queue("PONG {}".format(line[1])) def event001(IRC, line): IRC.join() @@ -29,7 +29,7 @@ def eventQUIT(IRC, line): if line[0][1:].split("@")[0].split("!")[0] == IRC.nick: IRC.log.warn("quit ! Reconnecting in 15 seconds..") time.sleep(15) - IRC.run() + IRC.init() def eventINVITE(IRC, line): IRC.log.info("{} invited the bot to {}".format(line[0][1:].split("!")[0], line[3][1:])) @@ -38,4 +38,7 @@ def eventPRIVMSG(IRC, line): nick,user = line[0][1:].split("@")[0].split("!") user = user[1:] if user[0] == '~' else user host = line[0].split("@")[1] - IRC.handle_msg(line[2], IRC.isAdmin(line[0][1:]), nick, user, host, ' '.join(line[3:])[1:]) + try: + IRC.handle_msg(line[2], IRC.isAdmin(line[0][1:]), nick, user, host, ' '.join(line[3:])[1:]) + except: + pass diff --git a/main.py b/main.py index cf99193..60cfb1d 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,19 @@ sys.dont_write_bytecode = True os.chdir(sys.path[0] or ".") sys.path += ("irc", "IRCUFC") +server = 'irc.arabs.ps' +port = 6697 +channel = ('#IRCUFC', None) # (chan, key) +use_ssl = True + +nickname = 'IRCUFC' +username = 'McBuffer' +realname = '** WE FIGHTIN **' + +optkey= "!" + +DEBUG = True if __name__ == '__main__': import irc - irc.IrcBot().run() + irc.IrcBot(nickname, username, realname, server, port, use_ssl, channel, optkey, DEBUG).init()