Skip to content

Instantly share code, notes, and snippets.

@monajalal
Created January 14, 2017 02:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save monajalal/99afa764d5956b6e2aa0a5e31ec76879 to your computer and use it in GitHub Desktop.
Save monajalal/99afa764d5956b6e2aa0a5e31ec76879 to your computer and use it in GitHub Desktop.
very very very slightly modified version so it wouldn't download stuff and copy them everytime
#!/usr/bin/python
from __future__ import print_function
import argparse
import json
import logging
import os
import subprocess
import sys
import csv
import traceback
import urllib
import urllib2
import zipfile
from sys import platform as _platform
import time
import six
# TODO: Rearrange directories for a proper import
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(os.path.abspath(os.path.join(CURRENT_DIR, os.pardir)), 'vnc-windows'))
import gym_windows
from enforce_version import enforce_version
logger = logging.getLogger(__name__)
### Constants ####
GTAV_DIR = os.environ['GTAV_DIR']
GTAV_CONTROLLER_DIR = CURRENT_DIR + r"/GTAVController"
INJECT_DIR = GTAV_CONTROLLER_DIR + r"/inject"
IS_STEAM = 'steam' in GTAV_DIR.lower()
CONTROLLER_PROCESS_NAME = 'GTAVController.exe'
TIGHTVNC_PROCESS_NAME = 'tvnserver.exe'
GTAV_PROCESS_NAME = 'GTA5.exe'
ALL_PROCESS_NAMES = [TIGHTVNC_PROCESS_NAME, CONTROLLER_PROCESS_NAME, GTAV_PROCESS_NAME]
def _start_gtav_command():
"""
This command needs to change directory to GTAV_DIR in order to properly find the maps that we need.
Therefore it always needs to be called with shell=True
Injectory command flags here: https://github.com/blole/injectory/tree/651700018750c4f2003f1f048b090c4d521717a6
"""
if IS_STEAM:
start_command = r'start steam://rungameid/271590'
else:
start_command = r'"{GTAV_DIR}/PlayGTAV.exe"'.format(GTAV_DIR=GTAV_DIR)
return start_command
def _gtav_is_running():
p_tasklist = subprocess.Popen('tasklist.exe /fo csv',
stdout=subprocess.PIPE,
universal_newlines=True)
pythons_tasklist = []
tasks = list(csv.DictReader(p_tasklist.stdout))
for p in tasks:
if p['Image Name'] == GTAV_PROCESS_NAME:
pythons_tasklist.append(p)
if len(pythons_tasklist) > 0:
return True
else:
return False
# TODO: Create base class in gym_windows
class GTAVEnvRunner(object):
def __init__(self, env_id, instance_id, skip_loading_saved_game, pause_for_attach, rewards_per_second, scenario):
if _platform != "win32":
raise AttributeError("GTAV only runs on Windows")
self.env_id = env_id
self.instance_id = instance_id
self.skip_loading_saved_game = skip_loading_saved_game
self.pause_for_attach = pause_for_attach
self.vnc_port = 5900
self.websocket_port = 15900
self.rewards_per_second = rewards_per_second
self.scenario = scenario
self.tightvnc_server = None
self.tightvnc_config = None
self.gtav_proc = None
self.gtav_controller = None
def _configure(self):
self.popen()
def popen(self):
subprocess.call("taskkill /IM %s /F" % CONTROLLER_PROCESS_NAME)
if _gtav_is_running():
logger.info("GTAV already running. Will use existing instance: /n{}".format(_start_gtav_command()))
else:
self._kill_competing_procs()
# Kill existing servers that may be running
logger.info("Starting GTAV with the following command: /n{}".format(_start_gtav_command()))
self.gtav_proc = subprocess.Popen(_start_gtav_command(), shell=True) # Needs shell for `cd`
while not gym_windows.processes_are_running(GTAV_PROCESS_NAME):
logger.info('Waiting for %s to start', GTAV_PROCESS_NAME)
subprocess.call("taskkill /IM %s /F" % TIGHTVNC_PROCESS_NAME)
self.tightvnc_server, self.tightvnc_config = gym_windows.configure_and_start_tightvnc(
logger, 'Grand Theft Auto V', self.vnc_port)
gtav_controller_command = self._start_gtav_controller_command()
gtav_env = self.get_gtav_env()
logger.info("Starting GTAVController with the following command: {command}".
format(command=gtav_controller_command))
self.gtav_controller = subprocess.Popen(gtav_controller_command, shell=True, env=gtav_env)
def _start_gtav_controller_command(self):
return r'"{GTAV_CONTROLLER_DIR}/bin/{CONTROLLER_PROCESS_NAME}" {env_id} {instance_id} {websocket_port} {rewards_per_second} {skip_loading_saved_game} {pause_for_attach} {scenario}'.format(
GTAV_CONTROLLER_DIR=GTAV_CONTROLLER_DIR,
env_id=self.env_id,
instance_id=self.instance_id,
websocket_port=self.websocket_port,
skip_loading_saved_game=json.dumps(self.skip_loading_saved_game),
rewards_per_second=self.rewards_per_second,
pause_for_attach=json.dumps(self.pause_for_attach),
scenario=self.scenario,
CONTROLLER_PROCESS_NAME=CONTROLLER_PROCESS_NAME,
)
@staticmethod
def _kill_competing_procs():
subprocess.call("taskkill /IM %s /F" % TIGHTVNC_PROCESS_NAME)
subprocess.call("taskkill /IM %s /F" % CONTROLLER_PROCESS_NAME)
# Don't kill steam as this can require restart to get GTA running again
def popen_cleanup(self):
for proc in [self.tightvnc_server, self.tightvnc_config, self.gtav_controller]:
if proc:
proc.kill()
proc.wait()
# TODONT kill self.gta_proc as we can reuse the process and it takes ~1 minute to start
# Kill procs that we might have missed
# TODO: Remove this for multitenancy
self._kill_competing_procs()
def get_gtav_env(self):
env = os.environ.copy()
env['PATH'] += r';C:\Program Files\vJoy\x86;C:\Program Files (x86)\AutoIt3\AutoItX;'
return env
def try_stuff(fn, clean):
try:
fn()
except:
exc_info = sys.exc_info()
try:
clean()
except:
# If this happens, it clobbers exc_info, which is why we had
# to save it above
import traceback
logger.error('Error cleaning up gtav env processes')
traceback.print_exc()
six.reraise(*exc_info)
def send_hearbeat(last_ping):
ping_diff = None
ping_period_seconds = 15 * 60
if last_ping is not None:
ping_diff = time.time() - last_ping
if ping_diff is None or ping_diff > ping_period_seconds:
logging.info('Sending heartbeat')
try:
last_ping = time.time()
urllib2.urlopen(os.environ['GTAV_DEAD_MANS_SNITCH_URL']).read()
except Exception as e:
logging.error('Error sending heartbeat \n' + traceback.format_exc())
return last_ping
def main():
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
parser.add_argument('-e', '--env_id', default='gtav.SaneDriving-v0',
help='What GTAV sub-environment do you want to run?')
parser.add_argument('-s', '--skip-loading-saved-game', action='store_true',
help='Do you want to skip reloading the saved game (useful for debugging)?')
parser.add_argument('-p', '--pause-for-attach', action='store_true',
help='Do you want to pause the controller before running the environment so we can attach a debugger?')
parser.add_argument('-i', '--instance_id', default='env_PLACEHOLDER_ID',
help='What is the id of this instance of the environment?')
parser.add_argument('-r', '--rewards_per_second', default=8., help='How many rewards per second?')
parser.add_argument('-sc', '--scenario', default='', help='The scenario you want to run')
args = parser.parse_args()
logging.basicConfig()
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
#enforce_version(GTAV_DIR)
runner = GTAVEnvRunner(args.env_id, args.instance_id, args.skip_loading_saved_game, args.pause_for_attach,
args.rewards_per_second, args.scenario)
try_stuff(runner.popen, runner.popen_cleanup)
last_heartbeat = None
started = False
while True:
running_procs = gym_windows.get_running_processes(ALL_PROCESS_NAMES)
if len(running_procs) == len(ALL_PROCESS_NAMES):
started = True
if 'GTAV_DEAD_MANS_SNITCH_URL' in os.environ:
last_heartbeat = send_hearbeat(last_heartbeat)
elif started:
missing = str(list(set(ALL_PROCESS_NAMES) - set(running_procs)))
runner.popen_cleanup()
raise Exception('Environment crashed due to the following failed processes: ' + missing)
time.sleep(5)
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment