######################################################################################## # This file is part of Spheres. # Copyright (C) 2019 Jesusalva # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ######################################################################################## # Definitions: WebSocket init python: # FIXME: Drop dead if session_id mismatches (new "token") # Same as ondata? def onmsg(self, message): global tr_load, tr_val, tr_busy stdout("Server : " + str(message)) # Wait wait, it might have been a SERVNOTICE! try: if (message.split(':')[0] == "NOTICE"): renpy.call_screen("msgbox", "{b}SERVER NOTICE{b}\n\n%s" % str(":".join(command.split(':')[1:]))) return except: stdout("Error displaying SERVNOTICE") return # Inform whatever is waiting for us that a reply was obtained tr_load=True tr_val=str(message) # We do not clean tr_busy here return def ondata(self, msg, dtype, cont): #print("INFO: data received\n") """ on_data: callback object which is called when a message received. This is called before on_message or on_cont_message, and then on_message or on_cont_message is called. on_data has 4 argument. The 1st argument is this class object. The 2nd argument is utf-8 string which we get from the server. The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came. The 4th argument is continue flag. if 0, the data continue """ #renpy.notify(msg) return def onopen(self): global tr_busy, tr_load, tr_val, HOST print("Opening connection to %s" % HOST) tr_load=True tr_val="" tr_busy=False send_packet_now("PING") def onerror(self, err): stdout("ERROR RECEIVED") stdout("More details: %s" % repr(err)) stdout("An error happened: %s" % str(err)) # FIXME: If such error happen, the game never dies # This is a huge problem o.o try: renpy.call_screen("msgbox", "An unrecoverable error happened.\nPlease close and re-open the app.") # "An unrecoverable error happened.\nPress OK to return to main menu screen.") #raise KeyboardException(str(err)) #raise err # FIXME: Not working, and the loop also does not work # Maybe because it causes an Exception? # I wonder if I can/should run this in a non-daemon thread? # But I sense the problem is not here but a level above # FIXME: renpy.quit() throws an exception - which in a thread is obviously not gonna work... #renpy.quit(relaunch=True, status=1) except: sdelay(20.0) return 1 class GameClient(WebSocketClient): def opened(self): onopen(self) # FIXME This is also onerror def closed(self, code, reason=None): print "Closed down", code, reason onerror(self, "%s" % str(reason)) def received_message(self, m): onmsg(self, str(m)) # Be mindful of where/when using this function # Or "onmsg" may accidentally not be cast =/ def send_packet_now(packet, args=""): global ws, tr_load, tr_val, tr_busy stdout("Sending: %s" % packet) ws.send(get_token() + ";" + packet + ";" + args) return def wait_packet(): global tr_load, tr_val, tr_busy timeout=0.0 print("Now waiting for packet!") while not tr_load: sdelay() # FIXME: This can cause errors in mobile? timeout+=0.02 if timeout >= TIMEOUT_INTERVAL: # FIXME: What if a screen is already being displayed? BUG try: renpy.call_screen("msgbox", "Error Code: %d\n\nApplication timeout, click to try again" % (ERR_TIMEOUT)) timeout=0.0 except: stdout("ERROR: Timeout and retry failure") break val=tr_val tr_busy=False print("value obtained: %s" % str(val)) if (val is None): return ERR_INVALID return val def schedule_packet(): global tr_load, tr_val, tr_busy # TODO: if tr_busy already true, wait until it is made false while tr_busy: sdelay() # Book processing space for ourselves tr_busy=True tr_load=False tr_val=None return def send_packet(packet, args=""): global tr_cmd, tr_busy, tr_load schedule_packet() send_packet_now(packet, args) tr_val = wait_packet() return # In past, this would keep running, in hopes of catching the program quit # and being able to kill the threads... But well, it worked poorly. # Still called "supervisor" but all it does now is init. # sslopt={"cert_reqs": CERT_NONE}) def supervisor(use_ssl): global ws stdout(_("Opening new socket...")) if use_ssl: ws = GameClient("wss://"+HOST+":61000") else: ws = GameClient("ws://"+HOST+":61000") ws.connect() renpy.invoke_in_thread(ws.run_forever) # May be problematic stdout("Connection established!") # The supervisor module is now uneeded as thread was cast return