Last active
March 3, 2017 00:08
-
-
Save trysdyn/3d9dc8c9951e7ce87227c9d302bcd488 to your computer and use it in GitHub Desktop.
This is a python (2.7) script that utilizes datafiles saved by Copy Kitty version 2.x to feed split data into LiveSplit. Requires Python 2.7, Livesplit, Livesplit Server, and Copy Kitty 2.x. 1.9 may work but is not supported.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copy Kitty 2.x IGT auto-splitter | |
# Utilizes LiveSplit and its Server component to auto-split a Copy Kitty run | |
# Built for version 2.1, should work on anything 2.x | |
### BEGIN USER-ACCESSIBLE CONFIGS ### | |
# Edit this to be the directory in which your saves are stored | |
infile = "D:\\steam\\steamapps\\common\\copy kitty\\saves + levels" | |
# If this is empty, the script will split every stage | |
# If you put something here, it will only split AFTER that stage | |
split_at = [] | |
# This will split on every stage in world 1 | |
#split_at = [1, 2, 3, 4, 5, 6, 7] | |
### END USER-ACCESSIBLE CONFIGS, THERE BE DRAGONS BEYOND ### | |
import time | |
import struct | |
import socket | |
from watchdog.observers import Observer | |
from watchdog.events import FileSystemEventHandler | |
class BokiHandler(FileSystemEventHandler): | |
# Data locations in the bytestream in the savefile | |
handle = {"started": 0, "stage": 2, "frames": 9, "seconds": 10, | |
"minutes": 11, "hours": 12, "days": 13, "weeks": 14} | |
last_stage = -1 | |
def on_modified(self, event): | |
# If the splitfile got edited, parse it | |
if event.src_path.endswith("split"): | |
data = self.parse_savefile(event.src_path) | |
# If we started the first stage, send starttimer and return | |
# This makes Real Time accurate too, though we don't care about RTA | |
if data["started"]: | |
s.send("reset\r\n") | |
s.send("starttimer\r\n") | |
print "Starting Real Timer" | |
return | |
# If the file updated but stage didn't change, something went odd | |
# abort in this case without doing anything | |
if data["stage"] == self.last_stage: | |
return | |
# Record what stage we just finished | |
self.last_stage = data["stage"] | |
# Figure out if we want to split on this stage | |
# By default we split every stage. If split_at is populated, we only | |
# split if the listed stages are completed instead | |
if len(split_at) and data["stage"] not in split_at: | |
return | |
# Figure out how many seconds in gametime we're in | |
sec = ((data["weeks"] * 86400 * 7) + | |
(data["days"] * 86400) + | |
(data["hours"] * 3600) + | |
(data["minutes"] * 60) + | |
data["seconds"] + | |
(data["frames"] * (1 / 60.0))) | |
# Send the split command to livesplit | |
print "Sending split at %02i:%02i:%02i" % (data["hours"], | |
data["minutes"], | |
data["seconds"]) | |
s.send("setgametime %f\r\n" % sec) | |
s.send("split\r\n") | |
def parse_savefile(self, filename): | |
# Datafile format: | |
# bool: Are we on the starting stage? | |
# int: starting stage | |
# int: last finished stage | |
# 6 ints: stage time in frames, sec, min, hr, dy, wk | |
# 6 ints: total time in frames, sec, min, hr, dy, wk | |
with open(filename, 'rb') as f: | |
data = struct.unpack("<b" + "i" * 14, f.read()) | |
# Use the information in handle dict to construct a data dict | |
packed_data = {} | |
for k, v in self.handle.items(): | |
packed_data[k] = data[v] | |
return packed_data | |
if __name__ == "__main__": | |
s = socket.socket() | |
# Fire up our livesplit socket | |
try: | |
s.connect(("localhost", 16834)) | |
print "Connected to LiveSplit" | |
except: | |
print "Could not connect to LiveSplit. Is it running?" | |
# Here we set a hook to fire BokiHandler.on_modified when files | |
# in CopyKitty's save dir change. | |
event_handler = BokiHandler() | |
observer = Observer() | |
observer.schedule(event_handler, path=infile, recursive=False) | |
observer.start() | |
# Now we check once a second for a ctrl-C to terminate | |
try: | |
while True: | |
time.sleep(1) | |
except KeyboardInterrupt: | |
print "Ctrl-C caught. Exiting" | |
finally: | |
observer.stop() | |
s.close() | |
observer.join() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment