import ini, packets import os, time from PyQt4 import QtCore, QtGui from bisect import bisect_left class DemoPlayer(QtCore.QObject): MS_Chat = QtCore.pyqtSignal(list) newChar = QtCore.pyqtSignal(str) newBackground = QtCore.pyqtSignal(str, bool) IC_Log = QtCore.pyqtSignal(str) OOC_Log = QtCore.pyqtSignal(str) charSlots = QtCore.pyqtSignal() allEvidence = QtCore.pyqtSignal(list) rainbowColor = QtCore.pyqtSignal(str) updatePlayerList = QtCore.pyqtSignal(str, int, int, str) timerUpdate = QtCore.pyqtSignal(int, int, int) def __init__(self, parent): super(DemoPlayer, self).__init__(parent) self.parent = parent self.paused = False self.demo = [] self.demo_length = len(self.demo) self.time = 0 self.demo_length_ms = 0 self.wait_timer = QtCore.QTimer(self) self.wait_timer.setSingleShot(True) self.wait_timer.timeout.connect(self.timer_done) self.mc = [] # Music changes self.bn = [] # Background changes self.last_music = "" self.last_bg = "" def start(self, file): self.paused = True self.wait_timer.stop() self.time = 0 self.demo_length_ms = 0 self.demo = [] self.mc = [] self.bn = [] self.last_music = "" self.last_bg = "" self.load_demo(file) print "[client] Started demo playback (%s commands)" % self.demo_length self.parent.demoslider.setMaximum(self.demo_length - 1) self.OOC_Log.emit("Demo playback started.") secs = self.demo_length_ms / 1000 mins = secs / 60 hours = mins / 60 self.OOC_Log.emit("Approximate duration: %02d:%02d:%02d." % (hours, mins % 60, secs % 60)) self.OOC_Log.emit("") self.step() def playpause(self): self.paused = not self.paused if not self.paused and self.time < self.demo_length: self.step() def step(self, skip_wait=False): if self.time >= self.demo_length: self.time = 0 return packet = self.demo[self.time] self.parent.demoslider.blockSignals(True) self.parent.demoslider.setValue(self.time) self.parent.demoslider.blockSignals(False) self.time += 1 self.paused = False if packet[0] == "wait": if skip_wait: self.time += 1 else: self.wait_timer.start(int(packet[1])) return packets.handle_packets(self, [packet], False) if self.time < self.demo_length: self.wait_timer.start(1) else: self.OOC_Log.emit("Demo playback finished.") def seek(self, time): self.parent.inbox_timer.stop() self.parent.inboxqueue = [] self.wait_timer.stop() self.time = time mc_times = [t[0] for t in self.mc] t = bisect_left(mc_times, self.time) - 1 if t >= 0: music = self.mc[t][1][1] if music != self.last_music: packets.handle_packets(self, [self.mc[t][1]], False) self.last_music = music bn_times = [t[0] for t in self.bn] t = bisect_left(bn_times, self.time) - 1 if t >= 0: bg = self.bn[t][1][1] if bg != self.last_bg: packets.handle_packets(self, [self.bn[t][1]], False) self.last_bg = bg self.step() def load_demo(self, file): last_line = "" time = 0 with open("logs/" + file) as f: for line in f: last_line = last_line + line if last_line.strip()[-1] == "%": packet = last_line.split("#")[:-1] self.demo.append(packet) if packet[0] == "MC": self.mc.append((time, packet)) elif packet[0] == "BN": self.bn.append((time, packet)) elif packet[0] == "wait": self.demo_length_ms += int(packet[1]) last_line = "" time += 1 self.demo_length = len(self.demo) def timer_done(self): if self.paused: return self.step() def stop(self): self.paused = True self.wait_timer.stop() class DemoRecorder(): def __init__(self): self.demofile = None self.lasttime = 0 def start(self): if not os.path.exists("logs"): os.mkdir("logs") currtime = time.localtime() self.lasttime = time.time() * 1000 self.demofile = "logs/%d-%02d-%02d %02d.%02d.%02d.demo" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5]) def record(self, packet, encode=False): if packet[0][0] in ["FM", "ARUP", "CharsCheck"]: return #print packet[0][0] with open(self.demofile, "a") as demofile: currtime = time.time() * 1000 diff = currtime - self.lasttime self.lasttime = currtime demofile.write(("wait#%d#%%" % diff) + "\n") line = "#".join(packet[0]) + "#%\n" if encode: line = line.encode('utf-8') demofile.write(line) class DemoPicker(QtGui.QDialog): def __init__(self, parent): super(DemoPicker, self).__init__() self.setModal(True) self.gamewindow = parent self.parent = parent self.setWindowTitle("Select a demo file...") self.setFixedSize(480, 512) self.setWindowIcon(QtGui.QIcon("AO2XP.ico")) self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint) self.hide() self.treeview = get_demo_treeview() self.treeview.doubleClicked.connect(self.on_demo_click) self.cancelbtn = QtGui.QPushButton() self.cancelbtn.setText("Cancel") self.cancelbtn.clicked.connect(self.hide) main_layout = QtGui.QVBoxLayout(self) btns_layout = QtGui.QHBoxLayout() btns_layout.addWidget(self.cancelbtn, 0, QtCore.Qt.AlignRight) main_layout.addWidget(self.treeview) main_layout.addLayout(btns_layout) self.cancelbtn.setFocus() def on_demo_click(self, item): fname = get_demo_fname(self.treeview, item) if not fname: return self.gamewindow.gamewidget.start_demo(fname) self.gamewindow.stackwidget.setCurrentWidget(self.gamewindow.gamewidget) self.hide() def get_demo_treeview(): demo_model = QtGui.QFileSystemModel(); demo_model.setRootPath("logs"); demo_model.setFilter(QtCore.QDir.Files | QtCore.QDir.AllDirs | QtCore.QDir.NoDotAndDotDot) demo_model.setNameFilters(QtCore.QStringList("*.demo")) demo_model.setNameFilterDisables(False) demo_treeview = QtGui.QTreeView() demo_treeview.setHeaderHidden(True) demo_treeview.setModel(demo_model); demo_treeview.setRootIndex(demo_model.index("logs")); for i in range(1, demo_model.columnCount()): demo_treeview.hideColumn(i) return demo_treeview def get_demo_fname(treeview, item): model = treeview.model() if model.isDir(item): return None path = QtCore.QStringList() while item.isValid(): fname = model.fileName(item) if fname == "logs": break path.insert(0, fname) item = item.parent() return path.join("/")