mods_core.sh 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. #!/bin/bash
  2. # LinuxGSM command_mods_install.sh module
  3. # Author: Daniel Gibbs
  4. # Contributors: https://linuxgsm.com/contrib
  5. # Website: https://linuxgsm.com
  6. # Description: Core modules for mods list/install/update/remove
  7. moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
  8. # Files and Directories.
  9. modsdir="${lgsmdir}/mods"
  10. modstmpdir="${modsdir}/tmp"
  11. extractdest="${modstmpdir}/extract"
  12. modsinstalledlist="installed-mods.txt"
  13. modsinstalledlistfullpath="${modsdir}/${modsinstalledlist}"
  14. ## Installation.
  15. # Download management.
  16. fn_mod_install_files() {
  17. fn_fetch_file "${modurl}" "" "" "" "${modstmpdir}" "${modfilename}"
  18. # Check if variable is valid checking if file has been downloaded and exists.
  19. if [ ! -f "${modstmpdir}/${modfilename}" ]; then
  20. fn_print_failure "An issue occurred downloading ${modprettyname}"
  21. fn_script_log_fail "An issue occurred downloading ${modprettyname}"
  22. core_exit.sh
  23. fi
  24. if [ ! -d "${extractdest}" ]; then
  25. mkdir -p "${extractdest}"
  26. fi
  27. fn_dl_extract "${modstmpdir}" "${modfilename}" "${extractdest}"
  28. # If modsubdirs names a specific subfolder (or path to one), use its contents as the install root.
  29. if [ "${modsubdirs}" != "0" ] && [ -d "${extractdest}/${modsubdirs}" ]; then
  30. local tmpsubdir
  31. tmpsubdir=$(mktemp -d)
  32. mv "${extractdest}/${modsubdirs}" "${tmpsubdir}/"
  33. rm -rf "${extractdest}"
  34. mv "${tmpsubdir}/$(basename "${modsubdirs}")" "${extractdest}"
  35. rm -rf "${tmpsubdir}"
  36. fi
  37. }
  38. # Compile a SourceMod plugin that is only distributed as source, using the installed SourceMod compiler.
  39. # The compiled plugin is added to the extracted mod files so it is installed, updated and removed with the mod.
  40. fn_mod_compile_sourcemod_plugin() {
  41. local pluginname="${1}"
  42. local spcompdir="${modinstalldir}/addons/sourcemod/scripting"
  43. # Prefer the 64-bit compiler; the 32-bit spcomp needs i386 libraries.
  44. local spcomp="${spcompdir}/spcomp64"
  45. if [ ! -x "${spcomp}" ]; then
  46. spcomp="${spcompdir}/spcomp"
  47. fi
  48. echo -en "compiling ${pluginname}.sp..."
  49. fn_sleep_time
  50. if [ ! -x "${spcomp}" ]; then
  51. fn_print_fail_eol_nl
  52. fn_script_log_fail "Compiling ${pluginname}.sp: SourceMod compiler not found in ${spcompdir}"
  53. echo -e "* SourceMod compiler not found in ${spcompdir}"
  54. exitcode=1
  55. core_exit.sh
  56. fi
  57. mkdir -p "${extractdest}/addons/sourcemod/plugins"
  58. spcompoutput=$("${spcomp}" -i"${spcompdir}/include" "${extractdest}/addons/sourcemod/scripting/${pluginname}.sp" -o"${extractdest}/addons/sourcemod/plugins/${pluginname}.smx" 2>&1)
  59. exitcode=$?
  60. if [ "${exitcode}" -ne 0 ] || [ ! -s "${extractdest}/addons/sourcemod/plugins/${pluginname}.smx" ]; then
  61. fn_print_fail_eol_nl
  62. fn_script_log_fail "Compiling ${pluginname}.sp"
  63. if [ -f "${lgsmlog}" ]; then
  64. echo -e "${spcompoutput}" >> "${lgsmlog}"
  65. fi
  66. echo -e "${spcompoutput}"
  67. exitcode=1
  68. core_exit.sh
  69. else
  70. fn_print_ok_eol_nl
  71. fn_script_log_pass "Compiling ${pluginname}.sp"
  72. fi
  73. }
  74. # Convert mod files to lowercase if needed.
  75. fn_mod_lowercase() {
  76. # Checking lowercase settings from mods array definition
  77. if [ "${modlowercase}" == "LowercaseOn" ]; then
  78. echo -en "converting ${modprettyname} files to lowercase..."
  79. fn_sleep_time
  80. fn_script_log_info "Converting ${modprettyname} files to lowercase"
  81. # Total files and directories for the mod, to output to the user
  82. fileswc=$(find "${extractdest}" | wc -l)
  83. # Total uppercase files and directories for the mod, to output to the user
  84. filesupperwc=$(find "${extractdest}" -name '*[[:upper:]]*' | wc -l)
  85. fn_script_log_info "Found ${filesupperwc} uppercase files out of ${fileswc}, converting"
  86. echo -en "Found ${filesupperwc} uppercase files out of ${fileswc}, converting..."
  87. # Convert files and directories starting from the deepest to prevent issues (-depth argument)
  88. while read -r src; do
  89. # We have to convert only the last file from the path, otherwise we will fail to convert anything if a parent dir has any uppercase
  90. # therefore, we have to separate the end of the filename to only lowercase it rather than the whole line
  91. # Gather parent dir, filename lowercase filename, and set lowercase destination name
  92. latestparentdir=$(dirname "${src}")
  93. latestfilelc=$(basename "${src}" | tr '[:upper:]' '[:lower:]')
  94. dst="${latestparentdir}/${latestfilelc}"
  95. # Only convert if destination does not already exist for some reason
  96. if [ ! -e "${dst}" ]; then
  97. # Finally we can rename the file
  98. mv "${src}" "${dst}"
  99. # Exit if it fails for any reason
  100. exitcode=$?
  101. if [ "${exitcode}" -ne 0 ]; then
  102. fn_print_fail_eol_nl
  103. core_exit.sh
  104. fi
  105. fi
  106. done < <(find "${extractdest}" -depth -name '*[[:upper:]]*')
  107. fn_print_ok_eol_nl
  108. fi
  109. }
  110. # Create ${modcommand}-files.txt containing the full extracted file/directory list.
  111. fn_mod_create_filelist() {
  112. echo -en "building ${modcommand}-files.txt..."
  113. fn_sleep_time
  114. # ${modsdir}/${modcommand}-files.txt.
  115. find "${extractdest}" -mindepth 1 -printf '%P\n' > "${modsdir}/${modcommand}-files.txt"
  116. exitcode=$?
  117. if [ "${exitcode}" -ne 0 ]; then
  118. fn_print_fail_eol_nl
  119. fn_script_log_fail "Building ${modsdir}/${modcommand}-files.txt"
  120. core_exit.sh
  121. else
  122. fn_print_ok_eol_nl
  123. fn_script_log_pass "Building ${modsdir}/${modcommand}-files.txt"
  124. fi
  125. # Adding removed files if needed.
  126. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  127. cat "${modsdir}/.removedfiles.tmp" >> "${modsdir}/${modcommand}-files.txt"
  128. fi
  129. }
  130. # Copy the mod into serverfiles.
  131. fn_mod_copy_destination() {
  132. echo -en "copying ${modprettyname} to ${modinstalldir}..."
  133. fn_sleep_time
  134. cp -Rf "${extractdest}/." "${modinstalldir}/"
  135. exitcode=$?
  136. if [ "${exitcode}" -ne 0 ]; then
  137. fn_print_fail_eol_nl
  138. fn_script_log_fail "Copying ${modprettyname} to ${modinstalldir}"
  139. else
  140. fn_print_ok_eol_nl
  141. fn_script_log_pass "Copying ${modprettyname} to ${modinstalldir}"
  142. fi
  143. }
  144. # Add the mod to the installed-mods.txt.
  145. fn_mod_add_list() {
  146. if [ -z "$(sed -n "/^${modcommand}$/p" "${modsinstalledlistfullpath}")" ]; then
  147. echo -e "${modcommand}" >> "${modsinstalledlistfullpath}"
  148. fn_script_log_info "${modcommand} added to ${modsinstalledlist}"
  149. fi
  150. }
  151. # Prevent sensitive directories from being erased upon uninstall by removing them from: ${modcommand}-files.txt.
  152. fn_mod_tidy_files_list() {
  153. # Check file list validity.
  154. fn_check_mod_files_list
  155. # Output to the user
  156. echo -en "tidy up ${modcommand}-files.txt..."
  157. fn_sleep_time
  158. fn_script_log_info "Tidy up ${modcommand}-files.txt"
  159. # Lines/files to remove from file list (end with ";" separator).
  160. removefromlist="cfg;addons;RustDedicated_Data;RustDedicated_Data\/Managed;RustDedicated_Data\/Managed\/x86;RustDedicated_Data\/Managed\/x64;"
  161. # Loop through files to remove from file list,
  162. # generate elements to remove from list.
  163. removefromlistamount=$(echo -e "${removefromlist}" | awk -F ';' '{ print NF }')
  164. # Test all subvalue of "removefromlist" using the ";" separator.
  165. for ((filesindex = 1; filesindex < removefromlistamount; filesindex++)); do
  166. # Put current file into test variable.
  167. removefilevar=$(echo -e "${removefromlist}" | awk -F ';' -v x=${filesindex} '{ print $x }')
  168. # Delete line(s) matching exactly.
  169. sed -i "/^${removefilevar}$/d" "${modsdir}/${modcommand}-files.txt"
  170. # Exit on error.
  171. exitcode=$?
  172. if [ "${exitcode}" -ne 0 ]; then
  173. fn_print_fail_eol_nl
  174. fn_script_log_fail "Error while tidying line: ${removefilevar} from: ${modsdir}/${modcommand}-files.txt"
  175. core_exit.sh
  176. break
  177. fi
  178. done
  179. fn_print_ok_eol_nl
  180. # Sourcemod fix
  181. # Remove metamod from sourcemod fileslist.
  182. if [ "${modcommand}" == "sourcemod" ]; then
  183. # Remove addons/metamod & addons/metamod/sourcemod.vdf from ${modcommand}-files.txt.
  184. sed -i "/^addons\/metamod$/d" "${modsdir}/${modcommand}-files.txt"
  185. sed -i "/^addons\/metamod\/sourcemod.vdf$/d" "${modsdir}/${modcommand}-files.txt"
  186. fi
  187. # Keep Metamod:Source when removing CounterStrikeSharp, but remove its own VDF.
  188. if [ "${modcommand}" == "counterstrikesharp" ]; then
  189. sed -i "/^addons\/metamod$/d" "${modsdir}/${modcommand}-files.txt"
  190. fi
  191. # Remove common CounterStrikeSharp paths from deletion list (Add your CounterStrikeSharp plugin here)
  192. if [ "${modcommand}" == "matchzy" ]; then
  193. sed -i "/^addons\/counterstrikesharp$/d" "${modsdir}/${modcommand}-files.txt"
  194. sed -i "/^addons\/counterstrikesharp\/gamedata$/d" "${modsdir}/${modcommand}-files.txt"
  195. sed -i "/^addons\/counterstrikesharp\/plugins$/d" "${modsdir}/${modcommand}-files.txt"
  196. fi
  197. # Remove common paths from deletion list (Add your sourcemod mod here)
  198. if [ "${modcommand}" == "gokz" ] || [ "${modcommand}" == "ttt" ] || [ "${modcommand}" == "steamworks" ] || [ "${modcommand}" == "get5" ] || [ "${modcommand}" == "nolobbyreservation" ]; then
  199. sed -i "/^addons\/sourcemod$/d" "${modsdir}/${modcommand}-files.txt"
  200. sed -i "/^addons\/sourcemod\/configs$/d" "${modsdir}/${modcommand}-files.txt"
  201. sed -i "/^addons\/sourcemod\/extensions$/d" "${modsdir}/${modcommand}-files.txt"
  202. sed -i "/^addons\/sourcemod\/logs$/d" "${modsdir}/${modcommand}-files.txt"
  203. sed -i "/^addons\/sourcemod\/plugins$/d" "${modsdir}/${modcommand}-files.txt"
  204. sed -i "/^addons\/sourcemod\/plugins\/disabled$/d" "${modsdir}/${modcommand}-files.txt"
  205. sed -i "/^addons\/sourcemod\/scripting$/d" "${modsdir}/${modcommand}-files.txt"
  206. sed -i "/^addons\/sourcemod\/scripting\/include$/d" "${modsdir}/${modcommand}-files.txt"
  207. sed -i "/^addons\/sourcemod\/translations$/d" "${modsdir}/${modcommand}-files.txt"
  208. # Don't delete directories of translations like 'fr', 'sv', 'de', etc
  209. sed -i "/^addons\/sourcemod\/translations\/[A-Za-z0-9_]*$/d" "${modsdir}/${modcommand}-files.txt"
  210. sed -i "/^cfg\/sourcemod$/d" "${modsdir}/${modcommand}-files.txt"
  211. sed -i "/^maps$/d" "${modsdir}/${modcommand}-files.txt"
  212. sed -i "/^materialss$/d" "${modsdir}/${modcommand}-files.txt"
  213. sed -i "/^materials\/models$/d" "${modsdir}/${modcommand}-files.txt"
  214. sed -i "/^materials\/models\/weapons$/d" "${modsdir}/${modcommand}-files.txt"
  215. sed -i "/^materials\/darkness$/d" "${modsdir}/${modcommand}-files.txt"
  216. sed -i "/^materials\/decals$/d" "${modsdir}/${modcommand}-files.txt"
  217. sed -i "/^materials\/overlays$/d" "${modsdir}/${modcommand}-files.txt"
  218. sed -i "/^models$/d" "${modsdir}/${modcommand}-files.txt"
  219. sed -i "/^models\/weapons$/d" "${modsdir}/${modcommand}-files.txt"
  220. sed -i "/^sound$/d" "${modsdir}/${modcommand}-files.txt"
  221. sed -i "/^sound\/weapons$/d" "${modsdir}/${modcommand}-files.txt"
  222. fi
  223. # Remove paths of specific mods from deletion list
  224. if [ "${modcommand}" == "gokz" ]; then
  225. sed -i "/^addons\/sourcemod\/scripting\/include\/smjansson.inc$/d" "${modsdir}/${modcommand}-files.txt"
  226. sed -i "/^addons\/sourcemod\/scripting\/include\/GlobalAPI-Core.inc$/d" "${modsdir}/${modcommand}-files.txt"
  227. sed -i "/^addons\/sourcemod\/scripting\/include\/sourcebanspp.inc$/d" "${modsdir}/${modcommand}-files.txt"
  228. sed -i "/^addons\/sourcemod\/scripting\/include\/autoexecconfig.inc$/d" "${modsdir}/${modcommand}-files.txt"
  229. sed -i "/^addons\/sourcemod\/scripting\/include\/colorvariables.inc$/d" "${modsdir}/${modcommand}-files.txt"
  230. sed -i "/^addons\/sourcemod\/scripting\/include\/movementapi.inc$/d" "${modsdir}/${modcommand}-files.txt"
  231. sed -i "/^addons\/sourcemod\/scripting\/include\/movement.inc$/d" "${modsdir}/${modcommand}-files.txt"
  232. sed -i "/^addons\/sourcemod\/scripting\/include\/dhooks.inc$/d" "${modsdir}/${modcommand}-files.txt"
  233. sed -i "/^addons\/sourcemod\/scripting\/include\/updater.inc$/d" "${modsdir}/${modcommand}-files.txt"
  234. fi
  235. if [ "${modcommand}" == "nolobbyreservation" ]; then
  236. sed -i "/^addons\/sourcemod\/gamedata$/d" "${modsdir}/${modcommand}-files.txt"
  237. fi
  238. }
  239. ## Information Gathering.
  240. # Get details of a mod any (relevant and unique, such as full mod name or install command) value.
  241. fn_mod_get_info() {
  242. # Variable to know when job is done.
  243. modinfocommand="0"
  244. # Find entry in global array.
  245. for ((index = 0; index < ${#mods_global_array[@]}; index++)); do
  246. # When entry is found.
  247. if [ "${mods_global_array[index]}" == "${currentmod}" ]; then
  248. # Go back to the previous "MOD" separator.
  249. for ((index_search = index; index_search >= 0; index_search--)); do
  250. # When "MOD" is found.
  251. if [ "${mods_global_array[index_search]}" == "MOD" ]; then
  252. # Get info.
  253. index=${index_search}
  254. fn_mods_define
  255. modinfocommand="1"
  256. break
  257. fi
  258. done
  259. fi
  260. # Exit the loop if job is done.
  261. if [ "${modinfocommand}" == "1" ]; then
  262. break
  263. fi
  264. done
  265. # What happens if mod is not found.
  266. if [ "${modinfocommand}" == "0" ]; then
  267. fn_script_log_error "Could not find information for ${currentmod}"
  268. fn_print_error_nl "Could not find information for ${currentmod}"
  269. core_exit.sh
  270. fi
  271. }
  272. # Define all variables for a mod at once when index is set to a separator.
  273. fn_mods_define() {
  274. if [ -z "$index" ]; then
  275. fn_script_log_fail "index variable not set. Please report an issue."
  276. fn_print_error "index variable not set. Please report an issue."
  277. echo -e "* https://github.com/GameServerManagers/LinuxGSM/issues"
  278. core_exit.sh
  279. fi
  280. modcommand="${mods_global_array[index + 1]}"
  281. modprettyname="${mods_global_array[index + 2]}"
  282. modurl="${mods_global_array[index + 3]}"
  283. modfilename="${mods_global_array[index + 4]}"
  284. modsubdirs="${mods_global_array[index + 5]}"
  285. modlowercase="${mods_global_array[index + 6]}"
  286. modinstalldir="${mods_global_array[index + 7]}"
  287. modkeepfiles="${mods_global_array[index + 8]}"
  288. modengines="${mods_global_array[index + 9]}"
  289. modgames="${mods_global_array[index + 10]}"
  290. modexcludegames="${mods_global_array[index + 11]}"
  291. modsite="${mods_global_array[index + 12]}"
  292. moddescription="${mods_global_array[index + 13]}"
  293. }
  294. # Builds list of installed mods.
  295. # using installed-mods.txt grabbing mod info from mods_list.sh.
  296. fn_mods_installed_list() {
  297. fn_mods_count_installed
  298. # Set/reset variables.
  299. installedmodsline="1"
  300. installedmodslist=()
  301. modprettynamemaxlength="0"
  302. modsitemaxlength="0"
  303. moddescriptionmaxlength="0"
  304. modcommandmaxlength="0"
  305. # Loop through every line of the installed mods list ${modsinstalledlistfullpath}.
  306. while [ "${installedmodsline}" -le "${installedmodscount}" ]; do
  307. currentmod=$(sed "${installedmodsline}q;d" "${modsinstalledlistfullpath}")
  308. # Get mod info to make sure mod exists.
  309. fn_mod_get_info
  310. # Add the mod to available commands.
  311. installedmodslist+=("${modcommand}")
  312. # Increment line check.
  313. ((installedmodsline++))
  314. done
  315. if [ "${installedmodscount}" ]; then
  316. fn_script_log_info "${installedmodscount} addons/mods are currently installed"
  317. fi
  318. }
  319. # Loops through mods_global_array to define available mods & provide available commands for mods installation.
  320. fn_mods_available() {
  321. # First, reset variables.
  322. compatiblemodslist=()
  323. availablemodscommands=()
  324. # Find compatible games.
  325. # Find separators through the global array.
  326. for ((index = "0"; index <= ${#mods_global_array[@]}; index++)); do
  327. # If current value is a separator; then.
  328. if [ "${mods_global_array[index]}" == "${modseparator}" ]; then
  329. # Set mod variables.
  330. fn_mods_define
  331. # Test if game is compatible.
  332. fn_mod_compatible_test
  333. # If game is compatible.
  334. if [ "${modcompatibility}" == "1" ]; then
  335. # Put it into an array to prepare user output.
  336. compatiblemodslist+=("${modprettyname}" "${modcommand}" "${modsite}" "${moddescription}")
  337. # Keep available commands in an array to make life easier.
  338. availablemodscommands+=("${modcommand}")
  339. fi
  340. fi
  341. done
  342. }
  343. ## Mod compatibility check.
  344. # Find out if a game is compatible with a mod from a modgames (list of games supported by a mod) variable.
  345. fn_compatible_mod_games() {
  346. # Reset test value.
  347. modcompatiblegame="0"
  348. # If value is set to GAMES (ignore).
  349. if [ "${modgames}" != "GAMES" ]; then
  350. # How many games we need to test.
  351. gamesamount=$(echo -e "${modgames}" | awk -F ';' '{ print NF }')
  352. # Test all subvalue of "modgames" using the ";" separator.
  353. for ((gamevarindex = 1; gamevarindex < gamesamount; gamevarindex++)); do
  354. # Put current game name into modtest variable.
  355. gamemodtest=$(echo -e "${modgames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }')
  356. # If game name matches.
  357. if [ "${gamemodtest}" == "${gamename}" ]; then
  358. # Mod is compatible.
  359. modcompatiblegame="1"
  360. fi
  361. done
  362. fi
  363. }
  364. # Find out if an engine is compatible with a mod from a modengines (list of engines supported by a mod) variable.
  365. fn_compatible_mod_engines() {
  366. # Reset test value.
  367. modcompatibleengine="0"
  368. # If value is set to ENGINES (ignore).
  369. if [ "${modengines}" != "ENGINES" ]; then
  370. # How many engines we need to test.
  371. enginesamount=$(echo -e "${modengines}" | awk -F ';' '{ print NF }')
  372. # Test all subvalue of "modengines" using the ";" separator.
  373. for ((gamevarindex = 1; gamevarindex < enginesamount; gamevarindex++)); do
  374. # Put current engine name into modtest variable.
  375. enginemodtest=$(echo -e "${modengines}" | awk -F ';' -v x=${gamevarindex} '{ print $x }')
  376. # If engine name matches.
  377. if [ "${enginemodtest}" == "${engine}" ]; then
  378. # Mod is compatible.
  379. modcompatibleengine="1"
  380. fi
  381. done
  382. fi
  383. }
  384. # Find out if a game is not compatible with a mod from a modnotgames (list of games not supported by a mod) variable.
  385. fn_not_compatible_mod_games() {
  386. # Reset test value.
  387. modeincompatiblegame="0"
  388. # If value is set to NOTGAMES (ignore).
  389. if [ "${modexcludegames}" != "NOTGAMES" ]; then
  390. # How many engines we need to test.
  391. excludegamesamount=$(echo -e "${modexcludegames}" | awk -F ';' '{ print NF }')
  392. # Test all subvalue of "modexcludegames" using the ";" separator.
  393. for ((gamevarindex = 1; gamevarindex < excludegamesamount; gamevarindex++)); do
  394. # Put current engine name into modtest variable.
  395. excludegamemodtest=$(echo -e "${modexcludegames}" | awk -F ';' -v x=${gamevarindex} '{ print $x }')
  396. # If engine name matches.
  397. if [ "${excludegamemodtest}" == "${gamename}" ]; then
  398. # Mod is compatible.
  399. modeincompatiblegame="1"
  400. fi
  401. done
  402. fi
  403. }
  404. # Sums up if a mod is compatible or not with modcompatibility=0/1.
  405. fn_mod_compatible_test() {
  406. # Test game and engine compatibility.
  407. fn_compatible_mod_games
  408. fn_compatible_mod_engines
  409. fn_not_compatible_mod_games
  410. if [ "${modeincompatiblegame}" == "1" ]; then
  411. modcompatibility="0"
  412. elif [ "${modcompatibleengine}" == "1" ] || [ "${modcompatiblegame}" == "1" ]; then
  413. modcompatibility="1"
  414. else
  415. modcompatibility="0"
  416. fi
  417. }
  418. ## Directory management.
  419. # Create mods files and directories if it doesn't exist.
  420. fn_create_mods_dir() {
  421. # Create lgsm data modsdir.
  422. if [ ! -d "${modsdir}" ]; then
  423. echo -en "creating LinuxGSM mods data directory ${modsdir}..."
  424. mkdir -p "${modsdir}"
  425. exitcode=$?
  426. if [ "${exitcode}" -ne 0 ]; then
  427. fn_print_fail_eol_nl
  428. fn_script_log_fail "Creating mod download dir ${modsdir}"
  429. core_exit.sh
  430. else
  431. fn_print_ok_eol_nl
  432. fn_script_log_pass "Creating mod download dir ${modsdir}"
  433. fi
  434. fi
  435. # Create mod install directory.
  436. if [ ! -d "${modinstalldir}" ]; then
  437. echo -en "creating mods install directory ${modinstalldir}..."
  438. mkdir -p "${modinstalldir}"
  439. exitcode=$?
  440. if [ "${exitcode}" -ne 0 ]; then
  441. fn_print_fail_eol_nl
  442. fn_script_log_fail "Creating mod install directory ${modinstalldir}"
  443. core_exit.sh
  444. else
  445. fn_print_ok_eol_nl
  446. fn_script_log_pass "Creating mod install directory ${modinstalldir}"
  447. fi
  448. fi
  449. # Create ${datadir}/${modsinstalledlist}.
  450. if [ ! -f "${modsinstalledlistfullpath}" ]; then
  451. touch "${modsinstalledlistfullpath}"
  452. fn_script_log_info "Created ${modsinstalledlistfullpath}"
  453. fi
  454. }
  455. # Create tmp download mod directory.
  456. fn_mods_create_tmp_dir() {
  457. if [ ! -d "${modstmpdir}" ]; then
  458. mkdir -p "${modstmpdir}"
  459. exitcode=$?
  460. echo -en "creating mod download directory ${modstmpdir}..."
  461. if [ "${exitcode}" -ne 0 ]; then
  462. fn_print_fail_eol_nl
  463. fn_script_log_fail "Creating mod download directory ${modstmpdir}"
  464. core_exit.sh
  465. else
  466. fn_print_ok_eol_nl
  467. fn_script_log_pass "Creating mod download directory ${modstmpdir}"
  468. fi
  469. fi
  470. }
  471. # Remove the tmp mod download directory when finished.
  472. fn_mods_clear_tmp_dir() {
  473. if [ -d "${modstmpdir}" ]; then
  474. echo -en "clearing mod download directory ${modstmpdir}..."
  475. rm -rf "${modstmpdir:?}"
  476. exitcode=$?
  477. if [ "${exitcode}" -ne 0 ]; then
  478. fn_print_fail_eol_nl
  479. fn_script_log_fail "Clearing mod download directory ${modstmpdir}"
  480. core_exit.sh
  481. else
  482. fn_print_ok_eol_nl
  483. fn_script_log_pass "Clearing mod download directory ${modstmpdir}"
  484. fi
  485. fi
  486. # Clear temp file list as well.
  487. if [ -f "${modsdir}/.removedfiles.tmp" ]; then
  488. rm -f "${modsdir:?}/.removedfiles.tmp"
  489. fi
  490. }
  491. # Counts how many mods were installed.
  492. fn_mods_count_installed() {
  493. if [ -f "${modsinstalledlistfullpath}" ]; then
  494. installedmodscount=$(wc -l < "${modsinstalledlistfullpath}")
  495. else
  496. installedmodscount=0
  497. fi
  498. }
  499. # Exits if no mods were installed.
  500. fn_mods_check_installed() {
  501. # Count installed mods.
  502. fn_mods_count_installed
  503. # If no mods are found.
  504. if [ "${installedmodscount}" -eq 0 ]; then
  505. echo -e ""
  506. fn_print_failure_nl "No installed mods or addons were found"
  507. echo -e " * Install mods using LinuxGSM first with: ./${selfname} mods-install"
  508. fn_script_log_error "No installed mods or addons were found."
  509. core_exit.sh
  510. fi
  511. }
  512. # Checks that mod files list exists and isn't empty.
  513. fn_check_mod_files_list() {
  514. # File list must exist and be valid before any operation on it.
  515. if [ -f "${modsdir}/${modcommand}-files.txt" ]; then
  516. # How many lines is the file list.
  517. modsfilelistsize=$(wc -l < "${modsdir}/${modcommand}-files.txt")
  518. # If file list is empty.
  519. if [ "${modsfilelistsize}" -eq 0 ]; then
  520. fn_print_failure "${modcommand}-files.txt is empty"
  521. echo -e "* Unable to remove ${modprettyname}"
  522. fn_script_log_fail "${modcommand}-files.txt is empty: Unable to remove ${modprettyname}."
  523. core_exit.sh
  524. fi
  525. else
  526. fn_print_failure "${modsdir}/${modcommand}-files.txt does not exist"
  527. fn_script_log_fail "${modsdir}/${modcommand}-files.txt does not exist: Unable to remove ${modprettyname}."
  528. core_exit.sh
  529. fi
  530. }
  531. fn_mod_exist() {
  532. modreq=$1
  533. # requires one parameter, the mod
  534. if [ -f "${modsdir}/${modreq}-files.txt" ]; then
  535. # how many lines is the file list
  536. modsfilelistsize=$(wc -l < "${modsdir}/${modreq}-files.txt")
  537. # if file list is empty
  538. if [ "${modsfilelistsize}" -eq 0 ]; then
  539. fn_mod_required_fail_exist "${modreq}"
  540. fi
  541. else
  542. fn_mod_required_fail_exist "${modreq}"
  543. fi
  544. }
  545. fn_mod_required_fail_exist() {
  546. modreq=$1
  547. # requires one parameter, the mod
  548. fn_script_log_fail "${modreq}-files.txt is empty: unable to find ${modreq} installed"
  549. echo -en "* Unable to find '${modreq}' which is required prior to installing this mod..."
  550. fn_print_fail_eol_nl
  551. core_exit.sh
  552. }
  553. fn_mod_liblist_gam_filenames() {
  554. # clear variables just in case
  555. moddll=""
  556. modso=""
  557. moddylib=""
  558. # default libraries
  559. case ${gamename} in
  560. "Counter-Strike 1.6")
  561. moddll="mp.dll"
  562. modso="cs.so"
  563. moddylib="cs.dylib"
  564. ;;
  565. "Day of Defeat")
  566. moddll="dod.dll"
  567. modso="dod.so"
  568. moddylib="dod.dylib"
  569. ;;
  570. "Team Fortress Classic")
  571. moddll="tfc.dll"
  572. modso="tfc.so"
  573. moddylib="tfc.dylib"
  574. ;;
  575. "Natural Selection")
  576. moddll="ns.dll"
  577. modso="ns_i386.so"
  578. moddylib=""
  579. ;;
  580. "The Specialists")
  581. moddll="mp.dll"
  582. modso="ts_i386.so"
  583. moddylib=""
  584. ;;
  585. "Half-Life: Deathmatch")
  586. moddll="hl.dll"
  587. modso="hl.so"
  588. moddylib="hl.dylib"
  589. ;;
  590. esac
  591. }
  592. # modifiers for liblist.gam to add/remote metamod binaries
  593. fn_mod_install_liblist_gam_file() {
  594. fn_mod_liblist_gam_filenames
  595. if [ -f "${modinstalldir}/liblist.gam" ]; then
  596. # modify the liblist.gam file to initialize Metamod
  597. logentry="sed replace (dlls\\${moddll}) ${modinstalldir}/liblist.gam"
  598. echo -en "modifying gamedll in liblist.gam..."
  599. rpldll="s/dlls\\\\${moddll}/addons\/metamod\/dlls\/metamod.dll/g"
  600. sed -i $rpldll "${modinstalldir}/liblist.gam"
  601. grep -q "addons/metamod/dlls/metamod.dll" "${modinstalldir}/liblist.gam"
  602. exitcode=$?
  603. # if replacement back didn't happen, error out.
  604. if [ "${exitcode}" -ne 0 ]; then
  605. fn_script_log_fail "${logentry}"
  606. fn_print_fail_eol_nl
  607. else
  608. fn_script_log_pass "${logentry}"
  609. fn_print_ok_eol_nl
  610. fi
  611. # modify the liblist.gam file to initialize metamod
  612. logentry="sed replace (dlls\\${modso}) ${modinstalldir}/liblist.gam"
  613. echo -en "modifying gamedll_linux in liblist.gam..."
  614. rplso="s/dlls\/${modso}/addons\/metamod\/dlls\/metamod.so/g"
  615. sed -i $rplso "${modinstalldir}/liblist.gam"
  616. grep -q "addons/metamod/dlls/metamod.so" "${modinstalldir}/liblist.gam"
  617. exitcode=$?
  618. # if replacement back didn't happen, error out
  619. if [ "${exitcode}" -ne 0 ]; then
  620. fn_script_log_fail "${logentry}"
  621. fn_print_fail_eol_nl
  622. else
  623. fn_script_log_pass "${logentry}"
  624. fn_print_ok_eol_nl
  625. fi
  626. # mac os needs to be checked not all mods support mac os
  627. if [ -n "${moddylib}" ]; then
  628. # modify the liblist.gam file to initialize metamod
  629. logentry="sed replace (dlls\\${moddylib}) ${modinstalldir}/liblist.gam"
  630. echo -en "modifying gamedll_osx in liblist.gam..."
  631. rpldylib="s/dlls\/${moddylib}/addons\/metamod\/dlls\/metamod.dylib/g"
  632. sed -i $rpldylib "${modinstalldir}/liblist.gam"
  633. grep -q "addons/metamod/dlls/metamod.dylib" "${modinstalldir}/liblist.gam"
  634. exitcode=$?
  635. # if replacement back didn't happen, error out.
  636. if [ "${exitcode}" -ne 0 ]; then
  637. fn_script_log_fail "${logentry}"
  638. fn_print_fail_eol_nl
  639. else
  640. fn_script_log_pass "${logentry}"
  641. fn_print_ok_eol_nl
  642. fi
  643. fi
  644. fi
  645. }
  646. fn_mod_remove_liblist_gam_file() {
  647. fn_mod_liblist_gam_filenames
  648. if [ -f "${modinstalldir}/liblist.gam" ]; then
  649. # modify the liblist.gam file back to defaults
  650. logentry="sed replace (addons/metamod/dlls/metamod.dll) ${modinstalldir}/liblist.gam"
  651. echo -en "modifying gamedll in liblist.gam..."
  652. rpldll="s/addons\/metamod\/dlls\/metamod.dll/dlls\\\\${moddll}/g"
  653. sed -i $rpldll "${modinstalldir}/liblist.gam"
  654. grep -q "${moddll}" "${modinstalldir}/liblist.gam"
  655. exitcode=$?
  656. # if replacement back didn't happen, error out.
  657. if [ "${exitcode}" -ne 0 ]; then
  658. fn_script_log_fail "${logentry}"
  659. fn_print_fail_eol_nl
  660. else
  661. fn_script_log_pass "${logentry}"
  662. fn_print_ok_eol_nl
  663. fi
  664. # modify the liblist.gam file back to defaults
  665. logentry="sed replace (addons/metamod/dlls/metamod.so) ${modinstalldir}/liblist.gam"
  666. echo -en "modifying gamedll_linux in liblist.gam..."
  667. rplso="s/addons\/metamod\/dlls\/metamod.so/dlls\/${modso}/g"
  668. sed -i $rplso "${modinstalldir}/liblist.gam"
  669. grep -q "${modso}" "${modinstalldir}/liblist.gam"
  670. exitcode=$?
  671. # if replacement back didn't happen, error out
  672. if [ "${exitcode}" -ne 0 ]; then
  673. fn_script_log_fail "${logentry}"
  674. fn_print_fail_eol_nl
  675. else
  676. fn_script_log_pass "${logentry}"
  677. fn_print_ok_eol_nl
  678. fi
  679. # mac os needs to be checked not all mods support mac os
  680. if [ -n "${moddylib}" ]; then
  681. # modify the liblist.gam file back to defaults
  682. logentry="sed replace (addons/metamod/dlls/metamod.dylib) ${modinstalldir}/liblist.gam"
  683. echo -en "modifying gamedll_osx in liblist.gam..."
  684. rpldylib="s/addons\/metamod\/dlls\/metamod.dylib/dlls\/${moddylib}/g"
  685. sed -i $rpldylib "${modinstalldir}/liblist.gam"
  686. grep -q "${moddylib}" "${modinstalldir}/liblist.gam"
  687. # if replacement back didn't happen, error out.
  688. exitcode=$?
  689. if [ "${exitcode}" -ne 0 ]; then
  690. fn_script_log_fail "${logentry}"
  691. fn_print_fail_eol_nl
  692. else
  693. fn_script_log_pass "${logentry}"
  694. fn_print_ok_eol_nl
  695. fi
  696. fi
  697. fi
  698. }
  699. # modifiers for gameinfo.gi to add/remove the Metamod:Source search path
  700. # Counter-Strike 2 only: the csgo paths below are specific to that game.
  701. fn_mod_install_gameinfo_gi_file() {
  702. local gameinfofile="${modinstalldir}/gameinfo.gi"
  703. local metamodpattern='^[[:space:]]*"?Game"?[[:space:]]+"?csgo/addons/metamod"?[[:space:]]*(//.*)?$'
  704. local lowviolencepattern='^[[:space:]]*"?Game_LowViolence"?[[:space:]]+"?csgo_lv"?[[:space:]]*(//.*)?$'
  705. if [ -f "${gameinfofile}" ]; then
  706. # the search path survives a mod update, only add it when it is missing
  707. if grep -Eq "${metamodpattern}" "${gameinfofile}"; then
  708. fn_script_log_info "Metamod:Source search path already set in ${gameinfofile}"
  709. else
  710. # insert the search path below Game_LowViolence as documented by Metamod:Source
  711. logentry="line (Game csgo/addons/metamod) inserted into ${gameinfofile}"
  712. echo -en "adding Metamod:Source search path in gameinfo.gi..."
  713. sed -i -E "\\@${lowviolencepattern}@a\\\\t\\t\\tGame\\tcsgo/addons/metamod" "${gameinfofile}"
  714. grep -Eq "${metamodpattern}" "${gameinfofile}"
  715. exitcode=$?
  716. # if the insert didn't happen, error out
  717. if [ "${exitcode}" -ne 0 ]; then
  718. fn_script_log_fail "${logentry}"
  719. fn_print_fail_eol_nl
  720. core_exit.sh
  721. else
  722. fn_script_log_pass "${logentry}"
  723. fn_print_ok_eol_nl
  724. fi
  725. fi
  726. else
  727. fn_script_log_fail "Unable to find ${gameinfofile}"
  728. fn_print_fail_nl "Unable to find ${gameinfofile}"
  729. core_exit.sh
  730. fi
  731. }
  732. # Unlike the install counterpart this does not call core_exit.sh on failure:
  733. # command_mods_remove.sh ends with fn_script_log, which leaves exitcode alone,
  734. # while command_mods_install.sh ends with fn_script_log_pass, which resets it to 0.
  735. fn_mod_remove_gameinfo_gi_file() {
  736. local gameinfofile="${modinstalldir}/gameinfo.gi"
  737. local metamodpattern='^[[:space:]]*"?Game"?[[:space:]]+"?csgo/addons/metamod"?[[:space:]]*(//.*)?$'
  738. if [ -f "${gameinfofile}" ]; then
  739. # is the search path found? If so remove it and keep the rest of the file
  740. if grep -Eq "${metamodpattern}" "${gameinfofile}"; then
  741. logentry="line (Game csgo/addons/metamod) removed from ${gameinfofile}"
  742. echo -en "removing Metamod:Source search path in gameinfo.gi..."
  743. # delete the line we inserted
  744. sed -i -E "\\@${metamodpattern}@d" "${gameinfofile}"
  745. if grep -Eq "${metamodpattern}" "${gameinfofile}"; then
  746. fn_script_log_fail "${logentry}"
  747. fn_print_fail_eol_nl
  748. else
  749. fn_script_log_pass "${logentry}"
  750. fn_print_ok_eol_nl
  751. fi
  752. fi
  753. fi
  754. }
  755. fn_mod_install_amxmodx_file() {
  756. # does plugins.ini exist?
  757. if [ -f "${modinstalldir}/addons/metamod/plugins.ini" ]; then
  758. # since it does exist, is the entry already in plugins.ini
  759. logentry="line (linux addons/amxmodx/dlls/amxmodx_mm_i386.so) inserted into ${modinstalldir}/addons/metamod/plugins.ini"
  760. echo -en "adding amxmodx_mm_i386.so in plugins.ini..."
  761. if ! grep -q "amxmodx_mm_i386.so" "${modinstalldir}/addons/metamod/plugins.ini"; then
  762. # file exists but the entry does not, let's add it
  763. if ! echo "linux addons/amxmodx/dlls/amxmodx_mm_i386.so" >> "${modinstalldir}/addons/metamod/plugins.ini"; then
  764. fn_script_log_fail "${logentry}"
  765. fn_print_fail_eol_nl
  766. else
  767. fn_script_log_pass "${logentry}"
  768. fn_print_ok_eol_nl
  769. fi
  770. fi
  771. else
  772. # create new file and add the mod to it
  773. if ! echo "linux addons/amxmodx/dlls/amxmodx_mm_i386.so" > "${modinstalldir}/addons/metamod/plugins.ini"; then
  774. fn_script_log_fail "${logentry}"
  775. fn_print_fail_eol_nl
  776. core_exit.sh
  777. else
  778. fn_script_log_pass "${logentry}"
  779. fn_print_ok_eol_nl
  780. fi
  781. fi
  782. }
  783. fn_mod_remove_amxmodx_file() {
  784. if [ -f "${modinstalldir}/addons/metamod/plugins.ini" ]; then
  785. # since it does exist, is the entry already in plugins.ini
  786. logentry="line (linux addons/amxmodx/dlls/amxmodx_mm_i386.so) removed from ${modinstalldir}/addons/metamod/plugins.ini"
  787. echo -en "removing amxmodx_mm_i386.so in plugins.ini..."
  788. # is it found? If so remove it and clean up
  789. if grep -q "linux addons/amxmodx/dlls/amxmodx_mm_i386.so" "${modinstalldir}/addons/metamod/plugins.ini"; then
  790. # delete the line we inserted
  791. sed -i '/linux addons\/amxmodx\/dlls\/amxmodx_mm_i386.so/d' "${modinstalldir}/addons/metamod/plugins.ini"
  792. # remove empty lines
  793. sed -i '/^$/d' "${modinstalldir}/addons/metamod/plugins.ini"
  794. exitcode=$?
  795. if [ "${exitcode}" -ne 0 ]; then
  796. fn_script_log_fail "${logentry}"
  797. fn_print_fail_eol_nl
  798. else
  799. fn_script_log_pass "${logentry}"
  800. fn_print_ok_eol_nl
  801. fi
  802. # if file is empty, remove it.
  803. if [ -f "${modinstalldir}/addons/metamod/plugins.ini" ]; then
  804. rm -f "${modinstalldir}/addons/metamod/plugins.ini"
  805. fn_script_log_pass "file removed ${modinstalldir}/addons/metamod/plugins.ini because it was empty"
  806. fi
  807. fi
  808. fi
  809. }
  810. ## Database initialisation.
  811. mods_list.sh
  812. fn_mods_available