Source code for valveexe.exe
import os
import uuid
import time
import subprocess
import psutil
import glob
from rcon import Client
from valveexe.utils import find_process, terminate_process
from valveexe.logger import Logger
from valveexe.console import RconConsole, ExecConsole
[docs]class ValveExe(object):
[docs] def __init__(self, gameExe, gameDir, steamExe=None, appid=None):
'''Defines a launchable source engine game to be interacted with.
.. note:: Some games cannot be launched by their .exe alone \
(ex:csgo, probably for anti-cheat related reasons). \
Those games need to include the optional parameters :any:`steamExe` \
and :any:`appid`. Those parameters are only to be used if \
absolutely needed, they are a fallback and will downgrade ValveEXE \
functionnality if present.
:param gameExe: the path for the game executable.
:type gameExe: path, str
:param gameDir: The mod directory.
:type gameDir: path, str
:param steamExe: The path for the Steam executable.
:type steamExe: optional, path, str
:param appid: The `Steam AppID <https://developer.valvesoftware.com/wiki/Steam_Application_IDs>`_.
:type appid: optional, int
'''
self.gameExe = gameExe
self.gameDir = gameDir
self.exeName = self.gameExe.split('\\')[-1]
self.appid = appid
self.steamExe = steamExe
self.uuid = str(uuid.uuid4()).split('-')[-1]
self.logName = 'valve-exe-' + self.uuid + '.log'
self.logPath = os.path.join(gameDir, self.logName)
self.console = None
self.rcon_enabled = None
self.hijacked = None
self._full_cleanup()
[docs] def launch(self, *params):
'''Launches the game as specified in :any:`__init__` with the
launch parameters supplied as arguments.
:param \*params: The launch parameters to be supplied to the executable.
:type \*params: str
'''
if self.steamExe and self.appid:
# Steam launches cannot be hijacked
terminate_process(self.exeName)
launch_params = [self.steamExe, '-applaunch', str(self.appid)]
else:
self.hijacked = bool(find_process(self.exeName))
launch_params = [self.gameExe, '-hijack']
launch_params.extend(['-game', self.gameDir])
launch_params.extend(['+log', '0', '+sv_logflush', '1',
'+con_logfile', self.logName])
if self._check_rcon_eligible() is not False:
launch_params.extend(['-usercon', '+ip', '0.0.0.0',
'+rcon_password', self.uuid])
launch_params.extend(list(*params))
self.process = subprocess.Popen(
launch_params,
creationflags=subprocess.DETACHED_PROCESS |
subprocess.CREATE_NEW_PROCESS_GROUP)
while not os.path.exists(self.logPath):
time.sleep(3)
self.logger = Logger(self.logPath)
[docs] def run(self, command, *params):
'''Forwards a command with its parameters to the active :any:`VConsole`
:param command: A Source Engine `console command \
<https://developer.valvesoftware.com/wiki/Console_Command_List>`_.
:type command: str
:param \*params: The values to be included with the command.
:type \*params: str
'''
if not self.process:
return
if self.console:
self.console.run(command, *params)
else:
with self as console:
console.run(command, *params)
[docs] def quit(self):
'''Closes the game client'''
process = self.process or find_process(self.exeName)
if process:
process.terminate()
self.process = None
def _check_rcon_eligible(self):
'''
None: Unknown
True: Eligible
False: Not eligible
'''
process = find_process(self.exeName)
if not process:
# no process running
return None
elif self.gameDir not in process.cmdline() and \
self.gameDir.split('\\')[-1] not in process.cmdline():
# wrong game
process.terminate()
return None
elif '-usercon' not in process.cmdline():
# doesn't have rcon enabled
return False
else:
# 'connections' confirms game is listening for rcon
return bool(process.connections())
def __enter__(self):
while self._check_rcon_eligible() is None:
time.sleep(3)
if self._check_rcon_eligible():
self.console = RconConsole("127.0.0.1", 27015, self.uuid)
else:
self.console = ExecConsole(self.gameExe, self.gameDir, self.uuid)
self.console.__enter__()
return self.console
def __exit__(self, exc_type, exc_val, exc_tb):
self.console.__exit__(exc_type, exc_val, exc_tb)
self.console = None
def __del__(self):
try:
self.run('con_logfile', '""')
except:
pass
del self.logger
def _full_cleanup(self):
for f in glob.glob(self.gameDir + 'valve-exe-*.log'):
try:
os.remove(f)
except:
pass