-
Notifications
You must be signed in to change notification settings - Fork 2
/
update.sh
executable file
·624 lines (573 loc) · 25.6 KB
/
update.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
#!/bin/bash
#todo: add --purge -p option to clean up old mods not in current html modlists
# exit when any command fails
set -e
# Get the directory where this file is located
script_dir="$( pushd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# The home directory for the user that launches the server
home_dir=$(eval echo ~$USER)
#navigate to config directory update the config and return.
case $script_dir in
${home_dir}/*) ;;
*) echo "ERROR: Not running as the correct user, attempting this will result in broken permissions."; exit 1;
esac
# Battalion config directory
config_dir="$home_dir/config"
# The directory where ARMA is installed
arma_dir="$home_dir/arma3"
# The directory where Workshop mods should be downloaded to
workshop_dir="$home_dir/workshop_mods"
# The directory that Steam creates within the specified directory (where mods are actually downloaded to)
mod_install_dir="$workshop_dir/steamapps/workshop/content/107410"
# The file for storing Steam credentials
steam_creds_file="$home_dir/.steam_credentials"
# The filename for the HTML template that can be imported to Steam to specify the modpack
workshop_template_dir="$home_dir/workshop_templates"
# The config repo settings file
settings_file="$config_dir/settings.json"
# The web panel config template file
web_panel_config_template="$script_dir/config.json"
# The web panel config file
web_panel_config_file="$script_dir/arma-server-web-admin/config.js"
# The web panel servers template file
web_panel_servers_template="$config_dir/servers.json.template"
# The web panel servers file
web_panel_servers_file="$script_dir/arma-server-web-admin/servers.json"
# The .htpasswd file with credentials for accessing the server control panel
htpasswd_file="$home_dir/panel.htpasswd"
# Profiles directories
repo_profiles_dir="$config_dir/profiles"
web_console_profiles_dir="$home_dir/arma-profiles"
# ARMA on Linux does weird things and appends a "/home" directory to the actual provided directory
arma_profiles_dir="$web_console_profiles_dir/home"
# Userconfig directories
repo_userconfig_dir="$config_dir/userconfig"
arma_userconfig_dir="$arma_dir/userconfig"
# The basic.cfg file to use
basic_cfg_file="$config_dir/basic.cfg"
# How many times to try downloading a mod before erroring out (multiple attempts required for large mods due to timeouts)
mod_download_attempts=6
# How many times to try downloading ARMA before erroring out (multiple attempts required on slow connections due to timeouts)
arma_download_attempts=6
# Default values for switches/options
force_new_steam_creds=false
force_new_web_panel_creds=false
force_validate=""
skip_steam_check=false
passwords_only=false
# The default branch/user
config_branch="master"
while getopts ":swvnpb:" opt; do
case $opt in
s) # force new credentials for Steam
force_new_steam_creds=true
;;
w) # force new credentials for the web panel
force_new_web_panel_creds=true
;;
v) # validate ARMA/mod files that have been downloaded
force_validate="validate"
echo "Forcing validation of Arma 3 and Workshop files"
;;
b) # Set the config branch
config_branch="$OPTARG"
;;
n) # skip Steam file checks for Arma and existing mods
skip_steam_check=true
echo "Skipping file checks for existing Arma 3 and Workshop files"
;;
p) # skip Steam file checks for Arma and existing mods
passwords_only=true
echo "Running update script for passwords-only"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
# A function for trimming strings
trim() {
echo "$(echo -e "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
}
# A function for getting Steam username/password from the command line
get_steam_creds () {
printf "Steam username: "
read steam_username </dev/tty
printf "Steam password: "
read -s steam_password </dev/tty
printf "\nSteam password (confirm): "
read -s steam_password2 </dev/tty
printf "\n"
if [ "$steam_password" != "$steam_password2" ]; then
echo "Passwords do not match! Try again."
get_steam_creds
else
printf "$steam_username\n$steam_password" > $steam_creds_file
fi
}
# A function that attempts to load stored Steam credentials if they exist, and
# asks for new ones to be entered if they don't (or if the '-s' switch was used)
load_steam_creds () {
if $force_new_steam_creds; then
echo "Forcing new Steam credentials"
get_steam_creds
elif [ ! -f "$steam_creds_file" ]; then
# The credentials file doesn't exist, so require new credentials
echo "No Steam credentials found, new ones required."
get_steam_creds
else
# Try to read it from the credentials file
readarray -t steam_vars < "$steam_creds_file"
if [ ${#steam_vars[@]} -ne 2 ]; then
# The credentials file has an invalid format, so require new credentials
echo "Invalid Steam credentials found, new ones required."
get_steam_creds
else
# The credentials file exists and has a valid format, so load the credentials from it
steam_username=${steam_vars[0]}
steam_password=${steam_vars[1]}
fi
fi
}
# A function for getting web panel username/password from the command line
get_web_panel_creds () {
printf "Web panel username: "
read web_panel_username </dev/tty
code=1
set +e
while [ $code -ne 0 ]; do
htpasswd -c "$htpasswd_file" "$web_panel_username"
code=$?
done
set -e
}
# A function that attempts to load stored web panel credentials if they exist, and
# asks for new ones to be entered if they don't (or if the '-w' switch was used)
load_web_panel_creds () {
if $force_new_web_panel_creds; then
echo "Forcing new web panel credentials"
get_web_panel_creds
elif [ ! -f "$htpasswd_file" ]; then
# The credentials file doesn't exist, so require new credentials
echo "No web panel credentials found, new ones required."
get_web_panel_creds
fi
}
run_steam_cmd() { # run_steam_cmd command attempts
# Don't exit on errors
set +e
# On a slow connection, the download may timeout, so we have to try multiple times (will resume the download)
for (( i=0; i<$2; i++ )); do
if [ $i -eq 0 ]; then
echo "Running steamcmd for $3"
else
echo "Retrying steamcmd for $3"
fi
printf "\e[2m"
# Create a file descriptor that directs to stdout
exec 9>&1
# Run the steam command and tee the output to stdout and the variable
result=$($1 2>&1 | tee >(cat - >&9))
# Track the exit code
code=$?
printf "\n\n\e[0m"
# Break the loop if the command was successful
if [ $code == 0 ] && echo "$result" | grep -iqF success && ! echo "$result" | grep -iqF failure; then
echo "Steamcmd for $3 was successful!"
set -e
return 0
fi
done
echo "Steamcmd for $3 failed: $result"
set -e
return 1
}
# Call the function for loading Steam credentials
load_steam_creds
# Call the function for loading web panel credentials
load_web_panel_creds
# If the script was only called to set passwords, exit out
if $passwords_only ; then
exit 0
fi
# Update the config directory
git -C "$config_dir" fetch --all
git -C "$config_dir" reset --hard "origin/$config_branch"
#---------------------------------------------
# Process the config repo 'settings.json' file
# Ensure the file exists
if [ ! -f "$settings_file" ]; then
echo "ERROR: missing 'settings.json' file in the config repository" >&2; exit 1
fi
# Load the settings JSON
settings_json=$(jq -enf "$settings_file")
if [ -z "$settings_json" ]; then
echo "ERROR: failed to parse 'settings.json' in the config repository" >&2; exit 1
fi
# Extract the battalion name from the settings file
battalion=$(echo "$settings_json" | jq -r ".battalion")
if [ -z "$battalion" ]; then
echo "ERROR: 'settings.json' file in config repository has no 'battalion' key/value" >&2; exit 1
fi
# Extract the list of game admins from the settings file
admin_steam_ids=$(echo "$settings_json" | jq -r ".admin_steam_ids")
if [ -z "$admin_steam_ids" ]; then
echo "ERROR: 'settings.json' file in config repository has no 'admin_steam_ids' key/value" >&2; exit 1
fi
# Extract the web console port from the settings file
web_console_local_port=$(echo "$settings_json" | jq -r ".web_console_local_port")
if [ -z "$web_console_local_port" ]; then
echo "ERROR: 'settings.json' file in config repository has no 'web_console_local_port' key/value" >&2; exit 1
fi
# Extract the server name prefix from the settings file
server_prefix=$(echo "$settings_json" | jq -r ".server_prefix")
if [ -z "$server_prefix" ]; then
echo "ERROR: 'settings.json' file in config repository has no 'server_prefix' key/value" >&2; exit 1
fi
# Extract the server name suffix from the settings file
server_suffix=$(echo "$settings_json" | jq -r ".server_suffix")
#---------------------------------------------
# Process the server repo 'config.json' file
# Ensure the config.json file for the web panel config exists
if [ ! -f "$web_panel_config_template" ]; then
echo "ERROR: missing 'config.json' file in the server repository" >&2; exit 1
fi
# Load the web panel config JSON
panel_config_json=$(jq -enf "$web_panel_config_template")
if [ -z "$panel_config_json" ]; then
echo "ERROR: failed to parse 'config.json' in the server repository" >&2; exit 1
fi
# Ensure the basic.cfg file exists
if [ ! -f "$basic_cfg_file" ]; then
echo "ERROR: missing 'basic.cfg' file in the config repository" >&2; exit 1
fi
# Process the config repo 'servers.json.template' file
if [ ! -f "$web_panel_servers_template" ]; then
echo "ERROR: missing 'servers.json.template' file in the config repository" >&2; exit 1
fi
# If there isn't an existing web panel servers file, create one
if [ ! -f "$web_panel_servers_file" ]; then
temp_password=$(tr -dc '[:alnum:]' < /dev/urandom | dd bs=4 count=4 2>/dev/null)
sed -e "s#\${password}#$temp_password#g" "$web_panel_servers_template" >"$web_panel_servers_file"
fi
# Names of output template files
workshop_template_file_required="$workshop_template_dir/$battalion (Required).html"
workshop_template_file_optional="$workshop_template_dir/$battalion (Optional).html"
workshop_template_file_all="$workshop_template_dir/$battalion (All).html"
# Regex for checking if a string is all digits
number_regex='^[0-9]+$'
# Mods to validate (have already been downloaded, just need to be checked for updates)
validate_mod_ids=()
# Mods to download (do not yet exist on the server)
download_mod_ids=()
# Mods that run server-side only
server_mod_ids=()
# Mods that clients must have
client_required_mod_ids=()
# Mods that clients may have
client_optional_mod_ids=()
#all mods (for download/verification)
all_mods=()
# Load the prefix of the template file
workshop_template_prefix=$(sed -e "s#\${battalion}#$battalion#g" "$script_dir/workshop_template_prefix.html.template")
# Prepare the required mods prefix
workshop_template_required=$(echo "$workshop_template_prefix" | sed -e "s#\${preset_name}#$battalion Required#g" -e "s#\${subname}#Required#g")
# Prepare the optional mods prefix
workshop_template_optional=$(echo "$workshop_template_prefix" | sed -e "s#\${preset_name}#$battalion Optional#g" -e "s#\${subname}#Optional#g")
# Prepare the all mods prefix
workshop_template_all=$(echo "$workshop_template_prefix" | sed -e "s#\${preset_name}#$battalion All#g" -e "s#\${subname}#All (Required + Optional)#g")
workshop_template_suffix=$(sed -e "s#\${battalion}#$battalion#g" "$script_dir/workshop_template_suffix.html.template")
workshop_template_mod=$(<"$script_dir/workshop_template_mod.html.template")
# Delete the template directory if it exists (to clean it out)
rm -rf "$workshop_template_dir"
# Re-create the template directory
mkdir -p "$workshop_template_dir"
#process html files or mod.txt
if ls $config_dir/*.html 1> /dev/null 2>&1; then
printf "\e[32mHTML config files exist\e[0m\n"
for modlist in $config_dir/*.html; do
echo "processing $modlist..."
mapfile -t this_modlist < <( python3 "$script_dir/process_html.py" "$modlist" )
name=$(basename "$modlist" ".html")
if [[ $modlist == *"server"* ]]; then
printf "\e[2mAdding mods from $name modlist to the server mods\e[0m\n"
server_mod_ids+=("${this_modlist[@]}")
elif [[ $modlist == *"optional"* ]]; then
printf "\e[2mAdding mods from $name modlist to the client optional mods\e[0m\n"
client_optional_mod_ids+=("${this_modlist[@]}")
else #otherwise it is probably a client mod
printf "\e[2mAdding mods from $name modlist to the client required mods\e[0m\n"
client_required_mod_ids+=("${this_modlist[@]}")
fi
done
elif [[ -f "$config_dir/mods.txt" ]]; then
#do mod.txt processing
printf "\e[32mHTML config files do not exist, Using mod.txt instead\e[0m\n"
# This reads each line of the mods.txt file, with a special condition for last lines that don't have a trailing newline
while read line || [ -n "$line" ]; do
# Increment the line counter
line_no=$((line_no+1))
# Trim whitespace of the ends of the line
line_trimmed="$(trim "$line")"
IFS='#' read -ra comment <<< "$line_trimmed"
# If the line was empty or just had a comment, skip it
if [ -z "${comment[0]}" ]; then
continue
fi
# Split the part before any comments on commas
IFS=',' read -ra parts <<< "${comment[0]}"
# Parse the line into its fields, trimming whitespace from each
mod_id="$(trim "${parts[0]}")"
mod_name="$(trim "${parts[1]}")"
mod_type="$(trim "${parts[2]}")"
# Ensure that the mod ID is a number (digits only)
if ! [[ $mod_id =~ $number_regex ]] ; then
echo "Error: invalid line in mods.txt, line $line_no - '$mod_id'" >&2; exit 1
fi
# Create the string that would represent this mod in the workshop template file
workshop_template_section=$(echo $workshop_template_mod | sed -e "s#\${mod_name}#$mod_name#g" -e "s#\${mod_id}#$mod_id#g")
# Check if it's a server-only mod
if [ $mod_type -eq 0 ]; then
# Add it to the list of server mods
server_mod_ids+=($mod_id)
# Check if it's a client required mod
elif [ $mod_type -eq 1 ]; then
# Add it to the list of client-required mods
client_required_mod_ids+=($mod_id)
# Add the HTML to the workshop template required file
workshop_template_required+="$workshop_template_section"
workshop_template_all+="$workshop_template_section"
elif [ $mod_type -eq 2 ]; then
# Optional client mods are not downloaded, just tracked for whitelisting
client_optional_mod_ids+=($mod_id)
# Add the HTML to the workshop template required file
workshop_template_optional+="$workshop_template_section"
workshop_template_all+="$workshop_template_section"
elif [ $mod_type -eq 3 ]; then
# Optional client mods are not downloaded, just tracked for whitelisting (but these are hidden ones that don't get added to the template)
client_optional_mod_ids+=($mod_id)
else
# The mod type was unrecognized
echo "Error: unknown mod type in mods.txt, line $line_no - '$mod_type'" >&2; exit 1
fi
done < "$config_dir/mods.txt"
# Append the workshop template suffix
workshop_template_required+="$workshop_template_suffix"
workshop_template_optional+="$workshop_template_suffix"
workshop_template_all+="$workshop_template_suffix"
# Write the complete workshop templates to file
echo "$workshop_template_required" > "$workshop_template_file_required"
echo "$workshop_template_optional" > "$workshop_template_file_optional"
echo "$workshop_template_all" > "$workshop_template_file_all"
else
printf "\e[31mERROR: Could not locate mod.txt or html files, please check configuration directory\e[0m\n" >&2; exit 1
fi
# Copy the ARMA profiles
find "$repo_profiles_dir" -mindepth 1 -type f -print0 |
while IFS= read -r -d '' profile_file; do
if [ ${profile_file: -13} != ".Arma3Profile" ]; then
echo "File '$profile_file' in profiles directory does not have a '.Arma3Profile' extension" >&2; exit 1
fi
profile_basename=$(basename "$profile_file")
profile_name=${profile_basename%.Arma3Profile}
if [[ ! $profile_name =~ ^[0-9a-zA-Z]+$ ]]; then
echo "File '$profile_file' in profiles directory does not have an alphanumeric profile name" >&2; exit 1
fi
# Create the profile directory
mkdir -p "$arma_profiles_dir/$profile_name"
output_file="$arma_profiles_dir/$profile_name/$profile_basename"
# Copy over the profile file
cp "$profile_file" "$output_file"
# Convert any Windows line-ending issues
dos2unix "$output_file"
done
# check whether mod needs downloading or validating
all_mods+=("${client_optional_mod_ids[@]}" "${client_required_mod_ids[@]}" "${server_mod_ids[@]}")
for mod_id in "${all_mods[@]}"
do
if [ -d "$mod_install_dir/$mod_id" ]; then
# If the install directory for this mod exists, then it's been successfully downloaded
# in the past so we just need to validate it
validate_mod_ids+=($mod_id)
else
# If it doesn't exist, it needs to be downloaded
download_mod_ids+=($mod_id)
fi
done
# Create the base steamcmd command with the login credentials
base_steam_cmd="/usr/games/steamcmd +login $steam_username $steam_password"
if ! $skip_steam_check ; then
arma_update_cmd="$base_steam_cmd +force_install_dir $arma_dir +app_update 233780 $force_validate +quit"
run_steam_cmd "$arma_update_cmd" $arma_download_attempts "downloading ARMA"
if [ $? != 0 ]; then
exit 1
fi
fi
# Next steps require the arma directory to exist
if [ ! -d $arma_dir ]; then
echo "ERROR: Arma install directory does not exist, cannot continue (did you call this script with the '-n' option, thereby skipping Arma install?)" >&2; exit 1
fi
# Copy the userconfig files
# Remove the existing userconfig folder
rm -rf "$arma_userconfig_dir"
# Copy over the new one
cp -R "$repo_userconfig_dir" "$arma_userconfig_dir"
# Remove the readme file in the mpmisisons folder (so it doesn't show up on the web console)
rm -f "$arma_dir/mpmissions/readme.txt"
# Create the base command for downloading a mod
mod_download_base_cmd="$base_steam_cmd +force_install_dir $workshop_dir"
# This section compiles a single command for validating all existing mods
# Since they're already existing, updates should be small and can be completed
# in one attempt without timing out.
# Only run this section if there are any mods to validate and we're not force-skipping the check
if [ ${#validate_mod_ids[@]} -gt 0 ] && ! $skip_steam_check ; then
mod_validate_cmd="$mod_download_base_cmd"
# Add a command to download each mod in this array
for mod_id in "${validate_mod_ids[@]}"; do
mod_validate_cmd="$mod_validate_cmd +workshop_download_item 107410 $mod_id $force_validate"
done
# Run the command
mod_validate_cmd="$mod_validate_cmd +quit"
run_steam_cmd "$mod_validate_cmd" 1 "validating existing mods"
if [ $? != 0 ]; then
exit 1
fi
fi
# This section downloads new mods by attempting each one separately, and attempting it multiple times
# so that it re-tries after timeouts to continue the download.
# Only run this section if there are any mods to download
if [ ${#download_mod_ids[@]} -gt 0 ]; then
# Download each mod
for mod_id in "${download_mod_ids[@]}"; do
# Prepare the command for doing the download
mod_cmd="$mod_download_base_cmd +workshop_download_item 107410 $mod_id validate +quit"
# Call the function that runs the command
run_steam_cmd "$mod_cmd" $mod_download_attempts "downloading mod $mod_id"
if [ $? != 0 ]; then
exit 1
fi
done
fi
# This section is for re-packaging the server-only workshop mods into a single
# mod folder. The server config then points to this folder to load server-side mods.
server_mods_name="mods_server"
server_mods_dir="$arma_dir/$server_mods_name"
# This is the directory where the PBOs are linked to
server_addons_dir="$server_mods_dir/addons"
# Remove the entire server_mods directory to ensure it's clean
rm -rf $server_mods_dir
# Re-create the directory structure
mkdir -p $server_addons_dir
# Loop through each server-only mod
for mod_id in "${server_mod_ids[@]}"; do
# This is the directory where the mod was downloaded
mod_dir="$mod_install_dir/$mod_id"
# Find all "addon" directories within the download directory
readarray -d '' found_dirs < <(find "$mod_dir" -maxdepth 1 -type d -iname 'addons' -print0)
# If no "addon" directories were found, that's an error
if [ ${#found_dirs[@]} -eq 0 ]; then
echo "Server mod with ID $mod_id has no 'addons' directory" >&2; exit 1
fi
# If multiple "addon" directories were found, that's an error
if [ ${#found_dirs[@]} -gt 1 ]; then
echo "Server mod with ID $mod_id has multiple 'addons' directories" >&2; exit 1
fi
# The directory where the mod PBOs were downloaded to
addon_dir=${found_dirs[0]}
# Loop through all files that are in the mod's addons dir
find "$addon_dir" -type f -printf '%P\0' |
while IFS= read -r -d '' file; do
# The link filename, in lowercase
output_file="$server_addons_dir/${file,,}"
# Create any sub-directories for the file
mkdir -p "$(dirname "$output_file")"
# Symlink the file
ln -s "$addon_dir/$file" "$output_file"
done
done
# This section is for re-packaging the client-and-server workshop mods into a single
# mod folder. The web control panel can then select this merged pack.
client_mods_dir="mods_client"
client_mods_path="$arma_dir/$client_mods_dir"
# This is the directory where the keys are linked to
client_keys_dir="$arma_dir/keys"
# Remove the entire client mods directory to ensure it's clean
rm -rf $client_mods_path
# Re-create the directory structure
mkdir -p $client_mods_path
# Delete all existing symlinked keys in the Arma keys directory
find "$client_keys_dir" -type l -delete
# Create the mod startup parameter
mod_param="-mod="
# Loop through each client-required mod to link the mod files
for mod_id in "${client_required_mod_ids[@]}"; do
# This is the directory where the mod was downloaded
mod_dir="$mod_install_dir/$mod_id"
mod_param+="$client_mods_dir/$mod_id;"
# Loop through all files that are in the mod folder to symlink them
find "$mod_dir" -type f -printf '%P\0' |
while IFS= read -r -d '' f; do
file_lowercase="${f,,}"
# The link filename, in lowercase
output_file="$client_mods_path/$mod_id/$file_lowercase"
# Create any sub-directories for the file
mkdir -p "$(dirname "$output_file")"
# If it's the meta.cpp or mod.cpp file, it's special
if [ "$file_lowercase" == "meta.cpp" ] || [ "$file_lowercase" == "mod.cpp" ]; then
# Copy the file (instead of symlink) so we can edit it
cp "$mod_dir/$f" "$output_file"
# Try converting from UTF-8 with a silent fail (won't do anything if input wasn't UTF-8)
# This is to handle the occational mod that uses CRLF line endings
dos2unix "$output_file"
# Change the mod name to be the mod ID so it takes less space in the packet sent to Steam (to fix issues with mod list in Arma 3 Launcher)
sed -i "s/^\(name\s*=\s*\).*$/\1\"$mod_id\";/" "$output_file"
else
# Otherwise, just symlink the file
ln -s "$mod_dir/$f" "$output_file"
fi
done
done
# All mods that should have their bikeys copied to the Arma key directory
key_mods+=( "${client_required_mod_ids[@]}" "${client_optional_mod_ids[@]}" )
# Loop over them to link their bikey files
for mod_id in "${key_mods[@]}"; do
# This is the directory where the mod was downloaded
mod_dir="$mod_install_dir/$mod_id"
# Find all "bikey" files within the download directory
readarray -d '' found_keys < <(find "$mod_dir" -type f -iname '*.bikey' -print0)
# If multiple "keys" directories were found, that's an error
if [ ${#found_keys[@]} -gt 1 ]; then
echo "Client mod with ID $mod_id has multiple '.bikey' files" >&2; exit 1
fi
if [ ${#found_keys[@]} -gt 0 ]; then
# The filename without the path
key_basename=$(basename "${found_keys[0]}")
# The link filename, in lowercase
output_file="$client_keys_dir/${key_basename,,}"
# Symlink the file (overwriting existing links/files of the same name)
ln -sf "${found_keys[0]}" "$output_file"
fi
done
# If there's at least one client-side mod to load, add a startup parameter for it
if [ ${#client_required_mod_ids[@]} -gt 0 ]; then
panel_config_json=$(echo "$panel_config_json" | jq ".parameters |= . + [\"$mod_param\"]")
fi
# If there's at least one server-only mod to load, add the config value for it
if [ ${#server_mod_ids[@]} -gt 0 ]; then
panel_config_json=$(echo "$panel_config_json" | jq ".serverMods |= . + [\"$server_mods_name\"]")
fi
# If the server suffix is provided, set it in the config file
if [ ! -z "$server_suffix" ]; then
panel_config_json=$(echo "$panel_config_json" | jq ".suffix = \"$server_suffix\"")
fi
# Add all other requried fields
panel_config_json=$(echo "$panel_config_json" | jq ".path = \"$arma_dir\" | .port = $web_console_local_port | .prefix = \"$server_prefix\" | .admins = $admin_steam_ids | .parameters |= . + [\"-profiles=$web_console_profiles_dir\", \"-cfg=$basic_cfg_file\"]")
# Write the web panel config.js file
echo "module.exports = $panel_config_json" > "$web_panel_config_file"
# Uses sudo but shouldnt require a password if install.sh worked correctly
sudo systemctl restart "arma3-web-console-$USER"