mods_core.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  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="addons/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 -ne "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 -ne "${renamedwc} / ${totalfileswc} / ${fileswc} converting ${modprettyname} files to lowercase..." $'\r'
  48. ((totalfileswc++))
  49. done < <(find "${extractdir}" -depth)
  50. echo -ne "${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 -ne "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 -ne "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 -ne "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. exitcode="1"
  169. core_exit.sh
  170. fi
  171. }
  172. # Define all variables for a mod at once when index is set to a separator
  173. fn_mods_define(){
  174. if [ -z "$index" ]; then
  175. fn_script_log_fatal "index variable not set. Please report an issue."
  176. fn_print_error "index variable not set. Please report an issue."
  177. echo "* https://github.com/GameServerManagers/LinuxGSM/issues"
  178. core_exit.sh
  179. fi
  180. modcommand="${mods_global_array[index+1]}"
  181. modprettyname="${mods_global_array[index+2]}"
  182. modurl="${mods_global_array[index+3]}"
  183. modfilename="${mods_global_array[index+4]}"
  184. modsubdirs="${mods_global_array[index+5]}"
  185. modlowercase="${mods_global_array[index+6]}"
  186. modinstalldir="${mods_global_array[index+7]}"
  187. modkeepfiles="${mods_global_array[index+8]}"
  188. modengines="${mods_global_array[index+9]}"
  189. modgames="${mods_global_array[index+10]}"
  190. modexcludegames="${mods_global_array[index+11]}"
  191. modsite="${mods_global_array[index+12]}"
  192. moddescription="${mods_global_array[index+13]}"
  193. }
  194. # Builds list of installed mods
  195. # using installed-mods.txt grabing mod info from mods_list.sh
  196. fn_mods_installed_list(){
  197. fn_mods_count_installed
  198. # Set/reset variables
  199. installedmodsline="1"
  200. installedmodslist=()
  201. modprettynamemaxlength="0"
  202. modsitemaxlength="0"
  203. moddescriptionmaxlength="0"
  204. modcommandmaxlength="0"
  205. # Loop through every line of the installed mods list ${modsinstalledlistfullpath}
  206. while [ "${installedmodsline}" -le "${installedmodscount}" ]; do
  207. currentmod="$(sed "${installedmodsline}q;d" "${modsinstalledlistfullpath}")"
  208. # Get mod info to make sure mod exists
  209. fn_mod_get_info
  210. # Add the mod to available commands
  211. installedmodslist+=( "${modcommand}" )
  212. # Increment line check
  213. ((installedmodsline++))
  214. done
  215. if [ -n "${installedmodscount}" ]; then
  216. fn_script_log_info "${installedmodscount} addons/mods are currently installed"
  217. fi
  218. }
  219. # Loops through mods_global_array to define available mods & provide available commands for mods installation
  220. fn_mods_available(){
  221. # First, reset variables
  222. compatiblemodslist=()
  223. availablemodscommands=()
  224. # Find compatible games
  225. # Find separators through the global array
  226. for ((index="0"; index <= ${#mods_global_array[@]}; index++)); do
  227. # If current value is a separator; then
  228. if [ "${mods_global_array[index]}" == "${modseparator}" ]; then
  229. # Set mod variables
  230. fn_mods_define
  231. # Test if game is compatible
  232. fn_mod_compatible_test
  233. # If game is compatible
  234. if [ "${modcompatibility}" == "1" ]; then
  235. # Put it into an array to prepare user output
  236. compatiblemodslist+=( "${modprettyname}" "${modcommand}" "${modsite}" "${moddescription}" )
  237. # Keep available commands in an array to make life easier
  238. availablemodscommands+=( "${modcommand}" )
  239. fi
  240. fi
  241. done
  242. }
  243. ## Mod compatibility check
  244. # Find out if a game is compatible with a mod from a modgames (list of games supported by a mod) variable
  245. fn_compatible_mod_games(){
  246. # Reset test value
  247. modcompatiblegame="0"
  248. # If value is set to GAMES (ignore)
  249. if [ "${modgames}" != "GAMES" ]; then
  250. # How many games we need to test
  251. gamesamount="$(echo "${modgames}" | awk -F ';' '{ print NF }')"
  252. # Test all subvalue of "modgames" using the ";" separator
  253. for ((gamevarindex=1; gamevarindex < gamesamount; gamevarindex++)); do
  254. # Put current game name into modtest variable
  255. gamemodtest="$( echo "${modgames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  256. # If game name matches
  257. if [ "${gamemodtest}" == "${gamename}" ]; then
  258. # Mod is compatible !
  259. modcompatiblegame="1"
  260. fi
  261. done
  262. fi
  263. }
  264. # Find out if an engine is compatible with a mod from a modengines (list of engines supported by a mod) variable
  265. fn_compatible_mod_engines(){
  266. # Reset test value
  267. modcompatibleengine="0"
  268. # If value is set to ENGINES (ignore)
  269. if [ "${modengines}" != "ENGINES" ]; then
  270. # How many engines we need to test
  271. enginesamount="$(echo "${modengines}" | awk -F ';' '{ print NF }')"
  272. # Test all subvalue of "modengines" using the ";" separator
  273. for ((gamevarindex=1; gamevarindex < ${enginesamount}; gamevarindex++)); do
  274. # Put current engine name into modtest variable
  275. enginemodtest="$( echo "${modengines}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  276. # If engine name matches
  277. if [ "${enginemodtest}" == "${engine}" ]; then
  278. # Mod is compatible!
  279. modcompatibleengine="1"
  280. fi
  281. done
  282. fi
  283. }
  284. # Find out if a game is not compatible with a mod from a modnotgames (list of games not supported by a mod) variable
  285. fn_not_compatible_mod_games(){
  286. # Reset test value
  287. modeincompatiblegame="0"
  288. # If value is set to NOTGAMES (ignore)
  289. if [ "${modexcludegames}" != "NOTGAMES" ]; then
  290. # How many engines we need to test
  291. excludegamesamount="$(echo "${modexcludegames}" | awk -F ';' '{ print NF }')"
  292. # Test all subvalue of "modexcludegames" using the ";" separator
  293. for ((gamevarindex=1; gamevarindex < excludegamesamount; gamevarindex++)); do
  294. # Put current engine name into modtest variable
  295. excludegamemodtest="$( echo "${modexcludegames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  296. # If engine name matches
  297. if [ "${excludegamemodtest}" == "${gamename}" ]; then
  298. # Mod is compatible!
  299. modeincompatiblegame="1"
  300. fi
  301. done
  302. fi
  303. }
  304. # Sums up if a mod is compatible or not with modcompatibility=0/1
  305. fn_mod_compatible_test(){
  306. # Test game and engine compatibility
  307. fn_compatible_mod_games
  308. fn_compatible_mod_engines
  309. fn_not_compatible_mod_games
  310. if [ "${modeincompatiblegame}" == "1" ]; then
  311. modcompatibility="0"
  312. elif [ "${modcompatibleengine}" == "1" ]||[ "${modcompatiblegame}" == "1" ]; then
  313. modcompatibility="1"
  314. else
  315. modcompatibility="0"
  316. fi
  317. }
  318. ## Directory management
  319. # Create mods files and directories if it doesn't exist
  320. fn_create_mods_dir(){
  321. # Create lgsm data modsdir
  322. if [ ! -d "${modsdir}" ]; then
  323. echo -en "creating LinuxGSM mods data directory ${modsdir}..."
  324. mkdir -p "${modsdir}"
  325. exitcode=$?
  326. if [ ${exitcode} -ne 0 ]; then
  327. fn_print_fail_eol_nl
  328. fn_script_log_fatal "Creating mod download dir ${modsdir}"
  329. core_exit.sh
  330. else
  331. fn_print_ok_eol_nl
  332. fn_script_log_pass "Creating mod download dir ${modsdir}"
  333. fi
  334. sleep 0.5
  335. fi
  336. # Create mod install directory
  337. if [ ! -d "${modinstalldir}" ]; then
  338. echo -en "creating mods install directory ${modinstalldir}..."
  339. mkdir -p "${modinstalldir}"
  340. exitcode=$?
  341. if [ ${exitcode} -ne 0 ]; then
  342. fn_print_fail_eol_nl
  343. fn_script_log_fatal "Creating mod install directory ${modinstalldir}"
  344. core_exit.sh
  345. else
  346. fn_print_ok_eol_nl
  347. fn_script_log_pass "Creating mod install directory ${modinstalldir}"
  348. fi
  349. sleep 0.5
  350. fi
  351. # Create lgsm/data/${modsinstalledlist}
  352. if [ ! -f "${modsinstalledlistfullpath}" ]; then
  353. touch "${modsinstalledlistfullpath}"
  354. fn_script_log_info "Created ${modsinstalledlistfullpath}"
  355. fi
  356. }
  357. # Create tmp download mod directory
  358. fn_mods_create_tmp_dir(){
  359. if [ ! -d "${modstmpdir}" ]; then
  360. mkdir -p "${modstmpdir}"
  361. exitcode=$?
  362. echo -ne "creating mod download directory ${modstmpdir}..."
  363. if [ ${exitcode} -ne 0 ]; then
  364. fn_print_fail_eol_nl
  365. fn_script_log_fatal "Creating mod download directory ${modstmpdir}"
  366. core_exit.sh
  367. else
  368. fn_print_ok_eol_nl
  369. fn_script_log_pass "Creating mod download directory ${modstmpdir}"
  370. fi
  371. fi
  372. }
  373. # Remove the tmp mod download directory when finished
  374. fn_mods_clear_tmp_dir(){
  375. if [ -d "${modstmpdir}" ]; then
  376. echo -ne "clearing mod download directory ${modstmpdir}..."
  377. rm -r "${modstmpdir}"
  378. exitcode=$?
  379. if [ ${exitcode} -ne 0 ]; then
  380. fn_print_fail_eol_nl
  381. fn_script_log_fatal "Clearing mod download directory ${modstmpdir}"
  382. core_exit.sh
  383. else
  384. fn_print_ok_eol_nl
  385. fn_script_log_pass "Clearing mod download directory ${modstmpdir}"
  386. fi
  387. fi
  388. # Clear temp file list as well
  389. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  390. rm "${modsdir}/.removedfiles.tmp"
  391. fi
  392. }
  393. # Counts how many mods were installed
  394. fn_mods_count_installed(){
  395. if [ -f "${modsinstalledlistfullpath}" ]; then
  396. installedmodscount="$(wc -l < "${modsinstalledlistfullpath}")"
  397. else
  398. installedmodscount=0
  399. fi
  400. }
  401. # Exits if no mods were installed
  402. fn_mods_check_installed(){
  403. # Count installed mods
  404. fn_mods_count_installed
  405. # If no mods are found
  406. if [ ${installedmodscount} -eq 0 ]; then
  407. echo ""
  408. fn_print_failure_nl "No installed mods or addons were found"
  409. echo " * Install mods using LinuxGSM first with: ./${selfname} mods-install"
  410. fn_script_log_error "No installed mods or addons were found."
  411. core_exit.sh
  412. fi
  413. }
  414. # Checks that mod files list exists and isn't empty
  415. fn_check_mod_files_list(){
  416. # File list must exist and be valid before any operation on it
  417. if [ -f "${modsdir}/${modcommand}-files.txt" ]; then
  418. # How many lines is the file list
  419. modsfilelistsize="$(wc -l < "${modsdir}/${modcommand}-files.txt")"
  420. # If file list is empty
  421. if [ "${modsfilelistsize}" -eq 0 ]; then
  422. fn_print_failure "${modcommand}-files.txt is empty"
  423. echo "* Unable to remove ${modprettyname}"
  424. fn_script_log_fatal "${modcommand}-files.txt is empty: Unable to remove ${modprettyname}."
  425. core_exit.sh
  426. fi
  427. else
  428. fn_print_failure "${modsdir}/${modcommand}-files.txt does not exist"
  429. fn_script_log_fatal "${modsdir}/${modcommand}-files.txt does not exist: Unable to remove ${modprettyname}."
  430. core_exit.sh
  431. fi
  432. }
  433. ## Database initialisation
  434. mods_list.sh
  435. fn_mods_available