def __init__(self, roms_path, game_id, render=True, throttle=False, debug=False): self.logger = logging.getLogger("Console") command = f"exec ./mame -rompath '{str(Path(roms_path).absolute())}' -pluginspath plugins -skip_gameinfo -sound none -console " + game_id if not render: command += " -video none" if throttle: command += " -throttle" else: command += " -frameskip 10" # Start lua console script_path = os.path.dirname(os.path.abspath(__file__)) self.process = Popen(command, cwd=f"{script_path}/mame", shell=True, stdin=PIPE, stdout=PIPE) # Start read queues self.stdout_queue = queue.Queue() self.gobbler = StreamGobbler(self.process.stdout, self.stdout_queue, debug=debug) self.gobbler.wait_for_cursor() self.gobbler.start()
def __init__(self, roms_path, game_id, cheat_debugger=False, render=True, throttle=False, frame_skip=0, sound=False, debug=False, binary_path=None): self.logger = logging.getLogger("Console") mame_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mame") if binary_path is None: binary_path = "./mame" else: binary_path = str(Path(binary_path).absolute()) command = f"exec {binary_path} -rompath '{str(Path(roms_path).absolute())}' -pluginspath plugins -skip_gameinfo -window -nomaximize -console " + game_id if not render: command += " -video none" if cheat_debugger: command += " -debug" if throttle: command += " -throttle" else: command += " -nothrottle" command += " -frameskip " + str(frame_skip) if not sound: command += " -sound none" # Start lua console self.process = Popen(command, cwd=mame_path, shell=True, stdin=PIPE, stdout=PIPE) # Start read queues self.stdout_queue = queue.Queue() self.gobbler = StreamGobbler(self.process.stdout, self.stdout_queue, debug=debug) self.gobbler.wait_for_cursor() self.gobbler.start()
def open(self, console): try: lua_mode = 'r' if self.mode == 'w' else 'w' # Open Console pipe console.writeln(self.pipeId + ' = assert(io.open("' + str(self.path.absolute()) + '", "' + lua_mode + '"))') self.logger.info("Opened lua pipe") # Read bytes not strings pipe_queue = Queue() open_thread = Thread( target=open_pipe, args=[pipe_queue, str(self.path.absolute()), self.mode]) open_thread.start() open_thread.join(timeout=3) self.fifo = pipe_queue.get(timeout=1) self.logger.info("Opened local pipe") if self.mode == "r": self.read_queue = Queue() StreamGobbler(self.fifo, self.read_queue).start() except Exception as e: error = "Failed to open pipe '" + str(self.path.absolute()) + "'" self.logger.error(error) raise IOError(error)
def run_gobbler(lines, output_queue): pipe = MockPipe(lines) line_queue = queue.Queue() gobbler = None try: gobbler = StreamGobbler(pipe, line_queue) gobbler.start() gobbler.wait_for_cursor() for _ in range(3): output_queue.put(line_queue.get(timeout=0.1)) finally: gobbler.stop()
def test_read_lines(self): lines = [b"test1\n", b"test2\n", b"test3\n"] pipe = MockPipe(lines) line_queue = queue.Queue() gobbler = None try: gobbler = StreamGobbler(pipe, line_queue) gobbler.start() gobbler.wait_for_cursor() for i in range(3): assert_that(line_queue.get(timeout=0.1), is_(equal_to(lines[i][:-1]))) finally: gobbler.stop()
class Console(object): # Starts up an instance of MAME with POpen # Uses a separate thread for reading from the console outputs # render is for displaying the frames to the emulator window, disabling it has little to no effect # throttle enabled will run any game at the intended gameplay speed, disabling it will run the game as fast as the computer can handle # debug enabled will print everything that comes out of the Lua engine console def __init__(self, roms_path, game_id, cheat_debugger=False, render=True, throttle=False, debug=False): self.logger = logging.getLogger("Console") command = f"exec ./mame -rompath '{str(Path(roms_path).absolute())}' -pluginspath plugins -skip_gameinfo -sound none -console "+game_id if not render: command += " -video none" if cheat_debugger: command += " -debug" if throttle: command += " -throttle" else: command += " -frameskip 10" # Start lua console script_path = os.path.dirname(os.path.abspath(__file__)) self.process = Popen(command, cwd=f"{script_path}/mame", shell=True, stdin=PIPE, stdout=PIPE) # Start read queues self.stdout_queue = queue.Queue() self.gobbler = StreamGobbler(self.process.stdout, self.stdout_queue, debug=debug) self.gobbler.wait_for_cursor() self.gobbler.start() # Read the oldest line which may have been output by the console # Uses the FIFO principle, once a line is read it is removed from the queue # timeout determines how long the function will wait for an output if there is nothing immediately available def readln(self, timeout=0.5): line = self.stdout_queue.get(timeout=timeout) while len(line)>0 and line[0] == 27: line = line[19:] return line.decode("utf-8") # Read as many lines from the console as there are available # timeout determines how long the function will wait for an output if there is nothing immediately available def readAll(self, timeout=0.5): lines = [] while True: try: lines.append(self.readln(timeout=timeout)) except queue.Empty as e: break return lines def writeln(self, command, expect_output=False, timeout=0.5, raiseError=True): self.process.stdin.write(command.encode("utf-8") + b'\n') self.process.stdin.flush() output = self.readAll(timeout=timeout) if expect_output and len(output) == 0: error = "Expected output but received nothing from emulator after '" + command + "'" if raiseError: self.logger.error(error) raise IOError(error) else: return None if not expect_output and len(output) > 0: error = "No output expected from command '" + command + "', but recieved: " + "\n".join(output) if raiseError: self.logger.error(error) raise IOError(error) else: return None if expect_output: return output # Mainly for testing # Safely kills the emulator process def close(self): self.process.kill() try: self.process.wait(timeout=3) except Exception as e: error = "Failed to close emulator console" self.logger.error(error, e) raise EnvironmentError(error) self.gobbler.stop()