mods_core.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. #!/bin/bash
  2. # LinuxGSM command_mods_install.sh function
  3. # Author: Daniel Gibbs
  4. # Contributor: UltimateByte
  5. # Website: https://linuxgsm.com
  6. # Description: Core functions for mods list/install/update/remove
  7. local commandname="MODS"
  8. local commandaction="Mods"
  9. local function_selfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
  10. # Files and Directories
  11. modsdir="${lgsmdir}/mods"
  12. modstmpdir="${modsdir}/tmp"
  13. extractdir="${modstmpdir}/extract"
  14. modsinstalledlist="installed-mods.txt"
  15. modsinstalledlistfullpath="${modsdir}/${modsinstalledlist}"
  16. ## Installation
  17. # Download management
  18. fn_mod_install_files(){
  19. fn_fetch_file "${modurl}" "${modstmpdir}" "${modfilename}"
  20. # Check if variable is valid checking if file has been downloaded and exists
  21. if [ ! -f "${modstmpdir}/${modfilename}" ]; then
  22. fn_print_failure "An issue occurred downloading ${modprettyname}"
  23. fn_script_log_fatal "An issue occurred downloading ${modprettyname}"
  24. core_exit.sh
  25. fi
  26. if [ ! -d "${extractdir}" ]; then
  27. mkdir -p "${extractdir}"
  28. fi
  29. fn_dl_extract "${modstmpdir}" "${modfilename}" "${extractdir}"
  30. }
  31. # Convert mod files to lowercase if needed
  32. fn_mod_lowercase(){
  33. if [ "${modlowercase}" == "LowercaseOn" ]; then
  34. echo -en "converting ${modprettyname} files to lowercase..."
  35. sleep 0.5
  36. fn_script_log_info "Converting ${modprettyname} files to lowercase"
  37. fileswc=$(find "${extractdir}" -depth | wc -l)
  38. echo -en "\r"
  39. while read -r src; do
  40. dst=$(dirname "${src}"$(/)basename "${src}" | tr 'A-Z' 'a-z')
  41. if [ "${src}" != "${dst}" ]
  42. then
  43. [ ! -e "${dst}" ] && mv -T "${src}" "${dst}" || echo "${src} was not renamed"
  44. local exitcode=$?
  45. ((renamedwc++))
  46. fi
  47. echo -en "${renamedwc} / ${totalfileswc} / ${fileswc} converting ${modprettyname} files to lowercase..." $'\r'
  48. ((totalfileswc++))
  49. done < <(find "${extractdir}" -depth)
  50. echo -en "${renamedwc} / ${totalfileswc} / ${fileswc} converting ${modprettyname} files to lowercase..."
  51. if [ ${exitcode} -ne 0 ]; then
  52. fn_print_fail_eol_nl
  53. core_exit.sh
  54. else
  55. fn_print_ok_eol_nl
  56. fi
  57. sleep 0.5
  58. fi
  59. }
  60. # Create ${modcommand}-files.txt containing the full extracted file/directory list
  61. fn_mod_create_filelist(){
  62. echo -en "building ${modcommand}-files.txt..."
  63. sleep 0.5
  64. # ${modsdir}/${modcommand}-files.txt
  65. find "${extractdir}" -mindepth 1 -printf '%P\n' > "${modsdir}/${modcommand}-files.txt"
  66. local exitcode=$?
  67. if [ ${exitcode} -ne 0 ]; then
  68. fn_print_fail_eol_nl
  69. fn_script_log_fatal "Building ${modsdir}/${modcommand}-files.txt"
  70. core_exit.sh
  71. else
  72. fn_print_ok_eol_nl
  73. fn_script_log_pass "Building ${modsdir}/${modcommand}-files.txt"
  74. fi
  75. # Adding removed files if needed
  76. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  77. cat "${modsdir}/.removedfiles.tmp" >> "${modsdir}/${modcommand}-files.txt"
  78. fi
  79. sleep 0.5
  80. }
  81. # Copy the mod into serverfiles
  82. fn_mod_copy_destination(){
  83. echo -en "copying ${modprettyname} to ${modinstalldir}..."
  84. sleep 0.5
  85. cp -Rf "${extractdir}/." "${modinstalldir}/"
  86. local exitcode=$?
  87. if [ ${exitcode} -ne 0 ]; then
  88. fn_print_fail_eol_nl
  89. fn_script_log_fatal "Copying ${modprettyname} to ${modinstalldir}"
  90. else
  91. fn_print_ok_eol_nl
  92. fn_script_log_pass "Copying ${modprettyname} to ${modinstalldir}"
  93. fi
  94. }
  95. # Add the mod to the installed-mods.txt
  96. fn_mod_add_list(){
  97. if [ ! -n "$(sed -n "/^${modcommand}$/p" "${modsinstalledlistfullpath}")" ]; then
  98. echo "${modcommand}" >> "${modsinstalledlistfullpath}"
  99. fn_script_log_info "${modcommand} added to ${modsinstalledlist}"
  100. fi
  101. }
  102. # Prevent sensitive directories from being erased upon uninstall by removing them from: ${modcommand}-files.txt
  103. fn_mod_tidy_files_list(){
  104. # Check file list validity
  105. fn_check_mod_files_list
  106. # Output to the user
  107. echo -en "tidy up ${modcommand}-files.txt..."
  108. sleep 0.5
  109. fn_script_log_info "Tidy up ${modcommand}-files.txt"
  110. # Lines/files to remove from file list (end with ";" separator)
  111. removefromlist="cfg;addons;RustDedicated_Data;RustDedicated_Data\/Managed;RustDedicated_Data\/Managed\/x86;RustDedicated_Data\/Managed\/x64;"
  112. # Loop through files to remove from file list,
  113. # generate elements to remove from list
  114. removefromlistamount="$(echo "${removefromlist}" | awk -F ';' '{ print NF }')"
  115. # Test all subvalue of "removefromlist" using the ";" separator
  116. for ((filesindex=1; filesindex < removefromlistamount; filesindex++)); do
  117. # Put current file into test variable
  118. removefilevar="$(echo "${removefromlist}" | awk -F ';' -v x=${filesindex} '{ print $x }')"
  119. # Delete line(s) matching exactly
  120. sed -i "/^${removefilevar}$/d" "${modsdir}/${modcommand}-files.txt"
  121. # Exit on error
  122. local exitcode=$?
  123. if [ ${exitcode} -ne 0 ]; then
  124. fn_print_fail_eol_nl
  125. fn_script_log_fatal "Error while tidying line: ${removefilevar} from: ${modsdir}/${modcommand}-files.txt"
  126. core_exit.sh
  127. break
  128. fi
  129. done
  130. fn_print_ok_eol_nl
  131. # Sourcemod fix
  132. # Remove metamod from sourcemod fileslist
  133. if [ "${modcommand}" == "sourcemod" ]; then
  134. # Remove addons/metamod & addons/metamod/sourcemod.vdf from ${modcommand}-files.txt
  135. sed -i "/^addons\/metamod$/d" "${modsdir}/${modcommand}-files.txt"
  136. sed -i "/^addons\/metamod\/sourcemod.vdf$/d" "${modsdir}/${modcommand}-files.txt"
  137. fi
  138. }
  139. ## Information Gathering
  140. # Get details of a mod any (relevant and unique, such as full mod name or install command) value
  141. fn_mod_get_info(){
  142. # Variable to know when job is done
  143. modinfocommand="0"
  144. # Find entry in global array
  145. for ((index=0; index <= ${#mods_global_array[@]}; index++)); do
  146. # When entry is found
  147. if [ "${mods_global_array[index]}" == "${currentmod}" ]; then
  148. # Go back to the previous "MOD" separator
  149. for ((index=index; index <= ${#mods_global_array[@]}; index--)); do
  150. # When "MOD" is found
  151. if [ "${mods_global_array[index]}" == "MOD" ]; then
  152. # Get info
  153. fn_mods_define
  154. modinfocommand="1"
  155. break
  156. fi
  157. done
  158. fi
  159. # Exit the loop if job is done
  160. if [ "${modinfocommand}" == "1" ]; then
  161. break
  162. fi
  163. done
  164. # What happens if mod is not found
  165. if [ "${modinfocommand}" == "0" ]; then
  166. fn_script_log_error "Could not find information for ${currentmod}"
  167. fn_print_error_nl "Could not find information for ${currentmod}"
  168. core_exit.sh
  169. fi
  170. }
  171. # Define all variables for a mod at once when index is set to a separator
  172. fn_mods_define(){
  173. if [ -z "$index" ]; then
  174. fn_script_log_fatal "index variable not set. Please report an issue."
  175. fn_print_error "index variable not set. Please report an issue."
  176. echo "* https://github.com/GameServerManagers/LinuxGSM/issues"
  177. core_exit.sh
  178. fi
  179. modcommand="${mods_global_array[index+1]}"
  180. modprettyname="${mods_global_array[index+2]}"
  181. modurl="${mods_global_array[index+3]}"
  182. modfilename="${mods_global_array[index+4]}"
  183. modsubdirs="${mods_global_array[index+5]}"
  184. modlowercase="${mods_global_array[index+6]}"
  185. modinstalldir="${mods_global_array[index+7]}"
  186. modkeepfiles="${mods_global_array[index+8]}"
  187. modengines="${mods_global_array[index+9]}"
  188. modgames="${mods_global_array[index+10]}"
  189. modexcludegames="${mods_global_array[index+11]}"
  190. modsite="${mods_global_array[index+12]}"
  191. moddescription="${mods_global_array[index+13]}"
  192. }
  193. # Builds list of installed mods
  194. # using installed-mods.txt grabing mod info from mods_list.sh
  195. fn_mods_installed_list(){
  196. fn_mods_count_installed
  197. # Set/reset variables
  198. installedmodsline="1"
  199. installedmodslist=()
  200. modprettynamemaxlength="0"
  201. modsitemaxlength="0"
  202. moddescriptionmaxlength="0"
  203. modcommandmaxlength="0"
  204. # Loop through every line of the installed mods list ${modsinstalledlistfullpath}
  205. while [ "${installedmodsline}" -le "${installedmodscount}" ]; do
  206. currentmod="$(sed "${installedmodsline}q;d" "${modsinstalledlistfullpath}")"
  207. # Get mod info to make sure mod exists
  208. fn_mod_get_info
  209. # Add the mod to available commands
  210. installedmodslist+=( "${modcommand}" )
  211. # Increment line check
  212. ((installedmodsline++))
  213. done
  214. if [ -n "${installedmodscount}" ]; then
  215. fn_script_log_info "${installedmodscount} addons/mods are currently installed"
  216. fi
  217. }
  218. # Loops through mods_global_array to define available mods & provide available commands for mods installation
  219. fn_mods_available(){
  220. # First, reset variables
  221. compatiblemodslist=()
  222. availablemodscommands=()
  223. # Find compatible games
  224. # Find separators through the global array
  225. for ((index="0"; index <= ${#mods_global_array[@]}; index++)); do
  226. # If current value is a separator; then
  227. if [ "${mods_global_array[index]}" == "${modseparator}" ]; then
  228. # Set mod variables
  229. fn_mods_define
  230. # Test if game is compatible
  231. fn_mod_compatible_test
  232. # If game is compatible
  233. if [ "${modcompatibility}" == "1" ]; then
  234. # Put it into an array to prepare user output
  235. compatiblemodslist+=( "${modprettyname}" "${modcommand}" "${modsite}" "${moddescription}" )
  236. # Keep available commands in an array to make life easier
  237. availablemodscommands+=( "${modcommand}" )
  238. fi
  239. fi
  240. done
  241. }
  242. ## Mod compatibility check
  243. # Find out if a game is compatible with a mod from a modgames (list of games supported by a mod) variable
  244. fn_compatible_mod_games(){
  245. # Reset test value
  246. modcompatiblegame="0"
  247. # If value is set to GAMES (ignore)
  248. if [ "${modgames}" != "GAMES" ]; then
  249. # How many games we need to test
  250. gamesamount="$(echo "${modgames}" | awk -F ';' '{ print NF }')"
  251. # Test all subvalue of "modgames" using the ";" separator
  252. for ((gamevarindex=1; gamevarindex < gamesamount; gamevarindex++)); do
  253. # Put current game name into modtest variable
  254. gamemodtest="$( echo "${modgames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  255. # If game name matches
  256. if [ "${gamemodtest}" == "${gamename}" ]; then
  257. # Mod is compatible !
  258. modcompatiblegame="1"
  259. fi
  260. done
  261. fi
  262. }
  263. # Find out if an engine is compatible with a mod from a modengines (list of engines supported by a mod) variable
  264. fn_compatible_mod_engines(){
  265. # Reset test value
  266. modcompatibleengine="0"
  267. # If value is set to ENGINES (ignore)
  268. if [ "${modengines}" != "ENGINES" ]; then
  269. # How many engines we need to test
  270. enginesamount="$(echo "${modengines}" | awk -F ';' '{ print NF }')"
  271. # Test all subvalue of "modengines" using the ";" separator
  272. for ((gamevarindex=1; gamevarindex < ${enginesamount}; gamevarindex++)); do
  273. # Put current engine name into modtest variable
  274. enginemodtest="$( echo "${modengines}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  275. # If engine name matches
  276. if [ "${enginemodtest}" == "${engine}" ]; then
  277. # Mod is compatible!
  278. modcompatibleengine="1"
  279. fi
  280. done
  281. fi
  282. }
  283. # Find out if a game is not compatible with a mod from a modnotgames (list of games not supported by a mod) variable
  284. fn_not_compatible_mod_games(){
  285. # Reset test value
  286. modeincompatiblegame="0"
  287. # If value is set to NOTGAMES (ignore)
  288. if [ "${modexcludegames}" != "NOTGAMES" ]; then
  289. # How many engines we need to test
  290. excludegamesamount="$(echo "${modexcludegames}" | awk -F ';' '{ print NF }')"
  291. # Test all subvalue of "modexcludegames" using the ";" separator
  292. for ((gamevarindex=1; gamevarindex < excludegamesamount; gamevarindex++)); do
  293. # Put current engine name into modtest variable
  294. excludegamemodtest="$( echo "${modexcludegames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  295. # If engine name matches
  296. if [ "${excludegamemodtest}" == "${gamename}" ]; then
  297. # Mod is compatible!
  298. modeincompatiblegame="1"
  299. fi
  300. done
  301. fi
  302. }
  303. # Sums up if a mod is compatible or not with modcompatibility=0/1
  304. fn_mod_compatible_test(){
  305. # Test game and engine compatibility
  306. fn_compatible_mod_games
  307. fn_compatible_mod_engines
  308. fn_not_compatible_mod_games
  309. if [ "${modeincompatiblegame}" == "1" ]; then
  310. modcompatibility="0"
  311. elif [ "${modcompatibleengine}" == "1" ]||[ "${modcompatiblegame}" == "1" ]; then
  312. modcompatibility="1"
  313. else
  314. modcompatibility="0"
  315. fi
  316. }
  317. ## Directory management
  318. # Create mods files and directories if it doesn't exist
  319. fn_create_mods_dir(){
  320. # Create lgsm data modsdir
  321. if [ ! -d "${modsdir}" ]; then
  322. echo -en "creating LinuxGSM mods data directory ${modsdir}..."
  323. mkdir -p "${modsdir}"
  324. exitcode=$?
  325. if [ ${exitcode} -ne 0 ]; then
  326. fn_print_fail_eol_nl
  327. fn_script_log_fatal "Creating mod download dir ${modsdir}"
  328. core_exit.sh
  329. else
  330. fn_print_ok_eol_nl
  331. fn_script_log_pass "Creating mod download dir ${modsdir}"
  332. fi
  333. sleep 0.5
  334. fi
  335. # Create mod install directory
  336. if [ ! -d "${modinstalldir}" ]; then
  337. echo -en "creating mods install directory ${modinstalldir}..."
  338. mkdir -p "${modinstalldir}"
  339. exitcode=$?
  340. if [ ${exitcode} -ne 0 ]; then
  341. fn_print_fail_eol_nl
  342. fn_script_log_fatal "Creating mod install directory ${modinstalldir}"
  343. core_exit.sh
  344. else
  345. fn_print_ok_eol_nl
  346. fn_script_log_pass "Creating mod install directory ${modinstalldir}"
  347. fi
  348. sleep 0.5
  349. fi
  350. # Create lgsm/data/${modsinstalledlist}
  351. if [ ! -f "${modsinstalledlistfullpath}" ]; then
  352. touch "${modsinstalledlistfullpath}"
  353. fn_script_log_info "Created ${modsinstalledlistfullpath}"
  354. fi
  355. }
  356. # Create tmp download mod directory
  357. fn_mods_create_tmp_dir(){
  358. if [ ! -d "${modstmpdir}" ]; then
  359. mkdir -p "${modstmpdir}"
  360. exitcode=$?
  361. echo -en "creating mod download directory ${modstmpdir}..."
  362. if [ ${exitcode} -ne 0 ]; then
  363. fn_print_fail_eol_nl
  364. fn_script_log_fatal "Creating mod download directory ${modstmpdir}"
  365. core_exit.sh
  366. else
  367. fn_print_ok_eol_nl
  368. fn_script_log_pass "Creating mod download directory ${modstmpdir}"
  369. fi
  370. fi
  371. }
  372. # Remove the tmp mod download directory when finished
  373. fn_mods_clear_tmp_dir(){
  374. if [ -d "${modstmpdir}" ]; then
  375. echo -en "clearing mod download directory ${modstmpdir}..."
  376. rm -r "${modstmpdir}"
  377. exitcode=$?
  378. if [ ${exitcode} -ne 0 ]; then
  379. fn_print_fail_eol_nl
  380. fn_script_log_fatal "Clearing mod download directory ${modstmpdir}"
  381. core_exit.sh
  382. else
  383. fn_print_ok_eol_nl
  384. fn_script_log_pass "Clearing mod download directory ${modstmpdir}"
  385. fi
  386. fi
  387. # Clear temp file list as well
  388. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  389. rm "${modsdir}/.removedfiles.tmp"
  390. fi
  391. }
  392. # Counts how many mods were installed
  393. fn_mods_count_installed(){
  394. if [ -f "${modsinstalledlistfullpath}" ]; then
  395. installedmodscount="$(wc -l < "${modsinstalledlistfullpath}")"
  396. else
  397. installedmodscount=0
  398. fi
  399. }
  400. # Exits if no mods were installed
  401. fn_mods_check_installed(){
  402. # Count installed mods
  403. fn_mods_count_installed
  404. # If no mods are found
  405. if [ ${installedmodscount} -eq 0 ]; then
  406. echo ""
  407. fn_print_failure_nl "No installed mods or addons were found"
  408. echo " * Install mods using LinuxGSM first with: ./${selfname} mods-install"
  409. fn_script_log_error "No installed mods or addons were found."
  410. core_exit.sh
  411. fi
  412. }
  413. # Checks that mod files list exists and isn't empty
  414. fn_check_mod_files_list(){
  415. # File list must exist and be valid before any operation on it
  416. if [ -f "${modsdir}/${modcommand}-files.txt" ]; then
  417. # How many lines is the file list
  418. modsfilelistsize="$(wc -l < "${modsdir}/${modcommand}-files.txt")"
  419. # If file list is empty
  420. if [ "${modsfilelistsize}" -eq 0 ]; then
  421. fn_print_failure "${modcommand}-files.txt is empty"
  422. echo "* Unable to remove ${modprettyname}"
  423. fn_script_log_fatal "${modcommand}-files.txt is empty: Unable to remove ${modprettyname}."
  424. core_exit.sh
  425. fi
  426. else
  427. fn_print_failure "${modsdir}/${modcommand}-files.txt does not exist"
  428. fn_script_log_fatal "${modsdir}/${modcommand}-files.txt does not exist: Unable to remove ${modprettyname}."
  429. core_exit.sh
  430. fi
  431. }
  432. ## Database initialisation
  433. mods_list.sh
  434. fn_mods_available