mods_core.sh 15 KB

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