mods_core.sh 14 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. fn_mod_tidy_files_list(){
  103. # Prevent sensitive directories from being erased by removing them from: ${modcommand}-files.txt
  104. # Check file 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 matching line(s)
  120. sed -i "/^${removefilevar}$/d" "${modsdir}/${modcommand}-files.txt"
  121. local exitcode=$?
  122. if [ ${exitcode} -ne 0 ]; then
  123. break
  124. fi
  125. done
  126. if [ ${exitcode} -ne 0 ]; then
  127. fn_print_fail_eol_nl
  128. else
  129. fn_print_ok_eol_nl
  130. fi
  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_print_error "index variable not set. Please report an issue."
  177. echo "* https://github.com/GameServerManagers/LinuxGSM/issues"
  178. exitcode="1"
  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. # Checks if a mod is compatible for installation
  221. # Provides available mods for installation
  222. # Provides commands for mods installation
  223. fn_mods_available(){
  224. # First, reset variables
  225. compatiblemodslist=()
  226. availablemodscommands=()
  227. # Find compatible games
  228. # Find separators through the global array
  229. for ((index="0"; index <= ${#mods_global_array[@]}; index++)); do
  230. # If current value is a separator; then
  231. if [ "${mods_global_array[index]}" == "${modseparator}" ]; then
  232. # Set mod variables
  233. fn_mods_define
  234. # Test if game is compatible
  235. fn_mod_compatible_test
  236. # If game is compatible
  237. if [ "${modcompatibility}" == "1" ]; then
  238. # Put it into an array to prepare user output
  239. compatiblemodslist+=( "${modprettyname}" "${modcommand}" "${modsite}" "${moddescription}" )
  240. # Keep available commands in an array to make life easier
  241. availablemodscommands+=( "${modcommand}" )
  242. fi
  243. fi
  244. done
  245. }
  246. ## Mod compatibility check
  247. # Find out if a game is compatible with a mod from a modgames (list of games supported by a mod) variable
  248. fn_compatible_mod_games(){
  249. # Reset test value
  250. modcompatiblegame="0"
  251. # If value is set to GAMES (ignore)
  252. if [ "${modgames}" != "GAMES" ]; then
  253. # How many games we need to test
  254. gamesamount="$(echo "${modgames}" | awk -F ';' '{ print NF }')"
  255. # Test all subvalue of "modgames" using the ";" separator
  256. for ((gamevarindex=1; gamevarindex < gamesamount; gamevarindex++)); do
  257. # Put current game name into modtest variable
  258. gamemodtest="$( echo "${modgames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  259. # If game name matches
  260. if [ "${gamemodtest}" == "${gamename}" ]; then
  261. # Mod is compatible !
  262. modcompatiblegame="1"
  263. fi
  264. done
  265. fi
  266. }
  267. # Find out if an engine is compatible with a mod from a modengines (list of engines supported by a mod) variable
  268. fn_compatible_mod_engines(){
  269. # Reset test value
  270. modcompatibleengine="0"
  271. # If value is set to ENGINES (ignore)
  272. if [ "${modengines}" != "ENGINES" ]; then
  273. # How many engines we need to test
  274. enginesamount="$(echo "${modengines}" | awk -F ';' '{ print NF }')"
  275. # Test all subvalue of "modengines" using the ";" separator
  276. for ((gamevarindex=1; gamevarindex < ${enginesamount}; gamevarindex++)); do
  277. # Put current engine name into modtest variable
  278. enginemodtest="$( echo "${modengines}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  279. # If engine name matches
  280. if [ "${enginemodtest}" == "${engine}" ]; then
  281. # Mod is compatible !
  282. modcompatibleengine="1"
  283. fi
  284. done
  285. fi
  286. }
  287. # Find out if a game is not compatible with a mod from a modnotgames (list of games not supported by a mod) variable
  288. fn_not_compatible_mod_games(){
  289. # Reset test value
  290. modeincompatiblegame="0"
  291. # If value is set to NOTGAMES (ignore)
  292. if [ "${modexcludegames}" != "NOTGAMES" ]; then
  293. # How many engines we need to test
  294. excludegamesamount="$(echo "${modexcludegames}" | awk -F ';' '{ print NF }')"
  295. # Test all subvalue of "modexcludegames" using the ";" separator
  296. for ((gamevarindex=1; gamevarindex < excludegamesamount; gamevarindex++)); do
  297. # Put current engine name into modtest variable
  298. excludegamemodtest="$( echo "${modexcludegames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }' )"
  299. # If engine name matches
  300. if [ "${excludegamemodtest}" == "${gamename}" ]; then
  301. # Mod is compatible !
  302. modeincompatiblegame="1"
  303. fi
  304. done
  305. fi
  306. }
  307. # Sums up if a mod is compatible or not with modcompatibility=0/1
  308. fn_mod_compatible_test(){
  309. # Test game and engine compatibility
  310. fn_compatible_mod_games
  311. fn_compatible_mod_engines
  312. fn_not_compatible_mod_games
  313. if [ "${modeincompatiblegame}" == "1" ]; then
  314. modcompatibility="0"
  315. elif [ "${modcompatibleengine}" == "1" ]||[ "${modcompatiblegame}" == "1" ]; then
  316. modcompatibility="1"
  317. else
  318. modcompatibility="0"
  319. fi
  320. }
  321. ## Directory management
  322. # Create mods files and directories if it doesn't exist
  323. fn_create_mods_dir(){
  324. # Create mod install directory
  325. if [ ! -d "${modinstalldir}" ]; then
  326. echo "creating mods install directory ${modinstalldir}..."
  327. mkdir -p "${modinstalldir}"
  328. exitcode=$?
  329. if [ ${exitcode} -ne 0 ]; then
  330. fn_print_fail_eol_nl
  331. fn_script_log_fatal "Creating mod download dir ${modinstalldir}"
  332. core_exit.sh
  333. else
  334. fn_print_ok_eol_nl
  335. fn_script_log_pass "Creating mod download dir ${modinstalldir}"
  336. fi
  337. sleep 0.5
  338. fi
  339. # Create lgsm/data/${modsinstalledlist}
  340. if [ ! -f "${modsinstalledlistfullpath}" ]; then
  341. touch "${modsinstalledlistfullpath}"
  342. fn_script_log_info "Created ${modsinstalledlistfullpath}"
  343. fi
  344. }
  345. # Create tmp download mod directory
  346. fn_mods_create_tmp_dir(){
  347. if [ ! -d "${modstmpdir}" ]; then
  348. mkdir -p "${modstmpdir}"
  349. exitcode=$?
  350. echo -ne "creating mod download directory ${modstmpdir}..."
  351. if [ ${exitcode} -ne 0 ]; then
  352. fn_print_fail_eol_nl
  353. fn_script_log_fatal "Creating mod download directory ${modstmpdir}"
  354. core_exit.sh
  355. else
  356. fn_print_ok_eol_nl
  357. fn_script_log_pass "Creating mod download directory ${modstmpdir}"
  358. fi
  359. fi
  360. }
  361. # Remove the tmp mod download directory when finished
  362. fn_mods_clear_tmp_dir(){
  363. if [ -d "${modstmpdir}" ]; then
  364. echo -ne "clearing mod download directory ${modstmpdir}..."
  365. rm -r "${modstmpdir}"
  366. exitcode=$?
  367. if [ ${exitcode} -ne 0 ]; then
  368. fn_print_fail_eol_nl
  369. fn_script_log_fatal "Clearing mod download directory ${modstmpdir}"
  370. core_exit.sh
  371. else
  372. fn_print_ok_eol_nl
  373. fn_script_log_pass "Clearing mod download directory ${modstmpdir}"
  374. fi
  375. fi
  376. # Clear temp file list as well
  377. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  378. rm "${modsdir}/.removedfiles.tmp"
  379. fi
  380. }
  381. fn_mods_count_installed(){
  382. if [ -f "${modsinstalledlistfullpath}" ]; then
  383. installedmodscount="$(wc -l < "${modsinstalledlistfullpath}")"
  384. else
  385. installedmodscount=0
  386. fi
  387. }
  388. # Exit if no mods were installed
  389. fn_mods_check_installed(){
  390. # Count installed mods
  391. fn_mods_count_installed
  392. # If no mods are found
  393. if [ ${installedmodscount} -eq 0 ]; then
  394. fn_print_information_nl "No installed mods or addons were found"
  395. echo " * Install mods using LGSM first with: ./${selfname} mods-install"
  396. fn_script_log_info "No installed mods or addons were found."
  397. core_exit.sh
  398. fi
  399. }
  400. fn_check_mod_files_list(){
  401. # File list must exist and be valid before any operation on it
  402. if [ -f "${modsdir}/${modcommand}-files.txt" ]; then
  403. # How many lines is the file list
  404. modsfilelistsize="$(wc -l < "${modsdir}/${modcommand}-files.txt")"
  405. # If file list is empty
  406. if [ "${modsfilelistsize}" -eq 0 ]; then
  407. fn_print_failure "${modcommand}-files.txt is empty"
  408. echo "* Unable to remove ${modprettyname}"
  409. fn_script_log_fatal "${modcommand}-files.txt is empty: Unable to remove ${modprettyname}."
  410. core_exit.sh
  411. fi
  412. else
  413. fn_print_failure "${modsdir}/${modcommand}-files.txt does not exist"
  414. fn_script_log_fatal "${modsdir}/${modcommand}-files.txt does not exist: Unable to remove ${modprettyname}."
  415. core_exit.sh
  416. fi
  417. }
  418. # Database initialisation
  419. mods_list.sh
  420. fn_mods_available