def extractStateChangesResponse(data): if len(data) >= 4: code, data = Protocol.extractInt(data) p1, data = Protocol.extractTLV(data) if code == 0: p2 = '' return PlayerStates.QUIT, p1, p2, None, data elif code != 1: logdebug().error( "Unknown player state change code {}".format(code)) state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) if not p2: p2 = "null" ip, data = Protocol.extractTLV(data) # \xff\xff\xff\x9f # \x00\x00\x00& unknown1, data = Protocol.extractInt(data) unknown2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) # \x00\x00\x17y marker, data = Protocol.extractInt(data) playerinfo = dict( player=p1, ip=ip, city=city, cc=cc, country=country, spectators=0, ) return state, p1, p2, playerinfo, data
def extractStateChangesResponse(data): if len(data) >= 4: code, data = Protocol.extractInt(data) p1, data = Protocol.extractTLV(data) if code == 0: p2 = '' return PlayerStates.QUIT, p1, p2, None, data elif code != 1: logdebug().error("Unknown player state change code {}".format(code)) state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) if not p2: p2 = "null" ip, data = Protocol.extractTLV(data) # \xff\xff\xff\x9f # \x00\x00\x00& unknown1, data = Protocol.extractInt(data) unknown2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) # \x00\x00\x17y marker, data = Protocol.extractInt(data) playerinfo = dict( player=p1, ip=ip, city=city, cc=cc, country=country, ) return state, p1, p2, playerinfo, data
def parseAuthResponse(self, data): if len(data) < 4: logdebug().error("Unknown auth response {}".format(repr(data))) return result, data = Protocol.extractInt(data) if result == 0: self.selectTimeout = 15 self.sigLoginSuccess.emit() # password incorrect, user incorrect #if result == 0x6 or result == 0x4: else: if self.tcpSock: self.tcpSock.close() self.tcpConnected = False #if self.udpSock: # self.udpSock.close() # self.udpConnected = False self.sigLoginFailed.emit() #self.sigStatusMessage.emit("Login failed {}".format(result)) if result==6: self.sigStatusMessage.emit("Login failed: wrong password") elif result==9: self.sigStatusMessage.emit("Login failed: too many connections") elif result==4: self.sigStatusMessage.emit("Login failed: username doesn't exist into database") elif result==8: self.sigStatusMessage.emit("Clone connection closed.\nPlease login again.") else: self.sigStatusMessage.emit("Login failed {}".format(result))
def runFBA(self, quark): self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit("ERROR: make sure ggpofba-ng.exe is in the same folder as FightCade") return args = [] fba=self.fba if IS_WINDOWS: fba=fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] fbaini = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.ini') fbadat = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.roms.dat') fbainibkp = os.path.join(os.path.abspath(os.path.expanduser("~")), 'ggpofba-ng.bkp.ini') if not os.path.isfile(fbaini): self.createFbaIni() logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args, shell=True) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def parseAuthResponse(self, data): if len(data) < 4: logdebug().error("Unknown auth response {}".format(repr(data))) return result, data = Protocol.extractInt(data) if result == 0: self.selectTimeout = 15 self.sigLoginSuccess.emit() # password incorrect, user incorrect #if result == 0x6 or result == 0x4: else: if self.tcpSock: self.tcpSock.close() self.tcpConnected = False #if self.udpSock: # self.udpSock.close() # self.udpConnected = False self.sigLoginFailed.emit() #self.sigStatusMessage.emit("Login failed {}".format(result)) if result == 6: self.sigStatusMessage.emit("Login failed: wrong password") elif result == 9: self.sigStatusMessage.emit( "Login failed: too many connections") elif result == 4: self.sigStatusMessage.emit( "Login failed: username doesn't exist into database") elif result == 8: self.sigStatusMessage.emit( "Clone connection closed.\nPlease login again.") else: self.sigStatusMessage.emit("Login failed {}".format(result))
def runFBA(self, quark): if not self.checkRom(): return if not self.fba: self.sigStatusMessage.emit("Please configure Setting > Locate ggpofba.exe") return wine = '' args = [] if IS_WINDOWS: args = [self.fba, quark, '-w'] else: wine = findWine() if not wine: self.sigStatusMessage.emit("Please configure Setting > Locate wine") return if IS_LINUX: args = [packagePathJoin('ggpo', 'scripts', 'ggpofba.sh'), wine, self.fba, quark] else: args = [wine, self.fba, quark] logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def sendJoinChannelRequest(self, channel=None): if channel: self.channel = channel Settings.setValue(Settings.SELECTED_CHANNEL, channel) if channel in self.channels: if channel != 'lobby': self.rom = self.channels[channel]['rom'] else: self.rom = '' else: logdebug().error("Invalid channel {}".format(channel)) if (int(self.channelport) != int(self.channels[channel]['port'])): self.switchingServer = True self.channelport = int(self.channels[channel]['port']) Settings.setValue(Settings.PORT, self.channelport) self.tcpSock.close() self.sequence = 0x1 self.connectTcp() self.sendWelcome() self.sendAuth(self.username, self.password) if Settings.value(Settings.AWAY): self.sendToggleAFK(1) self.sendAndRemember(Protocol.JOIN_CHANNEL, Protocol.packTLV(self.channel))
def runFBA(self, quark): self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit("Please configure Setting > Locate ggpofba-ng.exe") return args = [] fba=self.fba if IS_WINDOWS: fba=fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def parseStateChangesResponse(self, data): count, data = Protocol.extractInt(data) while count > 0 and len(data) >= 4: state, p1, p2, playerinfo, data = self.__class__.extractStateChangesResponse(data) if state == PlayerStates.PLAYING: self.parsePlayerStartGameResponse(p1, p2, playerinfo) if self.username == p1: self.playingagainst = p2 if self.username == p2: self.playingagainst = p1 if Settings.value(Settings.USER_LOG_PLAYHISTORY) and self.username in [p1, p2]: loguser().info(u"[IN A GAME] {} vs {}".format(p1, p2)) elif state == PlayerStates.AVAILABLE: self.parsePlayerAvailableResponse(p1, playerinfo) if self.playingagainst == p1: self.playingagainst = '' self.killEmulator() elif state == PlayerStates.AFK: self.parsePlayerAFKResponse(p1, playerinfo) if self.playingagainst == p1: self.playingagainst = '' self.killEmulator() elif state == PlayerStates.QUIT: self.parsePlayerLeftResponse(p1) else: logdebug().error( "Unknown state change payload state: {} {}".format(state, repr(data))) if state == PlayerStates.PLAYING: msg = p1 + ' ' + PlayerStates.codeToString(state) + ' ' + p2 else: msg = p1 + ' ' + PlayerStates.codeToString(state) logdebug().info(msg) count -= 1
def handleTcpResponse(self): if self.tcpReadState == self.STATE_TCP_READ_LEN: if len(self.tcpData) >= 4: self.tcpResponseLen, self.tcpData = Protocol.extractInt( self.tcpData) self.tcpReadState = self.STATE_TCP_READ_DATA self.handleTcpResponse() elif self.tcpReadState == self.STATE_TCP_READ_DATA: if len(self.tcpData) >= self.tcpResponseLen: # tcpResponseLen should be >= 4 if self.tcpResponseLen < 4: logdebug().error( 'Cannot handle TLV payload of less than 4 bytes') self.tcpData = self.tcpData[self.tcpResponseLen:] self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.handleTcpResponse() else: data = self.tcpData[:self.tcpResponseLen] self.tcpData = self.tcpData[self.tcpResponseLen:] seq = Protocol.unpackInt(data[0:4]) self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.dispatch(seq, data[4:]) self.handleTcpResponse()
def download(self): d = findGamesavesDir() if not d: self.sigStatusMessage.emit('Can\'t find Savestates Directory') self.sigFinished.emit(self.added, self.updated, self.nochange) return localJsonDigest = readLocalJsonDigest() # noinspection PyBroadException try: # gotta love CPython's GIL, yield thread time.sleep(0.05) response = urllib.request.urlopen(self.SAVESTATES_INDEX_URL, timeout=3) games = json.load(response) for filename, shahash in games.items(): if re.search(r'[^ .a-zA-Z0-9_-]', filename): logdebug().error( "Filename {} looks suspicious, ignoring".format( filename)) continue if filename in localJsonDigest: if localJsonDigest[filename] == shahash: self.nochange += 1 continue else: self.updated += 1 else: self.added += 1 if not self.checkonly: time.sleep(0.05) localfile = os.path.join(d, filename) fileurl = self.SAVESTATES_GITHUB_BASE_URL + urllib.parse.quote( filename) urllib.request.urlretrieve(fileurl, localfile) self.sigStatusMessage.emit( 'Downloaded {}'.format(localfile)) # remove config/games/<game>.fs gamefs = os.path.abspath( os.path.join(d, "..", "config", "games", filename.replace("_ggpo.fs", ".fs"))) if os.path.isfile(gamefs): try: os.remove(gamefs) except: pass if not self.checkonly: if not self.added and not self.updated: #self.sigStatusMessage.emit('All files are up to date') pass else: writeLocalJsonDigest() self.sigStatusMessage.emit( '{} files are current, added {}, updated {}'.format( self.nochange, self.added, self.updated)) except Exception as ex: logdebug().error(str(ex)) self.sigFinished.emit(self.added, self.updated, self.nochange)
def selectLoop(self): while self.selectLoopRunning: inputs = [] if self.udpConnected: inputs.append(self.udpSock) if self.tcpConnected: inputs.append(self.tcpSock) # windows doesn't allow select on 3 empty set if not inputs: time.sleep(1) continue inputready, outputready, exceptready = None, None, None # http://stackoverflow.com/questions/13414029/catch-interrupted-system-call-in-threading try: inputready, outputready, exceptready = select.select( inputs, [], [], self.selectTimeout) except select.error, ex: if ex[0] != errno.EINTR and ex[0] != errno.EBADF: raise if not inputready: self.sendPingQueries() else: for stream in inputready: if stream == self.tcpSock: data = None # noinspection PyBroadException try: data = stream.recv(16384) except: if not self.switchingServer: self.tcpConnected = False self.selectLoopRunning = False self.sigServerDisconnected.emit() return if data: self.tcpData += data self.handleTcpResponse() else: if not self.switchingServer: stream.close() self.tcpConnected = False self.selectLoopRunning = False self.sigServerDisconnected.emit() elif stream == self.udpSock: dgram = None # on windows xp # Python exception: error: [Errno 10054] # An existing connection was forcibly closed by the remote host # noinspection PyBroadException try: dgram, addr = self.udpSock.recvfrom(64) except: pass if dgram: logdebug().info("UDP " + repr(dgram) + " from " + repr(addr)) self.handleUdpResponse(dgram, addr)
def sendJoinChannelRequest(self, channel=None): if channel: self.channel = channel Settings.setValue(Settings.SELECTED_CHANNEL, channel) if channel in self.channels: if channel != 'lobby': self.rom = self.channels[channel]['rom'] else: logdebug().error("Invalid channel {}".format(channel)) self.sendAndRemember(Protocol.JOIN_CHANNEL, Protocol.packTLV(self.channel))
def selectLoop(self): while self.selectLoopRunning: inputs = [] if self.udpConnected: inputs.append(self.udpSock) if self.tcpConnected: inputs.append(self.tcpSock) # windows doesn't allow select on 3 empty set if not inputs: time.sleep(1) continue inputready, outputready, exceptready = None, None, None # http://stackoverflow.com/questions/13414029/catch-interrupted-system-call-in-threading try: inputready, outputready, exceptready = select.select(inputs, [], [], self.selectTimeout) except select.error, ex: if ex[0] != errno.EINTR and ex[0] != errno.EBADF: raise if not inputready: self.sendPingQueries() else: for stream in inputready: if stream == self.tcpSock: data = None # noinspection PyBroadException try: data = stream.recv(16384) except: if not self.switchingServer: self.tcpConnected = False self.selectLoopRunning = False self.sigServerDisconnected.emit() return if data: self.tcpData += data self.handleTcpResponse() else: if not self.switchingServer: stream.close() self.tcpConnected = False self.selectLoopRunning = False self.sigServerDisconnected.emit() elif stream == self.udpSock: dgram = None # on windows xp # Python exception: error: [Errno 10054] # An existing connection was forcibly closed by the remote host # noinspection PyBroadException try: dgram, addr = self.udpSock.recvfrom(64) except: pass if dgram: logdebug().info("UDP " + repr(dgram) + " from " + repr(addr)) self.handleUdpResponse(dgram, addr)
def runFBA(self, quark): if not self.checkRom(): return if not self.fba: self.sigStatusMessage.emit( "Please configure Setting > Locate ggpofba.exe") return wine = '' args = [] fba = self.fba if IS_WINDOWS: if Settings.value(Settings.SERVER_ADDRESS) == 'ggpo.net': ngexe = os.path.join(os.path.dirname(fba), 'ggpofba-ng.exe') if os.path.exists(ngexe): fba = ngexe args = [fba, quark, '-w'] else: wine = findWine() if Settings.value(Settings.SERVER_ADDRESS) != 'ggpo.net': fba = fba.replace('.exe', '.py') if IS_LINUX: args = [ packagePathJoin('ggpo', 'scripts', 'ggpofba.sh'), fba, quark ] else: args = [fba, quark] else: if not wine: self.sigStatusMessage.emit( "Please configure Setting > Locate wine") return if IS_LINUX: args = [ packagePathJoin('ggpo', 'scripts', 'ggpofba.sh'), wine, fba, quark ] else: args = [wine, fba, quark] logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def runFBA(self, quark): if "served" in quark: self.killEmulator() self.killPuncher() time.sleep(2) self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit( "ERROR: make sure ggpofba-ng.exe is in the same folder as FightCade" ) return args = [] fba = self.fba if IS_WINDOWS: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] fbaini = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.ini') fbadat = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.roms.dat') fbainibkp = os.path.join(os.path.abspath(os.path.expanduser("~")), 'ggpofba-ng.bkp.ini') if not os.path.isfile(fbaini): self.createFbaIni() logdebug().info(" ".join(args)) if Settings.value(Settings.COMPOSITION_DISABLED): self.desktopComposition(0) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args, shell=True) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex)) # backup FBA settings if os.path.isfile(fbaini) and os.path.isfile(fbadat): copyfile(fbaini, fbainibkp)
def run(cls, checkonly, statusMsgCallback=None, finishedCallback=None): if cls._thread: logdebug().error('Already has a download thread running') cls._thread = QtCore.QThread() cls._worker = SyncWorker(checkonly) cls._worker.moveToThread(cls._thread) if finishedCallback: cls._worker.sigFinished.connect(finishedCallback) cls._worker.sigFinished.connect(cls.cleanup) if statusMsgCallback: cls._worker.sigStatusMessage.connect(statusMsgCallback) cls._thread.started.connect(cls._worker.download) cls._thread.start()
def parseListChannelsResponse(self, data): #self.channels = {} if len(data) <= 8: logdebug().error('No channels found') self.sigChannelsLoaded.emit() return status1, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) logdebug().info("Load channels header " + repr(status1) + repr(status2)) while len(data) > 4: room, data = Protocol.extractTLV(data) romname, data = Protocol.extractTLV(data) title, data = Protocol.extractTLV(data) users, data = Protocol.extractInt(data) index, data = Protocol.extractInt(data) # 'sfa3': {'title': 'Street Fighter Alpha 3', 'rom': 'sfa3:sfa3u', 'room': 'sfa3'}, # 'sfa2': {'title': 'Street Fighter Alpha 2', 'rom': 'sfa2', 'room': 'sfa2'}, channel = { 'rom': romname.split(':')[0], 'room': room, 'title': title, 'users': users, } self.channels[room] = channel logdebug().info(repr(self.channels)) self.sigChannelsLoaded.emit() if len(data) > 0: logdebug().error('Channel REMAINING DATA len {} {}'.format(len(data), repr(data)))
def sendPingQuery(self, player): if not self.udpConnected: return if not player.ip: return if not player.port: player.port = 6009 num1 = randint(500000, 30000000) num2 = randint(4000000, 900000000) secret = str(num1) + " " + str(num2) message = "GGPO PING " + secret logdebug().info("send GGPO PING {} to {}".format(secret, repr(player.ip))) self.sendudp(message, (player.ip, player.port, )) self.pinglist[secret] = (player.ip, player.player, time.time())
def download(self): d = findGamesavesDir() if not d: self.sigStatusMessage.emit('Can\'t find Savestates Directory') self.sigFinished.emit(self.added, self.updated, self.nochange) return localJsonDigest = readLocalJsonDigest() # noinspection PyBroadException try: # gotta love CPython's GIL, yield thread time.sleep(0.05) response = urllib2.urlopen(self.SAVESTATES_INDEX_URL, timeout=3) games = json.load(response) for filename, shahash in games.items(): if re.search(r'[^ .a-zA-Z0-9_-]', filename): logdebug().error("Filename {} looks suspicious, ignoring".format(filename)) continue if filename in localJsonDigest: if localJsonDigest[filename] == shahash: self.nochange += 1 continue else: self.updated += 1 else: self.added += 1 if not self.checkonly: time.sleep(0.05) localfile = os.path.join(d, filename) fileurl = self.SAVESTATES_GITHUB_BASE_URL + urllib.quote(filename) urllib.urlretrieve(fileurl, localfile) self.sigStatusMessage.emit('Downloaded {}'.format(localfile)) # remove config/games/<game>.fs gamefs=os.path.abspath(os.path.join(d,"..","config","games",filename.replace("_ggpo.fs",".fs"))) if os.path.isfile(gamefs): try: os.remove(gamefs) except: pass if not self.checkonly: if not self.added and not self.updated: #self.sigStatusMessage.emit('All files are up to date') pass else: writeLocalJsonDigest() self.sigStatusMessage.emit( '{} files are current, added {}, updated {}'.format( self.nochange, self.added, self.updated)) except Exception, ex: logdebug().error(str(ex))
def parseListChannelsResponse(self, data): #self.channels = {} if len(data) <= 8: logdebug().error('No channels found') self.sigChannelsLoaded.emit() return status1, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) logdebug().info("Load channels header " + repr(status1) + repr(status2)) while len(data) > 4: room, data = Protocol.extractTLV(data) romname, data = Protocol.extractTLV(data) title, data = Protocol.extractTLV(data) users, data = Protocol.extractInt(data) port, data = Protocol.extractInt(data) index, data = Protocol.extractInt(data) # 'sfa3': {'title': 'Street Fighter Alpha 3', 'rom': 'sfa3:sfa3u', 'room': 'sfa3'}, # 'sfa2': {'title': 'Street Fighter Alpha 2', 'rom': 'sfa2', 'room': 'sfa2'}, channel = { 'rom': romname.split(':')[0], 'room': room, 'title': title, 'users': users, 'port': port, } self.channels[room] = channel logdebug().info(repr(self.channels)) self.sigChannelsLoaded.emit() if len(data) > 0: logdebug().error('Channel REMAINING DATA len {} {}'.format( len(data), repr(data)))
def dispatchInbandData(self, seq, data): if not seq in self.tcpCommandsWaitingForResponse: logdebug().error("Sequence {} data {} not matched".format(seq, data)) return origRequest = self.tcpCommandsWaitingForResponse[seq] del self.tcpCommandsWaitingForResponse[seq] if origRequest == Protocol.AUTH: self.parseAuthResponse(data) elif origRequest == Protocol.MOTD: self.parseMotdResponse(data) elif origRequest == Protocol.LIST_CHANNELS: self.parseListChannelsResponse(data) elif origRequest == Protocol.LIST_USERS: self.parseListUsersResponse(data) elif origRequest == Protocol.SPECTATE: status, data = Protocol.extractInt(data) if status != 0: self.sigStatusMessage.emit("Fail to spectate " + str(status)) elif origRequest in [Protocol.WELCOME, Protocol.JOIN_CHANNEL, Protocol.TOGGLE_AFK, Protocol.SEND_CHALLENGE, Protocol.CHAT, Protocol.ACCEPT_CHALLENGE, Protocol.DECLINE_CHALLENGE, Protocol.CANCEL_CHALLENGE]: if len(data) == 4: status, data = Protocol.extractInt(data) if status != 0: codestr = Protocol.codeToString(origRequest) logdebug().error("{} failed, data {}".format(codestr, repr(data))) self.sigActionFailed.emit(codestr) else: logdebug().error("Unknown response for {}; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data))) else: logdebug().error("Not handling {} response; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data)))
def runFBA(self, quark): self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit("ERROR: make sure ggpofba-ng.exe is in the same folder as FightCade") return args = [] fba=self.fba if IS_WINDOWS: fba=fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] # if dat file doesn't exist, restore FBA settings from backup fbaini = os.path.join(os.path.dirname(self.fba), 'config', 'ggpofba-ng.ini') fbadat = os.path.join(os.path.dirname(self.fba), 'config', 'ggpofba-ng.roms.dat') fbainibkp = os.path.join(os.path.abspath(os.path.expanduser("~")), 'ggpofba-ng.bkp.ini') if not os.path.isfile(fbadat) and os.path.isfile(fbainibkp): self.sigStatusMessage.emit("Restoring emulator settings") copyfile(fbainibkp, fbaini) #on linux & MAC, symlink the ROMs folder to avoid configuring FBA if not IS_WINDOWS: romdir=Settings.value(Settings.ROMS_DIR) if romdir: fbaRomPath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "ROMs") # remove it if it's a link or an empty dir if os.path.islink(fbaRomPath): os.remove(fbaRomPath) if os.path.isdir(fbaRomPath) and not os.listdir(fbaRomPath): os.rmdir(fbaRomPath) if not os.path.exists(fbaRomPath): os.symlink(romdir, fbaRomPath) logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args, shell=True) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def __init__(self): super(Controller, self).__init__() self.selectTimeout = 1 self.sequence = 0x1 self.tcpSock = None self.tcpConnected = False self.tcpData = '' self.tcpReadState = self.STATE_TCP_READ_LEN self.tcpResponseLen = 0 self.tcpCommandsWaitingForResponse = dict() self.udpSock = None self.udpConnected = False self.selectLoopRunning = True self.username = '' self.channel = 'lobby' self.rom = '' self.fba = None self.checkInstallation() self.unsupportedRom = '' self.checkUnsupportedRom() self.playingagainst = '' self.challengers = set() self.challenged = None self.channels = {} self.pinglist = {} self.players = {} self.available = {} self.playing = {} self.awayfromkb = {} self.ignored = Settings.pythonValue(Settings.IGNORED) or set() self.sigStatusMessage.connect(logdebug().info)
def parseSpectateResponse(self, data): p1, data = Protocol.extractTLV(data) p2, data = Protocol.extractTLV(data) # if the guy I challenged accepted, remove him as challenged if self.challenged and self.challenged in [p1, p2] and self.username in [p1, p2]: self.challenged = None # quark len(53) = 'quark:stream,ssf2t,challenge-07389-1393539605.46,7000' quark, data = Protocol.extractTLV(data) logdebug().info("Quark " + repr(quark)) if quark.startswith('quark:served'): smooth = Settings.value(Settings.SMOOTHING) if smooth: match = re.search(r'[0-9]+', smooth) if match: quark += ',{}'.format(match.group(0)) self.runFBA(quark)
def download(self): d = findUnsupportedGamesavesDir() if not d: self.sigStatusMessage.emit( 'Unsupported Savestates Directory is not set') self.sigFinished.emit(self.added, self.updated, self.nochange) return localJsonDigest = readLocalJsonDigest() # noinspection PyBroadException try: # gotta love CPython's GIL, yield thread time.sleep(0.05) response = urllib2.urlopen(self.SAVESTATES_INDEX_URL, timeout=3) games = json.load(response) for filename, shahash in games.items(): if re.search(r'[^ .a-zA-Z0-9_-]', filename): logdebug().error( "Filename {} looks suspicious, ignoring".format( filename)) continue if filename in localJsonDigest: if localJsonDigest[filename] == shahash: self.nochange += 1 continue else: self.updated += 1 else: self.added += 1 if not self.checkonly: time.sleep(0.05) localfile = os.path.join(d, filename) fileurl = self.SAVESTATES_GITHUB_BASE_URL + urllib.quote( filename) urllib.urlretrieve(fileurl, localfile) self.sigStatusMessage.emit( 'Downloaded {}'.format(localfile)) if not self.checkonly: if not self.added and not self.updated: self.sigStatusMessage.emit('All files are up to date') else: writeLocalJsonDigest() self.sigStatusMessage.emit( '{} files are current, added {}, updated {}'.format( self.nochange, self.added, self.updated)) except Exception, ex: logdebug().error(str(ex))
def runFBA(self, quark): if not self.checkRom(): return if not self.fba: self.sigStatusMessage.emit("Please configure Setting > Locate ggpofba.exe") return wine = '' args = [] fba = self.fba if IS_WINDOWS: if Settings.value(Settings.SERVER_ADDRESS) == 'ggpo.net': ngexe = os.path.join(os.path.dirname(fba), 'ggpofba-ng.exe') if os.path.exists(ngexe): fba = ngexe args = [fba, quark, '-w'] else: wine = findWine() if Settings.value(Settings.SERVER_ADDRESS) != 'ggpo.net': fba = fba.replace('.exe', '.py') if IS_LINUX: args = [packagePathJoin('ggpo', 'scripts', 'ggpofba.sh'), fba, quark] else: args = [fba, quark] else: if not wine: self.sigStatusMessage.emit("Please configure Setting > Locate wine") return if IS_LINUX: args = [packagePathJoin('ggpo', 'scripts', 'ggpofba.sh'), wine, fba, quark] else: args = [wine, fba, quark] logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def handleUdpResponse(self, dgram, addr): if not dgram: return command = dgram[0:9] secret = dgram[10:] remoteip, remoteport = addr if command == "GGPO PING": self.sendudp("GGPO PONG {}".format(secret), addr) logdebug().info("send GGPO PONG {} to {}".format(secret, repr(addr))) if dgram[0:9] == "GGPO PONG": if secret in self.pinglist: ip = self.pinglist[secret][0] name = self.pinglist[secret][1] t1 = self.pinglist[secret][2] t2 = time.time() if ip == remoteip: self.updatePlayerPing(name, int((t2 - t1) * 1000)) del self.pinglist[secret]
def parseSpectateResponse(self, data): p1, data = Protocol.extractTLV(data) p2, data = Protocol.extractTLV(data) # if the guy I challenged accepted, remove him as challenged if self.challenged and self.challenged in [ p1, p2 ] and self.username in [p1, p2]: self.challenged = None # quark len(53) = 'quark:stream,ssf2t,challenge-07389-1393539605.46,7000' quark, data = Protocol.extractTLV(data) logdebug().info("Quark " + repr(quark)) if quark.startswith('quark:served'): smooth = Settings.value(Settings.SMOOTHING) if smooth: match = re.search(r'[0-9]+', smooth) if match: quark += ',{}'.format(match.group(0)) self.runFBA(quark)
def parseListUsersResponse(self, data): self.resetPlayers() if not data: return status, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) while len(data) > 8: p1, data = Protocol.extractTLV(data) # if len(data) <= 4: break state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) ip, data = Protocol.extractTLV(data) unk1, data = Protocol.extractInt(data) unk2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) port, data = Protocol.extractInt(data) color, data = Protocol.extractInt(data) color = None if color >= 0x1000000 else QtGui.QColor((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF) spectators, data = Protocol.extractInt(data) self.addUser( player=p1, ip=ip, port=port, city=city, cc=cc, country=country, color=color, spectators=spectators+1, ) if state == PlayerStates.AVAILABLE: self.available[p1] = True elif state == PlayerStates.AFK: self.awayfromkb[p1] = True elif state == PlayerStates.PLAYING: if not p2: p2 = 'null' self.playing[p1] = p2 self.sigPlayersLoaded.emit() if len(data) > 0: logdebug().error('List users - REMAINING DATA len {} {}'.format(len(data), repr(data)))
def parseAuthResponse(self, data): if len(data) < 4: logdebug().error("Unknown auth response {}".format(repr(data))) return result, data = Protocol.extractInt(data) if result == 0: self.selectTimeout = 15 self.sigLoginSuccess.emit() # password incorrect, user incorrect #if result == 0x6 or result == 0x4: else: if self.tcpSock: self.tcpSock.close() self.tcpConnected = False if self.udpSock: self.udpSock.close() self.udpConnected = False self.sigLoginFailed.emit() self.sigStatusMessage.emit("Login failed {}".format(result))
def logdebugTriggered(value): if value: level = logging.INFO else: level = logging.ERROR Settings.setBoolean(Settings.DEBUG_LOG, value) for handler in logdebug().handlers: if isinstance(handler, logging.handlers.RotatingFileHandler): handler.setLevel(level) break
def dispatch(self, seq, data): logdebug().info('Dispatch ' + Protocol.outOfBandCodeToString(seq) + ' ' + repr(data)) # out of band data if seq == Protocol.CHAT_DATA: self.parseChatResponse(data) elif seq == Protocol.PLAYER_STATE_CHANGE: self.parseStateChangesResponse(data) elif seq == Protocol.CHALLENGE_DECLINED: self.parseChallengeDeclinedResponse(data) elif seq == Protocol.CHALLENGE_RECEIVED: self.parseChallengeReceivedResponse(data) elif seq == Protocol.CHALLENGE_RETRACTED: self.parseChallengeCancelledResponse(data) elif seq == Protocol.JOINING_A_CHANNEL: self.parseJoinChannelResponse(data) elif seq == Protocol.SPECTATE_GRANTED: self.parseSpectateResponse(data) else: # in band response to our previous request self.dispatchInbandData(seq, data)
def parseListUsersResponse(self, data): self.resetPlayers() if not data: return status, data = Protocol.extractInt(data) status2, data = Protocol.extractInt(data) while len(data) > 8: p1, data = Protocol.extractTLV(data) # if len(data) <= 4: break state, data = Protocol.extractInt(data) p2, data = Protocol.extractTLV(data) ip, data = Protocol.extractTLV(data) unk1, data = Protocol.extractInt(data) unk2, data = Protocol.extractInt(data) city, data = Protocol.extractTLV(data) cc, data = Protocol.extractTLV(data) if cc: cc = cc.lower() country, data = Protocol.extractTLV(data) port, data = Protocol.extractInt(data) spectators, data = Protocol.extractInt(data) self.addUser( player=p1, ip=ip, port=port, city=city, cc=cc, country=country, spectators=spectators+1, ) if state == PlayerStates.AVAILABLE: self.available[p1] = True elif state == PlayerStates.AFK: self.awayfromkb[p1] = True elif state == PlayerStates.PLAYING: if not p2: p2 = 'null' self.playing[p1] = p2 self.sigPlayersLoaded.emit() if len(data) > 0: logdebug().error('List users - REMAINING DATA len {} {}'.format(len(data), repr(data)))
def parseStateChangesResponse(self, data): count, data = Protocol.extractInt(data) while count > 0 and len(data) >= 4: state, p1, p2, playerinfo, data = self.__class__.extractStateChangesResponse( data) if state == PlayerStates.PLAYING: self.parsePlayerStartGameResponse(p1, p2, playerinfo) if Settings.USER_LOG_PLAYHISTORY and self.username in [p1, p2]: loguser().info(u"[IN A GAME] {} vs {}".format(p1, p2)) elif state == PlayerStates.AVAILABLE: self.parsePlayerAvailableResponse(p1, playerinfo) elif state == PlayerStates.AFK: self.parsePlayerAFKResponse(p1, playerinfo) elif state == PlayerStates.QUIT: self.parsePlayerLeftResponse(p1) else: logdebug().error( "Unknown state change payload state: {} {}".format( state, repr(data))) if state == PlayerStates.PLAYING: msg = p1 + ' ' + PlayerStates.codeToString(state) + ' ' + p2 else: msg = p1 + ' ' + PlayerStates.codeToString(state) logdebug().info(msg) count -= 1 if len(data) > 0: logdebug().error("stateChangesResponse, remaining data {}".format( repr(data)))
def runFBA(self, quark): self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit( "ERROR: make sure ggpofba-ng.exe is in the same folder as FightCade" ) return args = [] fba = self.fba if IS_WINDOWS: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] fbaini = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.ini') fbadat = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'config', 'ggpofba-ng.roms.dat') fbainibkp = os.path.join(os.path.abspath(os.path.expanduser("~")), 'ggpofba-ng.bkp.ini') if not os.path.isfile(fbaini): self.createFbaIni() logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args, shell=True) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def handleTcpResponse(self): if self.tcpReadState == self.STATE_TCP_READ_LEN: if len(self.tcpData) >= 4: self.tcpResponseLen, self.tcpData = Protocol.extractInt(self.tcpData) self.tcpReadState = self.STATE_TCP_READ_DATA self.handleTcpResponse() elif self.tcpReadState == self.STATE_TCP_READ_DATA: if len(self.tcpData) >= self.tcpResponseLen: # tcpResponseLen should be >= 4 if self.tcpResponseLen < 4: logdebug().error('Cannot handle TLV payload of less than 4 bytes') self.tcpData = self.tcpData[self.tcpResponseLen:] self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.handleTcpResponse() else: data = self.tcpData[:self.tcpResponseLen] self.tcpData = self.tcpData[self.tcpResponseLen:] seq = Protocol.unpackInt(data[0:4]) self.tcpResponseLen = 0 self.tcpReadState = self.STATE_TCP_READ_LEN self.dispatch(seq, data[4:]) self.handleTcpResponse()
def dispatchInbandData(self, seq, data): if not seq in self.tcpCommandsWaitingForResponse: logdebug().error("Sequence {} data {} not matched".format( seq, data)) return origRequest = self.tcpCommandsWaitingForResponse[seq] del self.tcpCommandsWaitingForResponse[seq] if origRequest == Protocol.AUTH: self.parseAuthResponse(data) elif origRequest == Protocol.MOTD: self.parseMotdResponse(data) elif origRequest == Protocol.LIST_CHANNELS: self.parseListChannelsResponse(data) elif origRequest == Protocol.LIST_USERS: self.parseListUsersResponse(data) elif origRequest == Protocol.SPECTATE: status, data = Protocol.extractInt(data) if status != 0: self.sigStatusMessage.emit("Fail to spectate " + str(status)) elif origRequest in [ Protocol.WELCOME, Protocol.JOIN_CHANNEL, Protocol.TOGGLE_AFK, Protocol.SEND_CHALLENGE, Protocol.CHAT, Protocol.ACCEPT_CHALLENGE, Protocol.DECLINE_CHALLENGE, Protocol.CANCEL_CHALLENGE ]: if len(data) == 4: status, data = Protocol.extractInt(data) if status != 0: codestr = Protocol.codeToString(origRequest) logdebug().error("{} failed, data {}".format( codestr, repr(data))) if codestr == "SEND_CHALLENGE": self.sigActionFailed.emit("SEND_CHALLENGE failed") elif codestr == "CANCEL_CHALLENGE": pass else: self.sigActionFailed.emit(codestr) else: logdebug().error( "Unknown response for {}; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data))) else: logdebug().error( "Not handling {} response; seq {}; data {}".format( Protocol.codeToString(origRequest), seq, repr(data)))
def sendJoinChannelRequest(self, channel=None): if channel: self.channel = channel Settings.setValue(Settings.SELECTED_CHANNEL, channel) if channel in self.channels: if channel != 'lobby': self.rom = self.channels[channel]['rom'] else: self.rom = '' else: logdebug().error("Invalid channel {}".format(channel)) if (int(self.channelport)!=int(self.channels[channel]['port'])): self.switchingServer=True self.channelport = int(self.channels[channel]['port']) Settings.setValue(Settings.PORT, self.channelport) self.tcpSock.close() self.sequence = 0x1 self.connectTcp() self.sendWelcome() self.sendAuth(self.username, self.password) if Settings.value(Settings.AWAY): self.sendToggleAFK(1) self.sendAndRemember(Protocol.JOIN_CHANNEL, Protocol.packTLV(self.channel))
def __init__(self): super(Controller, self).__init__() self.selectTimeout = 1 self.sequence = 0x1 self.tcpSock = None self.tcpConnected = False self.tcpData = '' self.tcpReadState = self.STATE_TCP_READ_LEN self.tcpResponseLen = 0 self.tcpCommandsWaitingForResponse = dict() self.udpSock = None self.udpConnected = False self.selectLoopRunning = True self.switchingServer = False self.username = '' self.password = '' self.channel = 'lobby' self.channelport = 7000 self.rom = '' self.fba = None self.checkInstallation() self.unsupportedRom = '' self.checkUnsupportedRom() self.playingagainst = '' self.side = 0 # if currently playing, 1 or 2 if we are player 1 or player 2 self.challengers = set() self.challenged = None self.channels = {} self.pinglist = {} self.players = {} self.available = {} self.playing = {} self.awayfromkb = {} self.ignored = Settings.pythonValue(Settings.IGNORED) or set() self.sigStatusMessage.connect(logdebug().info) tmp = calendar.timegm(time.localtime()) - calendar.timegm(time.gmtime()) self.utcoffset = tmp if tmp > 0 else 24*60*60 - tmp # packInt can't do negative numbers. self.os = 1 if IS_WINDOWS else (2 if IS_LINUX else (3 if IS_OSX else 0)) self.sendLock = threading.Lock() # Protect self.sequence/self.tcpCommandsWaitingForResponse in sync if extensions
def runFBA(self, quark): self.checkRom() self.fba = findFba() if not self.fba: self.sigStatusMessage.emit( "ERROR: make sure ggpofba-ng.exe is in the same folder as FightCade" ) return args = [] fba = self.fba if IS_WINDOWS: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.exe') else: fba = fba.replace('ggpofba-ng.exe', 'ggpofba.sh') args = [fba, quark, '-w'] fbaini = os.path.join(os.path.dirname(self.fba), 'config', 'ggpofba-ng.ini') fbadat = os.path.join(os.path.dirname(self.fba), 'config', 'ggpofba-ng.roms.dat') fbainibkp = os.path.join(os.path.abspath(os.path.expanduser("~")), 'ggpofba-ng.bkp.ini') fbainidef = os.path.join(os.path.dirname(self.fba), 'config', 'ggpofba-ng.default.ini') # if dat file doesn't exist, restore FBA settings from backup if not os.path.isfile(fbadat) and os.path.isfile(fbainibkp): self.sigStatusMessage.emit( "Trying to restore emulator config. If game doesn't start do Settings -> Locate ROMs folder." ) copyfile(fbainibkp, fbaini) # if dat file doesn't exist and there was no backup, use the default FBA config if not os.path.isfile(fbadat) and not os.path.isfile( fbaini) and not os.path.isfile(fbainibkp) and os.path.isfile( fbainidef): copyfile(fbainidef, fbaini) #on linux & MAC, symlink the ROMs folder to avoid configuring FBA if not IS_WINDOWS: romdir = Settings.value(Settings.ROMS_DIR) if romdir: fbaRomPath = os.path.join( os.path.abspath(os.path.dirname(sys.argv[0])), "ROMs") # remove it if it's a link or an empty dir if os.path.islink(fbaRomPath): os.remove(fbaRomPath) if os.path.isdir( fbaRomPath) and not os.listdir(fbaRomPath): os.rmdir(fbaRomPath) if not os.path.exists(fbaRomPath): os.symlink(romdir, fbaRomPath) logdebug().info(" ".join(args)) try: # starting python from cmd.exe and redirect stderr and we got # python WindowsError(6, 'The handle is invalid') # apparently it's still not fixed if IS_WINDOWS: Popen(args, shell=True) else: devnull = open(os.devnull, 'w') Popen(args, stdout=devnull, stderr=devnull) devnull.close() except OSError, ex: self.sigStatusMessage.emit("Error executing " + " ".join(args) + "\n" + repr(ex))
def sendAndRemember(self, command, data=''): with self.sendLock: # extensions may send messages from separate threads logdebug().info('Sending {} seq {} {}'.format(Protocol.codeToString(command), self.sequence, repr(data))) self.tcpCommandsWaitingForResponse[self.sequence] = command self.sendtcp(struct.pack('!I', command) + data)
def sendAndRemember(self, command, data=''): logdebug().info('Sending {} seq {} {}'.format(Protocol.codeToString(command), self.sequence, repr(data))) self.tcpCommandsWaitingForResponse[self.sequence] = command self.sendtcp(struct.pack('!I', command) + data)
def sendAndForget(self, command, data=''): logdebug().info('Sending {} seq {} {}'.format(Protocol.codeToString(command), self.sequence, repr(data))) self.sendtcp(struct.pack('!I', command) + data)
def sendAndForget(self, command, data=''): logdebug().info('Sending {} seq {} {}'.format( Protocol.codeToString(command), self.sequence, repr(data))) self.sendtcp(struct.pack('!I', command) + data)
def sendAndRemember(self, command, data=''): logdebug().info('Sending {} seq {} {}'.format( Protocol.codeToString(command), self.sequence, repr(data))) self.tcpCommandsWaitingForResponse[self.sequence] = command self.sendtcp(struct.pack('!I', command) + data)