DennisSkov

TCAdmin Workshop install script

Jan 21st, 2021
1,058
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import clr
  2.  
  3. from System.IO import Directory, File, Path, SearchOption
  4. from System import Environment, PlatformID, String, Exception
  5. from System.Text.RegularExpressions import Regex, RegexOptions, Match
  6.  
  7. #Support for parallel extraction
  8. clr.AddReference('System.Core')
  9. from System.Collections.Generic import List
  10. from System import Action
  11. from System.Threading.Tasks import Parallel, ParallelOptions
  12.  
  13. extractedcount=0
  14. totalfilecount=0
  15. lastfileprogress=0
  16.    
  17. ########################################
  18. # https://github.com/TheCherry/ark-server-manager #
  19. ########################################
  20. import struct
  21. import zlib
  22. import sys
  23.  
  24. def str_to_l(st):
  25.     return struct.unpack('q', st)[0]
  26.  
  27. def z_unpack(src, dst):
  28.     global extractedcount, totalfilecount, lastfileprogress
  29.     with open(src, 'rb') as f_src:
  30.         with open(dst, 'wb') as f_dst:
  31.             f_src.read(8)
  32.             size1 = str_to_l(f_src.read(8))
  33.             f_src.read(8)
  34.             size2 = str_to_l(f_src.read(8))
  35.             if(size1 == -1641380927):
  36.                 size1 = 131072L
  37.             runs = (size2 + size1 - 1L) / size1
  38.             array = []
  39.             for i in range(runs):
  40.                 array.append(f_src.read(8))
  41.                 f_src.read(8)
  42.             for i in range(runs):
  43.                 to_read = array[i]
  44.                 compressed = f_src.read(str_to_l(to_read))
  45.                 decompressed = zlib.decompress(compressed)
  46.                 f_dst.write(decompressed)
  47.     Script.WriteToConsole("Extracted " + dst.Replace(ThisService.RootDirectory, ""))
  48.     File.Delete(src)
  49.     File.Delete(src + ".uncompressed_size")
  50.     extractedcount=extractedcount+1
  51.     progress=round((float(extractedcount)/totalfilecount)*100,0)
  52.     ThisTaskStep.WriteLog(String.Format("Extracted {0}/{1} files...", extractedcount, totalfilecount))
  53.     if progress > lastfileprogress + 4:
  54.       lastfileprogress=progress
  55.       ThisTaskStep.UpdateProgress(progress)
  56.      
  57. #######################################################################
  58. # https://github.com/barrycarey/Ark_Mod_Downloader/blob/master/Ark_Mod_Downloader.py #
  59. #######################################################################
  60. import os
  61. import struct
  62. from collections import OrderedDict
  63. map_names = []
  64. map_count=0
  65. temp_mod_path = os.path.join(ThisService.RootDirectory, "ShooterGame/Content/Mods")
  66. meta_data = OrderedDict([])
  67.  
  68. def parse_base_info(modid):
  69.    
  70.         Script.WriteToConsole("[+] Collecting Mod Details From mod.info")
  71.  
  72.         mod_info = os.path.join(temp_mod_path, modid, "mod.info")
  73.  
  74.         if not os.path.isfile(mod_info):
  75.             raise Exception("[x] Failed to locate mod.info. Cannot Continue. Please try again.")
  76.             return False
  77.  
  78.         with open(mod_info, "rb") as f:
  79.             read_ue4_string(f)
  80.             map_count = struct.unpack('i', f.read(4))[0]
  81.  
  82.             for i in range(map_count):
  83.                 cur_map = read_ue4_string(f)
  84.                 if cur_map:
  85.                     map_names.append(cur_map)
  86.  
  87.         return True
  88.  
  89. def parse_meta_data(modid):
  90.         """
  91.        Parse the modmeta.info files and extract the key value pairs need to for the .mod file.
  92.        How To Parse modmeta.info:
  93.            1. Read 4 bytes to tell how many key value pairs are in the file
  94.            2. Read next 4 bytes tell us how many bytes to read ahead to get the key
  95.            3. Read ahead by the number of bytes retrieved from step 2
  96.            4. Read next 4 bytes to tell how many bytes to read ahead to get value
  97.            5. Read ahead by the number of bytes retrieved from step 4
  98.            6. Start at step 2 again
  99.        :return: Dict
  100.        """
  101.  
  102.         ThisTaskStep.WriteLog("[+] Collecting Mod Meta Data From modmeta.info")
  103.         ThisTaskStep.WriteLog("[+] Located The Following Meta Data:")
  104.  
  105.         mod_meta = os.path.join(temp_mod_path, modid, "modmeta.info")
  106.         if not os.path.isfile(mod_meta):
  107.             raise Exception("[x] Failed To Locate modmeta.info. Cannot continue without it. Please try again.")
  108.             return False
  109.  
  110.         with open(mod_meta, "rb") as f:
  111.  
  112.             total_pairs = struct.unpack('i', f.read(4))[0]
  113.  
  114.             for i in range(total_pairs):
  115.  
  116.                 key, value = "", ""
  117.  
  118.                 key_bytes = struct.unpack('i', f.read(4))[0]
  119.                 key_flag = False
  120.                 if key_bytes < 0:
  121.                     key_flag = True
  122.                     key_bytes -= 1
  123.  
  124.                 if not key_flag and key_bytes > 0:
  125.  
  126.                     raw = f.read(key_bytes)
  127.                     key = raw[:-1].decode()
  128.  
  129.                 value_bytes = struct.unpack('i', f.read(4))[0]
  130.                 value_flag = False
  131.                 if value_bytes < 0:
  132.                     value_flag = True
  133.                     value_bytes -= 1
  134.  
  135.                 if not value_flag and value_bytes > 0:
  136.                     raw = f.read(value_bytes)
  137.                     value = raw[:-1].decode()
  138.  
  139.                 # TODO This is a potential issue if there is a key but no value
  140.                 if key and value:
  141.                     Script.WriteToConsole("[!] " + key + ":" + value)
  142.                     meta_data[key] = value
  143.  
  144.         return True
  145.  
  146. def create_mod_file(modid):
  147.         """
  148.        Create the .mod file.
  149.        This code is an adaptation of the code from Ark Server Launcher.  All credit goes to Face Wound on Steam
  150.        :return:
  151.        """
  152.         if not parse_base_info(modid) or not parse_meta_data(modid):
  153.             return False
  154.  
  155.         ThisTaskStep.WriteLog("[+] Writing .mod File")
  156.         with open(os.path.join(temp_mod_path, modid + ".mod"), "w+b") as f:
  157.  
  158.             modid = int(modid)
  159.             if modid > 2147483647:
  160.               diff = modid-2147483647
  161.               modid = -2147483647 + diff - 2                                          
  162.             f.write(struct.pack('ixxxx', modid))  # Needs 4 pad bits
  163.             write_ue4_string("ModName", f)
  164.             write_ue4_string("", f)
  165.  
  166.             map_count = len(map_names)
  167.             f.write(struct.pack("i", map_count))
  168.  
  169.             for m in map_names:
  170.                 write_ue4_string(m, f)
  171.  
  172.             # Not sure of the reason for this
  173.             num2 = 4280483635
  174.             f.write(struct.pack('I', num2))
  175.             num3 = 2
  176.             f.write(struct.pack('i', num3))
  177.  
  178.             if "ModType" in meta_data:
  179.                 mod_type = b'1'
  180.             else:
  181.                 mod_type = b'0'
  182.  
  183.             # TODO The packing on this char might need to be changed
  184.             f.write(struct.pack('p', mod_type))
  185.             meta_length = len(meta_data)
  186.             f.write(struct.pack('i', meta_length))
  187.  
  188.             for k, v in meta_data.items():
  189.                 write_ue4_string(k, f)
  190.                 write_ue4_string(v, f)
  191.  
  192.         return True
  193.  
  194. def read_ue4_string(file):
  195.         count = struct.unpack('i', file.read(4))[0]
  196.         flag = False
  197.         if count < 0:
  198.             flag = True
  199.             count -= 1
  200.  
  201.         if flag or count <= 0:
  202.             return ""
  203.  
  204.         return file.read(count)[:-1].decode()
  205.  
  206. def write_ue4_string(string_to_write, file):
  207.         string_length = len(string_to_write) + 1
  208.         file.write(struct.pack('i', string_length))
  209.         barray = bytearray(string_to_write, "utf-8")
  210.         file.write(barray)
  211.         file.write(struct.pack('p', b'0'))
  212.  
  213. ###########################################
  214. ###########################################
  215. ###########################################
  216. # If the content folder doesn't exist use downloads                                                
  217. if not Directory.Exists(InstallPath) :
  218.   InstallPath=InstallPath.Replace("/content/", "/downloads/")                                                            
  219.  
  220. # Always use Windows files. Linux files cause the game server to crash at startup.
  221. oseditor = "WindowsNoEditor"
  222. noeditor=Path.Combine(InstallPath, oseditor)
  223.  
  224. # Use other OS folder if it doesn't exist.
  225. if not Directory.Exists(noeditor) :
  226.   oseditor = "LinuxNoEditor"
  227.   noeditor = Path.Combine(InstallPath, oseditor)  
  228.  
  229. if Environment.OSVersion.Platform == PlatformID.Win32NT:
  230. # Extract and delete all .z files (Windows)
  231.   actions = List[Action]()
  232.   currentNumber = 0
  233.   zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories)
  234.   totalfilecount=zfiles.Count
  235.   for zfile in zfiles:
  236.    file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
  237.    action=Action(lambda a=zfile, b=file: z_unpack(a, b))
  238.    actions.Add(action)
  239.    
  240.   options=ParallelOptions()
  241.   #Extract 2 files at a time.
  242.   options.MaxDegreeOfParallelism = 2
  243.   totalfilecount=actions.Count
  244.   Parallel.Invoke(options, actions.ToArray())
  245. else:
  246.   # Extract and delete all .z files (Linux)
  247.   zfiles=Directory.GetFiles(noeditor, "*.z", SearchOption.AllDirectories)
  248.   totalfilecount=zfiles.Count
  249.   currentNumber = 0
  250.   for zfile in zfiles:
  251.     currentNumber += 1
  252.     progress = (float(currentNumber) / float(totalfilecount)) * 100
  253.     file=Path.Combine(Path.GetDirectoryName(zfile), Path.GetFileNameWithoutExtension(zfile))
  254.     z_unpack(zfile, file)
  255.     File.Delete(zfile)
  256.     File.Delete(zfile + ".uncompressed_size")
  257.  
  258. # Move folder to correct location. Delete if it already exists.
  259. # Define modid before FileId is altered so we write the correct id to inifile
  260. modid = FileId
  261. if FileId > 2147483647:
  262.   diff = FileId-2147483647
  263.   FileId = -2147483647 + diff - 2
  264.  
  265. modfolder=Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Content/Mods/{0}", modid))
  266. if Directory.Exists(modfolder) :
  267.   Directory.Delete(modfolder, True)
  268. Directory.Move(Path.Combine(InstallPath, oseditor), modfolder)
  269.  
  270. # Update ini file
  271. serveros = "WindowsServer" if Environment.OSVersion.Platform == PlatformID.Win32NT else "LinuxServer"
  272. inifile = Path.Combine(ThisService.RootDirectory, String.Format("ShooterGame/Saved/Config/{0}/GameUserSettings.ini", serveros))
  273. pattern="ActiveMods[ \t]*=[ \t]*(?<ActiveMods>[0-9, \t]*)"
  274. filecontents = File.ReadAllText(inifile)
  275. match = Regex.Match(filecontents, pattern, RegexOptions.IgnoreCase)
  276. if match.Success :
  277.   activemods = match.Groups["ActiveMods"].Value
  278.   if String.IsNullOrEmpty(activemods) or activemods.IndexOf(modid.ToString()) == -1 :
  279.     if activemods.Length > 0 :
  280.       activemods = activemods + ","
  281.       activemods = activemods + modid.ToString()
  282.       filecontents=filecontents.Replace(match.Groups["ActiveMods"].Value, activemods)
  283.     else :
  284.       activemods = modid.ToString()
  285.       filecontents = filecontents.Substring(0, match.Groups["ActiveMods"].Index) + activemods + filecontents.Substring(match.Groups["ActiveMods"].Index)
  286.     File.WriteAllText(inifile, filecontents)
  287.  
  288. #Create .mod
  289. parse_base_info(modid.ToString())
  290. parse_meta_data(modid.ToString())
  291. create_mod_file(modid.ToString())
  292.  
  293. # Delete folder
  294. if Directory.Exists(InstallPath) :
  295.   Directory.Delete(InstallPath, True)
RAW Paste Data