-
-
Save marceldev89/12da69b95d010c8a810fd384cca8d02a to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
# MIT License | |
# | |
# Copyright (c) 2017 Marcel de Vries | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in all | |
# copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
# SOFTWARE. | |
import os | |
import os.path | |
import re | |
import shutil | |
import time | |
from datetime import datetime | |
from urllib import request | |
#region Configuration | |
STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh" | |
STEAM_USER = "" | |
STEAM_PASS = "" | |
A3_SERVER_ID = "233780" | |
A3_SERVER_DIR = "/home/steam/arma3/install" | |
A3_WORKSHOP_ID = "107410" | |
A3_WORKSHOP_DIR = "{}/steamapps/workshop/content/{}".format(A3_SERVER_DIR, A3_WORKSHOP_ID) | |
A3_MODS_DIR = "/home/steam/arma3/mods" | |
MODS = { | |
"@cba_a3": "450814997", | |
"@ace3": "463939057", | |
"@alive": "620260972", | |
"@cup_terrains_core": "583496184", | |
"@cup_terrains_maps": "583544987", | |
"@cup_weapons": "497660133", | |
"@cup_units": "497661914", | |
"@cup_vehicles": "541888371" | |
} | |
PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL) | |
WORKSHOP_CHANGELOG_URL = "https://steamcommunity.com/sharedfiles/filedetails/changelog" | |
#endregion | |
#region Functions | |
def log(msg): | |
print("") | |
print("{{0:=<{}}}".format(len(msg)).format("")) | |
print(msg); | |
print("{{0:=<{}}}".format(len(msg)).format("")) | |
def call_steamcmd(params): | |
os.system("{} {}".format(STEAM_CMD, params)) | |
print("") | |
def update_server(): | |
steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS) | |
steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR) | |
steam_cmd_params += " +app_update {} validate".format(A3_SERVER_ID) | |
steam_cmd_params += " +quit" | |
call_steamcmd(steam_cmd_params) | |
def mod_needs_update(mod_id, path): | |
if os.path.isdir(path): | |
response = request.urlopen("{}/{}".format(WORKSHOP_CHANGELOG_URL, mod_id)).read() | |
response = response.decode("utf-8") | |
match = PATTERN.search(response) | |
if match: | |
updated_at = datetime.fromtimestamp(int(match.group(1))) | |
created_at = datetime.fromtimestamp(os.path.getctime(path)) | |
return (updated_at >= created_at) | |
return False | |
def update_mods(): | |
for mod_name, mod_id in MODS.items(): | |
path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id) | |
# Check if mod needs to be updated | |
if os.path.isdir(path): | |
if mod_needs_update(mod_id, path): | |
# Delete existing folder so that we can verify whether the | |
# download succeeded | |
shutil.rmtree(path) | |
else: | |
print("No update required for \"{}\" ({})... SKIPPING".format(mod_name, mod_id)) | |
continue | |
# Keep trying until the download actually succeeded | |
tries = 0 | |
while os.path.isdir(path) == False and tries < 10: | |
log("Updating \"{}\" ({}) | {}".format(mod_name, mod_id, tries + 1)) | |
steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS) | |
steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR) | |
steam_cmd_params += " +workshop_download_item {} {} validate".format( | |
A3_WORKSHOP_ID, | |
mod_id | |
) | |
steam_cmd_params += " +quit" | |
call_steamcmd(steam_cmd_params) | |
# Sleep for a bit so that we can kill the script if needed | |
time.sleep(5) | |
tries = tries + 1 | |
if tries >= 10: | |
log("!! Updating {} failed after {} tries !!".format(mod_name, tries)) | |
def lowercase_workshop_dir(): | |
os.system("(cd {} && find . -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;)".format(A3_WORKSHOP_DIR)) | |
def create_mod_symlinks(): | |
for mod_name, mod_id in MODS.items(): | |
link_path = "{}/{}".format(A3_MODS_DIR, mod_name) | |
real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id) | |
if os.path.isdir(real_path): | |
if not os.path.islink(link_path): | |
os.symlink(real_path, link_path) | |
print("Creating symlink '{}'...".format(link_path)) | |
else: | |
print("Mod '{}' does not exist! ({})".format(mod_name, real_path)) | |
#endregion | |
log("Updating A3 server ({})".format(A3_SERVER_ID)) | |
update_server() | |
log("Updating mods") | |
update_mods() | |
log("Converting uppercase files/folders to lowercase...") | |
lowercase_workshop_dir() | |
log("Creating symlinks...") | |
create_mod_symlinks() |
Nah the symlink can be called anything you like it to be as long as it points to the workshop folder. So in the script the
@
thingies could be@random_mod_name_12345_etc
. ๐EDIT: Except for parentheses and probably some other characters that could possibly break it. ๐
Damn, I'm getting an error when launching the script. Attaching my config section just in case you spot my mistake (in bold the changes I made):
#region Configuration
STEAM_CMD = "/home/arma3/steam/steamcmd.sh"
STEAM_USER = "me"
STEAM_PASS = "my-password"A3_SERVER_ID = "233780"
A3_SERVER_DIR = "/home/arma3/arma3"
A3_WORKSHOP_ID = "107410"A3_WORKSHOP_DIR = "{}/steamapps/workshop/content/{}".format(A3_SERVER_DIR, A3_WORKSHOP_ID)
A3_MODS_DIR = "/home/arma3/arma3"MODS = {
"@cba_a3": "450814997",
"@ace3": "463939057",
"@alive": "620260972",
"@cup_terrains_core": "583496184",
"@cup_terrains_maps": "583544987",
"@cup_weapons": "497660133",
"@cup_units": "497661914",
"@cup_vehicles": "541888371"
}PATTERN = re.compile(r"workshopAnnouncement.*?<p id="(\d+)">", re.DOTALL)
WORKSHOP_CHANGELOG_URL = "https://steamcommunity.com/sharedfiles/filedetails/changelog"
#endregion
What's the error exactly?
What's the error exactly?
I can't read it entirely but it's something like this:
============================================
!! Updating @cba_a3 failed after 10 tries !!
================================
Updating "@ace3" (463939057) | 1
sh: 1: Syntax error: ")" unexpected
================================
Updating "@ace3" (463939057) | 2
sh: 1: Syntax error: ")" unexpected
^CTraceback (most recent call last):
File "a3update.py", line 158, in
update_mods()
File "a3update.py", line 129, in update_mods
time.sleep(5)
Did you set the execute permission on steamcmd.sh? (chmod +x steamcmd.sh
)
Did you set the execute permission on steamcmd.sh? (
chmod +x steamcmd.sh
)
I just did it again just in case, but yes. I executed like 1h ago to install Arma.
I just did it again just in case, but yes. I executed like 1h ago to install Arma.
Hmm not sure what's up. I guess you can try printing the steam_cmd_params
variable and running the steamcmd manually just to see if that works properly. It's at https://gist.github.com/marceldev89/12da69b95d010c8a810fd384cca8d02a#file-a3update-py-L124.
Hit me up on the Arma 3 or ALiVE mod discord if you need help with further debugging (my user id is marceldev89#4565
, no clue if you can talk to that directly or not..). A bit easier than doing it here. ๐
OK, I think I've found the problem. My Steam password had some special characters and I bet the script didn't like those. I've changed the password and now I'm getting this which I think means Steam blocked my server's IP for a while.
Logging in user 'me' to Steam Public ...
FAILED login with result code Rate Limit Exceeded
EDIT: I post this here just in case somebody has the same issue. Thanks a lot for your help!
Ah, that makes sense, kinda. Probably should've quoted the steamcmd parameters. I haven't ran a server for ages but when I do I'll try to take a look at those parentheses and other special char issues.
Thanks for reporting back. ๐
Hey,
maybe some of you guys could help me.
Iam sadly getting an error when using this script.
"sh: 1: /home/steam/steamcmd: Permission denied"
Well, it's saying permission denied so make sure your user has permissions to access and execute steamcmd
Yeah nvm I am just an idiot ^^
Had a typo in it.
STEAM_CMD = "/home/steam/steamcmd"
instead of
STEAM_CMD = "/home/steam/steamcmd/steamcmd.sh"
Haha all good. Glad you got it working! :)
Love the script been using it for a while now I was wondering if there is a work around for
steam_cmd_params += " +quit"
because with how steam changed it to that you have to own the game to download workshop items for the script to work normally having to disable 2factor is a bit of a pain every time you want to update the modpack for the server
I tried to think of a workaround but probably sleeping during my python lectures wasn't the brightest idea ^^
OK, got it!! Thanks a lot.