359 lines
16 KiB
Python
359 lines
16 KiB
Python
#!/home/autismbot1/ze_runner/venv/bin/python3
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import atexit
|
|
from watchdog.observers import Observer
|
|
from watchdog.events import FileSystemEventHandler
|
|
import random
|
|
import signal
|
|
import socket
|
|
import codecs
|
|
import json
|
|
import datetime
|
|
import time
|
|
import glob
|
|
|
|
restart_time = datetime.datetime.now() + datetime.timedelta(hours=3)
|
|
block_connection = False
|
|
the_undesired_crash_counter = 0 #hate this monkey solution
|
|
|
|
whoami = subprocess.getoutput(["whoami"])
|
|
with open(f'/home/{whoami}/ze_runner/config.json') as jsonfile:
|
|
data_ports = json.load(jsonfile)
|
|
looptestPath = f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/cfg/looptest.cfg"
|
|
chatmsg = ""
|
|
|
|
def overwrite_file_access():
|
|
#setting defaults with setfacl for the download directory does not work seemingly
|
|
subprocess.Popen(["chmod", "-R", "g+rwx", f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/download"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()
|
|
|
|
def writeCfgInput(Input_user):
|
|
with open(looptestPath, 'w') as f:
|
|
f.write(Input_user)
|
|
#print("wrote to file: ", Input_user)
|
|
if "connect" in Input_user or "retry;" in Input_user:
|
|
#give extra time for disconnect, retry and connect commands
|
|
time.sleep(1.0)
|
|
else:
|
|
time.sleep(0.35)
|
|
open(looptestPath, 'w').close() #clearing file.
|
|
|
|
def close_observer(observer):
|
|
observer.stop()
|
|
observer.join() #supposedly needed to clear resources again.
|
|
|
|
def clean_up_files():
|
|
#deleting POSIX shared memory objects, as long as one process has them open they exist. THis is to prevent /dev/shm from being full
|
|
#due to steam child processes.
|
|
#even with steam turned offline might there be chromium web browsers left over from steam who still hold processes open.
|
|
#only kind of potential issues from this is steam cloud being out of sync, which is really fucking irrelevant.
|
|
subprocess.Popen(["rm -rf /tmp/steam*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
subprocess.Popen(["rm -rf /tmp/dbus*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
subprocess.Popen(["rm -rf /tmp/pressure*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
subprocess.Popen(["rm -rf /tmp/tigervnc*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
subprocess.Popen(["rm -rf /tmp/dumps*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
subprocess.Popen(["rm -rf /dev/shm/u100*"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()[0]
|
|
#users should only have permissions to delete their own files probably
|
|
|
|
#check if any bz2 files left over.
|
|
for f in glob.glob(f"/home/{whoami}/.steam/steam/steamapps/common/Counter-Strike Source/cstrike/download/**/*.bz2", recursive=True):
|
|
file_size = None
|
|
while True:
|
|
time.sleep(10)
|
|
stdout, stderr = subprocess.Popen(["ls", "-l", f], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
|
if stderr: break #finished downloading, bz2 file is removed.
|
|
if stdout:
|
|
cur_file_size = stdout.decode().split("autismbots")[1].strip().split(" ")[0]
|
|
if file_size == cur_file_size:
|
|
#delete the bz2 file if its not progressing downloading
|
|
#print("deleting file: ", f)
|
|
subprocess.Popen(["rm", "-rf", f], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()
|
|
break
|
|
file_size = cur_file_size
|
|
|
|
#clean up the game cache as it otherwise just keeps growing in gigabyte size
|
|
subprocess.Popen(["rm", "-rf", f"/home/{whoami}/.steam/steam/steamapps/common/Counter-Strike Source/cstrike/cache"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()
|
|
|
|
def exit_handler():
|
|
print('reached exithandler')
|
|
#securing the looptest.cfg wont be stuck accidently with commands
|
|
kill_owned_process("pidof cstrike_linux64")
|
|
kill_owned_process("pidof xterm")
|
|
|
|
clean_up_files()
|
|
subprocess.getoutput([f"pkill -9 -u {whoami}"])
|
|
|
|
def bot_process_movement(input_line):
|
|
dist_target = input_line[input_line.index("dist_target:") + len("dist_target:"):input_line.index("enemy_distance")]
|
|
enemy_distance = input_line[input_line.index("enemy_distance:") + len("enemy_distance:"):input_line.index("targeteam:")]
|
|
targeteam = input_line[input_line.index("targeteam:") + len("targeteam:"):input_line.index("state:")]
|
|
state = input_line[input_line.index("state:") + len("state:"):]
|
|
|
|
state = int(state.strip())
|
|
dist_target = float(dist_target)
|
|
enemy_distance = float(enemy_distance)
|
|
targeteam = int(targeteam)
|
|
#request by bane that bots should simply not infect people shrug
|
|
strInput = "-attack; wait 2; -use; wait 5; -attack; wait 5; cl_minmodels 1; wait 2; +use; wait 5; "
|
|
if targeteam == 3:
|
|
strInput = "-attack; wait 2; -use; wait 5; -attack; wait 5; cl_minmodels 1; wait 2; +use; wait 5; "
|
|
#python has no switches and such
|
|
if state in [5, 7]:
|
|
strInput += "-forward; wait 2; use weapon_knife; wait 5;"
|
|
elif state >= 3:
|
|
strInput += "use weapon_knife; wait 5; +forward; wait 2;"
|
|
elif state == 0:
|
|
strInput += "use weapon_p90; wait 2; -forward; wait 2;"
|
|
elif state == 1:
|
|
strInput += "wait 2; use weapon_elite; wait 3; +forward; wait 2;"
|
|
elif state == 2:
|
|
strInput += "-forward; wait 2; use weapon_elite; wait 3;"
|
|
elif state == 8:
|
|
strInput += "use weapon_knife; wait 5; +forward; wait 2;"
|
|
|
|
#print('dist_target: ', dist_target, ' enemy distance: ', enemy_distance, ' targeteam: ', targeteam, ' state:', state)
|
|
if state not in [0, 2, 5, 7, 8]:
|
|
strInput = strinput_append(strInput, 2)
|
|
#print('strInput final:', strInput)
|
|
writeCfgInput(strInput)
|
|
|
|
def strinput_append(strInput, nth):
|
|
for _ in range(3 * nth):
|
|
boolean_val = random.choice([True, False])
|
|
if boolean_val:
|
|
strInput += " +moveleft; wait 15; -moveleft; "
|
|
else:
|
|
strInput += " +moveright; wait 15; -moveright; "
|
|
return strInput
|
|
|
|
def kill_user_owned_pid(pid):
|
|
#print('pid: ', pid, ' killed')
|
|
pid = int(pid.strip())
|
|
os.kill(pid, signal.SIGKILL)
|
|
time.sleep(10)
|
|
|
|
def return_user_owned_pid(pidof):
|
|
owner_pid = subprocess.getoutput([f"{pidof}"])
|
|
if owner_pid:
|
|
for pid in owner_pid.split(" "):
|
|
username = subprocess.getoutput([f"""ps -o user= -p {pid}"""])
|
|
if username == whoami:
|
|
return pid
|
|
return None
|
|
|
|
def kill_owned_process(pidof):
|
|
pid = return_user_owned_pid(pidof)
|
|
while pid:
|
|
kill_user_owned_pid(pid)
|
|
pid = return_user_owned_pid(pidof)
|
|
|
|
def restart_sdl_and_steam():
|
|
#subprocess.getoutput(["screen -XS XTERM quit"])
|
|
kill_owned_process("pidof cstrike_linux64")
|
|
kill_owned_process("pidof xterm")
|
|
|
|
subprocess.getoutput([f'vncserver -kill']) #only displays vncservers for the specific user.
|
|
subprocess.getoutput([f'vncserver -list -cleanstale']) #cleans stale sessions by removing files from disk
|
|
time.sleep(5)
|
|
|
|
cmd = f'vncserver -localhost no -geometry 1x1 -depth 24'
|
|
#cmd = f'vncserver -localhost no -geometry 800x800 -depth 24'
|
|
#print(f'cmd: {cmd}')
|
|
subprocess.getoutput([cmd])
|
|
print('reached .bashrc executing steam and variables')
|
|
|
|
def bot_connect(data):
|
|
#use whatever ip you want here to connect with
|
|
str1 = ""
|
|
if "connect to ze" == data:
|
|
str1 = f"connect {data_ports['server_ip_port_ze']};"
|
|
elif "connect to ze2" == data:
|
|
str1 = f"connect {data_ports['server_ip_port_ze2']};" #wait 15000;
|
|
writeCfgInput(str1)
|
|
|
|
def cpulimit_pid_of_game():
|
|
# ' > /dev/null' redirects stdout to /dev/null
|
|
# '2>&1' redirects stderr to the same place as stdout
|
|
pid = return_user_owned_pid("pidof cstrike_linux64")
|
|
cmd = f"cpulimit --pid={pid} --limit=25 --background > /dev/null 2>&1"
|
|
#cmd = f"cpulimit --pid={pid} --limit=55 --background > /dev/null 2>&1"
|
|
subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE).communicate()[0]
|
|
|
|
def my_file_created_function(event_path):
|
|
#print(f"New file created: {event_path}")
|
|
if event_path.startswith("/tmp/source_engine") and event_path.endswith(".lock"):
|
|
subprocess.Popen([f"rm -rf {event_path}"], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()
|
|
return
|
|
if not event_path.lower().endswith(".bsp.bz2"):
|
|
return
|
|
|
|
global block_connection
|
|
block_connection = True
|
|
overwrite_file_access() #set chmod once for the bsp bz2 file.
|
|
file_size = None
|
|
|
|
while True:
|
|
stdout, stderr = subprocess.Popen(["ls", "-l", event_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
|
if stderr:
|
|
#print('stderr: ', stderr)
|
|
print(f'finished the bz2 file {event_path}')
|
|
break
|
|
|
|
#print('stdout: ', stdout)
|
|
user = stdout.decode().split("autismbots")[0]
|
|
if whoami in user: #we are the user that owns the file on disk
|
|
cur_file_size = stdout.decode().split("autismbots")[1].strip().split(" ")[0]
|
|
if file_size == cur_file_size:
|
|
#print('cur_file_size: ', cur_file_size, ' stdout decode: ', stdout.decode())
|
|
print(f"Deleting {event_path} due to being stuck on download.")
|
|
subprocess.Popen(["rm", "-rf", event_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).communicate()
|
|
continue
|
|
file_size = cur_file_size
|
|
else:
|
|
writeCfgInput("disconnect;")
|
|
time.sleep(10)
|
|
|
|
overwrite_file_access() #set chmod for the actual bsp file
|
|
global the_undesired_crash_counter
|
|
the_undesired_crash_counter = 0
|
|
block_connection = False
|
|
|
|
class NewFileHandler(FileSystemEventHandler):
|
|
def on_created(self, event):
|
|
my_file_created_function(event.src_path)
|
|
|
|
def handle_temp_files():
|
|
#mostly its just downloading maps that we will be doing
|
|
root_dir = f"/tmp"
|
|
event_handler = NewFileHandler()
|
|
observer = Observer()
|
|
# Schedule the observer to watch the root directory and its subdirectories recursively
|
|
observer.schedule(event_handler, root_dir, recursive=True)
|
|
observer.start()
|
|
return observer
|
|
|
|
def handle_bz2_files():
|
|
#mostly its just downloading maps that we will be doing
|
|
root_dir = f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/download"
|
|
event_handler = NewFileHandler()
|
|
observer = Observer()
|
|
# Schedule the observer to watch the root directory and its subdirectories recursively
|
|
observer.schedule(event_handler, root_dir, recursive=True)
|
|
observer.start()
|
|
return observer
|
|
|
|
if __name__ == '__main__':
|
|
atexit.register(exit_handler)
|
|
local_port = data_ports['udp_port']
|
|
external_port_messages = data_ports['chat_external_port']
|
|
buffer_size = 4096 #potentially not large enough?
|
|
|
|
clean_up_files()
|
|
observer = handle_bz2_files()
|
|
observer1 = handle_temp_files()
|
|
|
|
maps_folder_size = subprocess.Popen(["du", "-sh", f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/download/maps"], stdout=subprocess.PIPE).communicate()[0].decode().split("\t")[0]
|
|
#deleting when maps folder larger than some GB
|
|
if maps_folder_size.endswith("G"):
|
|
maps_folder_size = float(maps_folder_size[:-1])
|
|
if maps_folder_size > 40.0:
|
|
for f in glob.glob(f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/download/maps/*"):
|
|
subprocess.Popen(["rm", "-rf", f], stdout=subprocess.PIPE).communicate()
|
|
|
|
restart_sdl_and_steam()
|
|
is_bot_connected_to_ze2 = False
|
|
|
|
print('preparing to launch game....')
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
sock.bind(("", local_port))
|
|
sock.settimeout(5.0)
|
|
messager_name = ""
|
|
|
|
#give the game some time to launch
|
|
fail_safe = 5
|
|
while return_user_owned_pid("pidof cstrike_linux64") is None:
|
|
#just a fail safe to prevent being stuck forever.
|
|
if fail_safe <= 0:
|
|
sys.exit(1)
|
|
time.sleep(10)
|
|
print("launching game...")
|
|
fail_safe -= 1
|
|
time.sleep(10)
|
|
cpulimit_pid_of_game()
|
|
|
|
try:
|
|
while True:
|
|
try:
|
|
#print("socket waiting for recvfrom")
|
|
data, addr = sock.recvfrom(buffer_size)
|
|
except socket.timeout:
|
|
continue
|
|
data = codecs.decode(data, "utf-8", "ignore")
|
|
ip = addr[0]
|
|
port = addr[1]
|
|
#print('port: ', port, " ip: ", ip)
|
|
#print('data: ', data)
|
|
if not data:
|
|
continue
|
|
|
|
#print("ip: ", ip, " port: ", port)
|
|
if ip == data_ports['discord_bot_ip'] and port == external_port_messages:
|
|
if messager_name in data:
|
|
messager_name = ""
|
|
response_msg = f"""say {messager_name} {data}"""
|
|
print("remote UDP packet response_msg: ", response_msg)
|
|
writeCfgInput(response_msg)
|
|
if ip != data_ports['ovh_ip']:
|
|
continue
|
|
elif data == "rtv":
|
|
response_msg = "say rtv"
|
|
writeCfgInput(response_msg)
|
|
elif data == "bot kicked server full":
|
|
print('bot kicked server full: ', datetime.datetime.now().time())
|
|
elif "autismo connected to ze" == data:
|
|
print('Bot connected to ze!')
|
|
is_bot_connected_to_ze2 = False
|
|
the_undesired_crash_counter = 0
|
|
elif "not connected to ze2" == data:
|
|
is_bot_connected_to_ze2 = False
|
|
elif "autismo connected to ze2" == data:
|
|
print('Bot connected to ze2!')
|
|
the_undesired_crash_counter = 0
|
|
is_bot_connected_to_ze2 = True
|
|
elif "connect to ze" == data or ("connect to ze2" == data and not is_bot_connected_to_ze2):
|
|
if datetime.datetime.now() >= restart_time or return_user_owned_pid("pidof cstrike_linux64") is None or the_undesired_crash_counter >= 5:
|
|
#it already finished downloading the contnet, it just cant connect. probably stuck. shitty solution welp
|
|
kill_owned_process("pidof cstrike_linux64")
|
|
sock.close()
|
|
close_observer(observer)
|
|
close_observer(observer1)
|
|
print('exiting after running the game for several hours or game being stuck on map change.')
|
|
sys.exit(1)
|
|
elif not block_connection: #need this check to prevent connection spam during content downloading.
|
|
print('data: ', data, ' the_undesired_crash_counter: ', the_undesired_crash_counter)
|
|
the_undesired_crash_counter += 1
|
|
bot_connect(data)
|
|
elif "clientmessage:" in data:
|
|
messager_name = data.split("clientmessage:", 1)[1].split(f" {data_ports['magic_secret']}")[0]
|
|
databyte_send_message = messager_name + data.split(f"{data_ports['magic_secret']}")[1]
|
|
sock.sendto(databyte_send_message.encode(), (data_ports["discord_bot_ip"], external_port_messages))
|
|
#print('databyte_send_message: ', databyte_send_message)
|
|
elif data.startswith("dist_target:"):
|
|
bot_process_movement(data)
|
|
elif data.startswith("surfing:"):
|
|
bot_process_surf(data)
|
|
elif data.startswith("hull info:"):
|
|
hull_info = data[data.index("hull info:") + len("hull info:"):]
|
|
strInput = ""
|
|
if hull_info == "jump":
|
|
strInput += "+jump; wait 5; -jump; +duck; wait 50; -duck; wait 5; "
|
|
elif hull_info == "crouch":
|
|
strInput += "+duck; wait 50; -duck; wait 5; "
|
|
writeCfgInput(strInput)
|
|
finally:
|
|
sock.close()
|
|
close_observer(observer)
|
|
close_observer(observer1)
|