├── .github └── workflows │ └── python-app.yml ├── .gitignore ├── README.md ├── requirements.txt ├── scripts └── install_mods │ ├── __init__.py │ └── install_mods.py └── tests ├── servertest.ini └── test_install_mods.py /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: ["main"] 9 | pull_request: 10 | branches: ["main"] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python 3.10 22 | uses: actions/setup-python@v3 23 | with: 24 | python-version: "3.10" 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install flake8 pytest beautifulsoup4 requests 29 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 30 | - name: Lint with flake8 31 | run: | 32 | # stop the build if there are Python syntax errors or undefined names 33 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 34 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 35 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 36 | - name: Test with pytest 37 | env: 38 | STEAM_URL: ${{ secrets.STEAM_URL }} 39 | run: | 40 | python -m pytest 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _pycache__/ 2 | .pytest_cache/ 3 | *.py[cod] 4 | venv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project-Zomboid-Scripts 2 | 3 | Useful tools I made for maintaining my project zomboid dedicated server 4 | 5 | ## InstallMods.py 6 | 7 | Adds a list of mods from a Steam workshop collection to the servertest.ini file. 8 | Requires Python version 3 and beautifulsoup4 library installed. Must be placed within the server directory: `Zomboid/Server/`. Server must have been run at least once before. 9 | 10 | To Install beautifulsoup4 run this command line: 11 | ``` 12 | python3 -m pip install beautifulsoup4 requests 13 | ``` 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristianpayne1/Project-Zomboid-Scripts/6a5e598bdc81f7a41a6ca5ea2f3573450fcbd68b/requirements.txt -------------------------------------------------------------------------------- /scripts/install_mods/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristianpayne1/Project-Zomboid-Scripts/6a5e598bdc81f7a41a6ca5ea2f3573450fcbd68b/scripts/install_mods/__init__.py -------------------------------------------------------------------------------- /scripts/install_mods/install_mods.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | import requests 3 | import argparse 4 | from bs4 import BeautifulSoup 5 | from pathlib import Path 6 | import re 7 | 8 | WORKSHOP_CONFIG_KEY = "WorkshopItems" 9 | MODS_CONFIG_KEY = "Mods" 10 | STEAM_WORKSHOP_TEMPLATE_URL = "https://steamcommunity.com/sharedfiles/filedetails/?id=%s" 11 | 12 | 13 | class BColors: 14 | HEADER = '\033[95m' 15 | OKGREEN = '\033[92m' 16 | FAIL = '\033[91m' 17 | ENDC = '\033[0m' 18 | 19 | 20 | def get_mod_ids(workshop_id: str): 21 | # For workshop item, find the mod ID 22 | mod_html = requests.get(STEAM_WORKSHOP_TEMPLATE_URL % workshop_id).text 23 | mod_soup = BeautifulSoup(mod_html, "html.parser") 24 | mod_page_text = mod_soup.get_text() 25 | if "Mod ID:" in mod_page_text or "ModID:" in mod_page_text: 26 | modIDs = re.findall(r'(?:(?<=Mod ID: )|(?<=ModID: ))(.*?)(?=\W)', mod_page_text) 27 | for modID in modIDs: 28 | print(modID) 29 | return modIDs 30 | else: 31 | print(BColors.FAIL + "Couldn't find Mod ID for workshop item: {}".format(workshop_id) + BColors.ENDC) 32 | return [] 33 | 34 | 35 | def replace_key_or_add(key: str, replacement: str, s: str) -> str: 36 | if key not in s: 37 | return s + '\n' + replacement 38 | 39 | pattern = f"\b{key}[^\b]*\b" 40 | return re.sub(pattern, replacement, s) 41 | 42 | def main(collection_url: str, server_config: Path = Path("./servertest.ini"), print_only: bool = False): 43 | # Exception handling 44 | if collection_url == "": 45 | raise Exception(BColors.FAIL + "\nCollection URL empty! Please the collection URL" + BColors.ENDC) 46 | 47 | if not server_config.exists(): 48 | raise Exception(BColors.FAIL + f"\nNo {server_config} file found! Make sure you have started your server at least once before and specified the path to the config" + BColors.ENDC) 49 | 50 | if not print_only: 51 | print(BColors.HEADER + "========== Overwriting mod list ==========" + BColors.ENDC) 52 | 53 | # Read collection HTML 54 | collectionHTML = requests.get(collection_url).text 55 | collectionSoup = BeautifulSoup(collectionHTML, "html.parser") 56 | 57 | # Find items 58 | collectionItems = collectionSoup.find_all("div", "collectionItem") 59 | 60 | print(f"{len(collectionItems)} workshop items found:") 61 | 62 | # Extract IDs and format to semi-colon seperated string 63 | workshopIDs = [] 64 | for item in collectionItems: 65 | id = item["id"].split("_")[1] 66 | if id: 67 | workshopIDs.append(id) 68 | 69 | formattedWorkshopIDs = ';'.join(workshopIDs) 70 | workshop_line = f"{WORKSHOP_CONFIG_KEY}={formattedWorkshopIDs}" 71 | 72 | # Multi-threading go zoom 🏎️💨 73 | with concurrent.futures.ThreadPoolExecutor() as executor: 74 | results = executor.map(get_mod_ids, workshopIDs) 75 | 76 | modIDs = [item for sublist in list(results) for item in sublist] 77 | formattedModIDs = ';'.join(modIDs) 78 | mods_line = f"{MODS_CONFIG_KEY}={formattedModIDs}" 79 | 80 | print() 81 | 82 | if print_only: 83 | print(f"Workshop IDs: {formattedWorkshopIDs}") 84 | print(f"Mod IDs: {formattedModIDs}") 85 | exit() 86 | 87 | # Write to servertest.ini 88 | with server_config.open('r+') as file: 89 | file_string: str = file.read() 90 | 91 | file_string = replace_key_or_add(WORKSHOP_CONFIG_KEY, workshop_line, file_string) 92 | file_string = replace_key_or_add(MODS_CONFIG_KEY, mods_line, file_string) 93 | 94 | with server_config.open('w') as file: 95 | file.write(file_string) 96 | 97 | print(BColors.OKGREEN + f"Finished writing to {server_config} ✅" + BColors.ENDC) 98 | print(BColors.HEADER + "=========================================" + BColors.ENDC) 99 | 100 | if __name__ == "__main__": 101 | parser = argparse.ArgumentParser("Project Zomboid mod installer") 102 | parser.add_argument("url", type=str, help="Steam collection URL") 103 | parser.add_argument("--server-file", type=Path, default=Path("./servertest.ini"), help="Path to servertest.ini (or server's ini config file)") 104 | parser.add_argument('-p', "--print-only", default=False, action="store_const", const=True, help="Don't write to server config file and print mod IDs instead") 105 | 106 | args = parser.parse_args() 107 | 108 | collection_url: str = args.url 109 | server_config: Path = args.server_file.expanduser().absolute() 110 | print_only: bool = args.print_only 111 | 112 | main(collection_url, server_config, print_only) 113 | -------------------------------------------------------------------------------- /tests/servertest.ini: -------------------------------------------------------------------------------- 1 | # Players can hurt and kill other players 2 | PVP=true 3 | 4 | # Game time stops when there are no players online 5 | PauseEmpty=true 6 | 7 | # Toggles global chat on or off. 8 | GlobalChat=true 9 | 10 | ChatStreams=s,r,a,w,y,sh,f,all 11 | 12 | # Clients may join without already having an account in the whitelist. If set to false, administrators must manually create username/password combos. 13 | Open=true 14 | 15 | # The first welcome message visible in the chat panel. This will be displayed immediately after player login. you can use RGB colours to chance the colour of the welcome message. You can also use to create a separate lines within your text. Use: This message will show up red! 16 | ServerWelcomeMessage=Welcome to Project Zomboid Multiplayer! To interact with the Chat panel: press Tab, T, or Enter. The Tab key will change the target stream of the message. Global Streams: /all Local Streams: /say, /yell Special Steams: /whisper, /safehouse, /faction. Press the Up arrow to cycle through your message history. Click the Gear icon to customize chat. Happy surviving! 17 | 18 | # Add unknown usernames to the whitelist when players join. Clients will supply their own username/password on joining. (This is for Open=true servers) 19 | AutoCreateUserInWhiteList=false 20 | 21 | # Display usernames above player's heads in-game. 22 | DisplayUserName=true 23 | 24 | # Display first & last name above player's heads. 25 | ShowFirstAndLastName=false 26 | 27 | # Force every new player to spawn at these set x,y,z world coordinates. Find desired coordinates at map.projectzomboid.com. (Ignored when 0,0,0) 28 | SpawnPoint=0,0,0 29 | 30 | # Players can enter and leave PVP on an individual basis. A player can only hurt another player when at least one of them is in PVP mode - as shown by the unobscured skull and crossbones on the left of the screen. When SafetySystem=false, players are free to hurt each other at any time if PVP is enabled. 31 | SafetySystem=true 32 | 33 | # Display a skull icon over the head of players who have entered PVP mode 34 | ShowSafety=true 35 | 36 | # The time it takes for a player to enter and leave PVP mode\nMinimum=0 Maximum=1000 Default=2 37 | SafetyToggleTimer=2 38 | 39 | # The delay before a player can enter or leave PVP mode again, having recently done so\nMinimum=0 Maximum=1000 Default=3 40 | SafetyCooldownTimer=3 41 | 42 | # Item types new players spawn with.\nSeparate multiple item types with commas.\nExample: Base.Axe,Base.Bag_BigHikingBag 43 | SpawnItems= 44 | 45 | # Default starting port for player data. If UDP, this is this one of two ports used.\nMinimum=0 Maximum=65535 Default=16261 46 | DefaultPort=16261 47 | 48 | # Reset ID determines if the server has undergone a soft-reset. If this number does match the client, the client must create a new character. Used in conjunction with PlayerServerID. It is strongly advised that you backup these IDs somewhere\nMinimum=0 Maximum=2147483647 Default=106718896 49 | ResetID=8138685 50 | 51 | # Enter the mod loading ID here. It can be found in \Steam\steamapps\workshop\modID\mods\modName\info.txt 52 | Mods=BetterSortCC;CombatText;CraftHelper41;DisplayEquippedLocation;ExpirationFoodR;improvedbuildmenu41;MapLegendUI;MinimalDisplayBars;RainWash;showmaterial;SimpleAddInventoryPages20191226;TheStar 53 | 54 | # Enter the foldername of the mod found in \Steam\steamapps\workshop\modID\mods\modName\media\maps\ 55 | Map=Muldraugh, KY 56 | 57 | # Kick clients whose game files don't match the server's. 58 | DoLuaChecksum=true 59 | 60 | DenyLoginOnOverloadedServer=true 61 | 62 | # Shows the server on the in-game browser. (Note: Steam-enabled servers are always visible in the Steam server browser) 63 | Public=false 64 | 65 | # Name of the server displayed in the in-game browser and, if applicable, the Steam browser 66 | PublicName=My PZ Server 67 | 68 | # Description displayed in the in-game public server browser. Typing \n will create a new line in your description 69 | PublicDescription= 70 | 71 | # Maximum number of players that can be on the server at one time. This excludes admins. 72 | # WARNING: Server player counts above 32 will potentially result in poor map streaming and desync. Please advance with caution.\nMinimum=1 Maximum=100 Default=32 73 | MaxPlayers=32 74 | 75 | # Ping limit, in milliseconds, before a player is kicked from the server. (Set to 100 to disable)\nMinimum=100 Maximum=2147483647 Default=400 76 | PingLimit=400 77 | 78 | # After X hours, all containers in the world will respawn loot. To spawn loot a container must have been looted at least once. Loot respawn is not impacted by visibility or subsequent looting.\nMinimum=0 Maximum=2147483647 Default=0 79 | HoursForLootRespawn=0 80 | 81 | # Containers with a number of items greater, or equal to, this setting will not respawn\nMinimum=1 Maximum=2147483647 Default=4 82 | MaxItemsForLootRespawn=4 83 | 84 | # Items will not respawn in buildings that players have barricaded or built in 85 | ConstructionPreventsLootRespawn=true 86 | 87 | # Remove player accounts from the whitelist after death. This prevents players creating a new character after death on Open=false servers 88 | DropOffWhiteListAfterDeath=false 89 | 90 | # All forms of fire are disabled - except for campfires 91 | NoFire=false 92 | 93 | # If checked, every time a player dies a global message will be displayed in the chat 94 | AnnounceDeath=false 95 | 96 | # The number of in-game minutes it takes to read one page of a book\nMinimum=0.00 Maximum=60.00 Default=1.00 97 | MinutesPerPage=1.0 98 | 99 | # Loaded parts of the map are saved after this set number of real-world minutes have passed. (The map is usually saved only after clients leave a loaded area)\nMinimum=0 Maximum=2147483647 Default=0 100 | SaveWorldEveryMinutes=0 101 | 102 | # Both admins and players can claim safehouses 103 | PlayerSafehouse=false 104 | 105 | # Only admins can claim safehouses 106 | AdminSafehouse=false 107 | 108 | # Allow non-members to enter a safehouse without being invited 109 | SafehouseAllowTrepass=true 110 | 111 | # Allow fire to damage safehouses 112 | SafehouseAllowFire=true 113 | 114 | # Allow non-members to take items from safehouses 115 | SafehouseAllowLoot=true 116 | 117 | # Players will respawn in a safehouse that they were a member of before they died 118 | SafehouseAllowRespawn=false 119 | 120 | # Players must have survived this number of in-game days before they are allowed to claim a safehouse\nMinimum=0 Maximum=2147483647 Default=0 121 | SafehouseDaySurvivedToClaim=0 122 | 123 | # Players are automatically removed from a safehouse they have not visited for this many real-world hours\nMinimum=0 Maximum=2147483647 Default=144 124 | SafeHouseRemovalTime=144 125 | 126 | # Governs whether players can claim non-residential buildings. 127 | SafehouseAllowNonResidential=false 128 | 129 | # Allow players to destroy world objects with sledgehammers 130 | AllowDestructionBySledgehammer=true 131 | 132 | # Allow players to destroy world objects only in their safehouse (require AllowDestructionBySledgehammer to true). 133 | SledgehammerOnlyInSafehouse=false 134 | 135 | # Kick players that appear to be moving faster than is possible. May be buggy -- use with caution. 136 | KickFastPlayers=false 137 | 138 | # ServerPlayerID determines if a character is from another server, or single player. This value may be changed by soft resets. If this number does match the client, the client must create a new character. This is used in conjunction with ResetID. It is strongly advised that you backup these IDs somewhere 139 | ServerPlayerID=461185265 140 | 141 | # The port for the RCON (Remote Console)\nMinimum=0 Maximum=65535 Default=27015 142 | RCONPort=27015 143 | 144 | # RCON password (Pick a strong password) 145 | RCONPassword= 146 | 147 | # Enables global text chat integration with a Discord channel 148 | DiscordEnable=false 149 | 150 | # Discord bot access token 151 | DiscordToken= 152 | 153 | # The Discord channel name. (Try the separate channel ID option if having difficulties) 154 | DiscordChannel= 155 | 156 | # The Discord channel ID. (Use if having difficulties with Discord channel name option) 157 | DiscordChannelID= 158 | 159 | # Clients must know this password to join the server. (Ignored when hosting a server via the Host button) 160 | Password= 161 | 162 | # Limits the number of different accounts a single Steam user may create on this server. Ignored when using the Hosts button.\nMinimum=0 Maximum=2147483647 Default=0 163 | MaxAccountsPerUser=0 164 | 165 | # Players are allowed to sleep when their survivor becomes tired, but they do not NEED to sleep 166 | SleepAllowed=false 167 | 168 | # Players get tired and need to sleep. (Ignored if SleepAllowed=false) 169 | SleepNeeded=false 170 | 171 | # Minimum=0 Maximum=65535 Default=8766 172 | SteamPort1=8766 173 | 174 | # Minimum=0 Maximum=65535 Default=8767 175 | SteamPort2=8767 176 | 177 | # List Workshop Mod IDs for the server to download. Each must be separated by a semicolon. Example: WorkshopItems=514427485;513111049 178 | WorkshopItems=2313387159;2286124931;2186592938;1988306113;1355561500;1969456967;2710167561;2004998206;2657661246;1922750845;1946782371;2619072426 179 | 180 | # Show Steam usernames and avatars in the Players list. Can be true (visible to everyone), false (visible to no one), or admin (visible to only admins) 181 | SteamScoreboard=true 182 | 183 | # Enable the Steam VAC system 184 | SteamVAC=true 185 | 186 | # Attempt to configure a UPnP-enabled internet gateway to automatically setup port forwarding rules. The server will fall back to default ports if this fails 187 | UPnP=true 188 | 189 | # VOIP is enabled when checked 190 | VoiceEnable=true 191 | 192 | # The minimum tile distance over which VOIP sounds can be heard.\nMinimum=0.00 Maximum=100000.00 Default=10.00 193 | VoiceMinDistance=10.0 194 | 195 | # The maximum tile distance over which VOIP sounds can be heard.\nMinimum=0.00 Maximum=100000.00 Default=100.00 196 | VoiceMaxDistance=100.0 197 | 198 | # Toggle directional audio for VOIP 199 | Voice3D=true 200 | 201 | # Minimum=10.00 Maximum=150.00 Default=70.00 202 | SpeedLimit=70.0 203 | 204 | LoginQueueEnabled=false 205 | 206 | # Minimum=20 Maximum=1200 Default=60 207 | LoginQueueConnectTimeout=60 208 | 209 | # Set the IP from which the server is broadcast. This is for network configurations with multiple IP addresses, such as server farms 210 | server_browser_announced_ip= 211 | 212 | # Players can respawn in-game at the coordinates where they died 213 | PlayerRespawnWithSelf=false 214 | 215 | # Players can respawn in-game at a split screen / Remote Play player's location 216 | PlayerRespawnWithOther=false 217 | 218 | # Governs how fast time passes while players sleep. Value multiplies the speed of the time that passes during sleeping.\nMinimum=1.00 Maximum=100.00 Default=40.00 219 | FastForwardMultiplier=40.0 220 | 221 | # Safehouse acts like a normal house if a member of the safehouse is connected (so secure when players are offline) 222 | DisableSafehouseWhenPlayerConnected=false 223 | 224 | # Players can create factions when true 225 | Faction=true 226 | 227 | # Players must survive this number of in-game days before being allowed to create a faction\nMinimum=0 Maximum=2147483647 Default=0 228 | FactionDaySurvivedToCreate=0 229 | 230 | # Number of players required as faction members before the faction owner can create a group tag\nMinimum=1 Maximum=2147483647 Default=1 231 | FactionPlayersRequiredForTag=1 232 | 233 | # Disables radio transmissions from players with an access level 234 | DisableRadioStaff=false 235 | 236 | # Disables radio transmissions from players with 'admin' access level 237 | DisableRadioAdmin=true 238 | 239 | # Disables radio transmissions from players with 'gm' access level 240 | DisableRadioGM=true 241 | 242 | # Disables radio transmissions from players with 'overseer' access level 243 | DisableRadioOverseer=false 244 | 245 | # Disables radio transmissions from players with 'moderator' access level 246 | DisableRadioModerator=false 247 | 248 | # Disables radio transmissions from invisible players 249 | DisableRadioInvisible=true 250 | 251 | # Semicolon-separated list of commands that will not be written to the cmd.txt server log. For example: \n-vehicle. Inputting * means do NOT write any vehicle command. Inputting: \n+vehicle.installPart means DO write that command 252 | ClientCommandFilter=-vehicle.*;+vehicle.damageWindow;+vehicle.fixPart;+vehicle.installPart;+vehicle.uninstallPart 253 | 254 | # Semicolon-separated list of actions that will be written to the ClientActionLogs.txt server log. 255 | ClientActionLogs=ISEnterVehicle;ISExitVehicle;ISTakeEngineParts; 256 | 257 | # Track changes in player perk levels in PerkLog.txt server log 258 | PerkLogs=true 259 | 260 | # Maximum number of items that can be placed in a container. Zero means there is no limit. (PLEASE NOTE: This includes individual small items such as nails. A limit of 50 will mean only 50 nails can be stored.)\nMinimum=0 Maximum=9000 Default=0 261 | ItemNumbersLimitPerContainer=0 262 | 263 | # Number of days before old blood splats are removed. 264 | # Removal happens when map chunks are loaded. 265 | # Zero means they will never disappear\nMinimum=0 Maximum=365 Default=0 266 | BloodSplatLifespanDays=0 267 | 268 | # Allow use of non-ASCII (cyrillic etc) characters in usernames 269 | AllowNonAsciiUsername=false 270 | 271 | BanKickGlobalSound=true 272 | 273 | # If enabled, when HoursForCorpseRemoval triggers, it will also remove player’s corpses from the ground. 274 | RemovePlayerCorpsesOnCorpseRemoval=false 275 | 276 | # If true, player can use the "delete all" button on bins. 277 | TrashDeleteAll=false 278 | 279 | # If true, player can hit again when struck by another player. 280 | PVPMeleeWhileHitReaction=false 281 | 282 | # If true, players will have to mouse over someone to see their display name. 283 | MouseOverToSeeDisplayName=true 284 | 285 | # If true, automatically hide the player you can't see (like zombies). 286 | HidePlayersBehindYou=true 287 | 288 | # Damage multiplier for PVP melee attacks.\nMinimum=0.00 Maximum=500.00 Default=30.00 289 | PVPMeleeDamageModifier=30.0 290 | 291 | # Damage multiplier for PVP ranged attacks.\nMinimum=0.00 Maximum=500.00 Default=50.00 292 | PVPFirearmDamageModifier=50.0 293 | 294 | # Modify the range of zombie attraction to cars. (Lower values can help with lag.)\nMinimum=0.00 Maximum=10.00 Default=0.50 295 | CarEngineAttractionModifier=0.5 296 | 297 | # Governs whether players bump (and knock over) other players when running through them. 298 | PlayerBumpPlayer=false 299 | 300 | # Controls display of remote players on the in-game map.\n1=Hidden 2=Friends 3=Everyone\nMinimum=1 Maximum=3 Default=1 301 | MapRemotePlayerVisibility=1 302 | 303 | # Minimum=1 Maximum=300 Default=5 304 | BackupsCount=5 305 | 306 | BackupsOnStart=true 307 | 308 | BackupsOnVersionChange=true 309 | 310 | # Minimum=0 Maximum=1500 Default=0 311 | BackupsPeriod=0 312 | 313 | # Disables anti-cheat protection for type 1. 314 | AntiCheatProtectionType1=true 315 | 316 | # Disables anti-cheat protection for type 2. 317 | AntiCheatProtectionType2=true 318 | 319 | # Disables anti-cheat protection for type 3. 320 | AntiCheatProtectionType3=true 321 | 322 | # Disables anti-cheat protection for type 4. 323 | AntiCheatProtectionType4=true 324 | 325 | # Disables anti-cheat protection for type 5. 326 | AntiCheatProtectionType5=true 327 | 328 | # Disables anti-cheat protection for type 6. 329 | AntiCheatProtectionType6=true 330 | 331 | # Disables anti-cheat protection for type 7. 332 | AntiCheatProtectionType7=true 333 | 334 | # Disables anti-cheat protection for type 8. 335 | AntiCheatProtectionType8=true 336 | 337 | # Disables anti-cheat protection for type 9. 338 | AntiCheatProtectionType9=true 339 | 340 | # Disables anti-cheat protection for type 10. 341 | AntiCheatProtectionType10=true 342 | 343 | # Disables anti-cheat protection for type 11. 344 | AntiCheatProtectionType11=true 345 | 346 | # Disables anti-cheat protection for type 12. 347 | AntiCheatProtectionType12=true 348 | 349 | # Disables anti-cheat protection for type 13. 350 | AntiCheatProtectionType13=true 351 | 352 | # Disables anti-cheat protection for type 14. 353 | AntiCheatProtectionType14=true 354 | 355 | # Disables anti-cheat protection for type 15. 356 | AntiCheatProtectionType15=true 357 | 358 | # Disables anti-cheat protection for type 16. 359 | AntiCheatProtectionType16=true 360 | 361 | # Disables anti-cheat protection for type 17. 362 | AntiCheatProtectionType17=true 363 | 364 | # Disables anti-cheat protection for type 18. 365 | AntiCheatProtectionType18=true 366 | 367 | # Disables anti-cheat protection for type 19. 368 | AntiCheatProtectionType19=true 369 | 370 | # Disables anti-cheat protection for type 20. 371 | AntiCheatProtectionType20=true 372 | 373 | AntiCheatProtectionType21=true 374 | 375 | AntiCheatProtectionType22=true 376 | 377 | AntiCheatProtectionType23=true 378 | 379 | AntiCheatProtectionType24=true 380 | 381 | # Threshold value multiplier for anti-cheat protection: type 2.\nMinimum=1.00 Maximum=10.00 Default=3.00 382 | AntiCheatProtectionType2ThresholdMultiplier=3.0 383 | 384 | # Threshold value multiplier for anti-cheat protection: type 3.\nMinimum=1.00 Maximum=10.00 Default=1.00 385 | AntiCheatProtectionType3ThresholdMultiplier=1.0 386 | 387 | # Threshold value multiplier for anti-cheat protection: type 4.\nMinimum=1.00 Maximum=10.00 Default=1.00 388 | AntiCheatProtectionType4ThresholdMultiplier=1.0 389 | 390 | # Threshold value multiplier for anti-cheat protection: type 9.\nMinimum=1.00 Maximum=10.00 Default=1.00 391 | AntiCheatProtectionType9ThresholdMultiplier=1.0 392 | 393 | # Threshold value multiplier for anti-cheat protection: type 15.\nMinimum=1.00 Maximum=10.00 Default=1.00 394 | AntiCheatProtectionType15ThresholdMultiplier=1.0 395 | 396 | # Threshold value multiplier for anti-cheat protection: type 20.\nMinimum=1.00 Maximum=10.00 Default=1.00 397 | AntiCheatProtectionType20ThresholdMultiplier=1.0 398 | 399 | # Minimum=1.00 Maximum=10.00 Default=3.00 400 | AntiCheatProtectionType23ThresholdMultiplier=3.0 -------------------------------------------------------------------------------- /tests/test_install_mods.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from pathlib import Path 4 | from scripts.install_mods.install_mods import main 5 | 6 | STEAM_URL = os.environ['STEAM_URL'] 7 | 8 | def test_main(): 9 | main(STEAM_URL, Path("./tests/servertest.ini")) --------------------------------------------------------------------------------