#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" if [[ -f "$ENV_FILE" ]]; then set -a # shellcheck disable=SC1090 source "$ENV_FILE" set +a fi ETC_DIR="${ACORE_ETC_DIR:-$ROOT_DIR/env/dist/etc}" MODULES_ETC_DIR="${ACORE_MODULES_ETC_DIR:-$ETC_DIR/modules}" WORLD_CONF="${WORLD_CONF:-$ETC_DIR/worldserver.conf}" DRY_RUN=0 FORCE_PLAYERBOTS="${FORCE_PLAYERBOTS:-${ACORE_PLAYERBOTS_FORCE:-0}}" : "${ACORE_WORLD_ENABLE_PLAYER_SETTINGS:=1}" : "${ACORE_WORLD_CONSOLE_ENABLE:=0}" : "${ACORE_WORLD_DBC_ENFORCE_ITEM_ATTRIBUTES:=0}" : "${ACORE_WORLD_INSTANCE_IGNORE_RAID:=1}" : "${ACORE_WORLD_RATE_CREATURE_ELITE_ELITE_DAMAGE:=0.80}" : "${ACORE_WORLD_RATE_CREATURE_RARE_DAMAGE:=0.80}" : "${ACORE_WORLD_RATE_CREATURE_RAREELITE_DAMAGE:=0.75}" : "${ACORE_WORLD_RATE_CREATURE_WORLDBOSS_DAMAGE:=1}" : "${ACORE_WORLD_RATE_CREATURE_ELITE_ELITE_SPELL_DAMAGE:=0.80}" : "${ACORE_WORLD_RATE_CREATURE_RARE_SPELL_DAMAGE:=0.80}" : "${ACORE_WORLD_RATE_CREATURE_RAREELITE_SPELL_DAMAGE:=0.75}" : "${ACORE_WORLD_RATE_CREATURE_WORLDBOSS_SPELL_DAMAGE:=1}" : "${ACORE_WORLD_RATE_CREATURE_ELITE_ELITE_HP:=0.75}" : "${ACORE_WORLD_RATE_CREATURE_RARE_HP:=0.70}" : "${ACORE_WORLD_RATE_CREATURE_RAREELITE_HP:=0.65}" : "${ACORE_WORLD_RATE_CREATURE_WORLDBOSS_HP:=1}" : "${ACORE_AUTOBALANCE_MIN_PLAYERS:=1}" : "${ACORE_AUTOBALANCE_MIN_PLAYERS_HEROIC:=1}" : "${ACORE_AUTOBALANCE_MIN_PLAYERS_RAID:=1}" : "${ACORE_AUTOBALANCE_MIN_PLAYERS_RAID_HEROIC:=1}" : "${ACORE_AUTOBALANCE_INFLECTION_POINT_RAID:=0.75}" : "${ACORE_AUTOBALANCE_INFLECTION_POINT_RAID_BOSS_MODIFIER:=1.35}" : "${ACORE_AUTOBALANCE_INFLECTION_POINT_RAID_HEROIC:=0.80}" : "${ACORE_AUTOBALANCE_INFLECTION_POINT_RAID_HEROIC_BOSS_MODIFIER:=1.45}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_BOSS_DAMAGE:=0.85}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_HEROIC_BOSS_DAMAGE:=0.75}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_GLOBAL:=0.85}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_DAMAGE:=0.75}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_BOSS_HEALTH:=0.70}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_BOSS_DAMAGE:=0.55}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_HEROIC_GLOBAL:=0.75}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_HEROIC_DAMAGE:=0.70}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_HEROIC_BOSS_HEALTH:=0.60}" : "${ACORE_AUTOBALANCE_STAT_MODIFIER_RAID_HEROIC_BOSS_DAMAGE:=0.45}" : "${ACORE_PROGRESSION_ENABLE:=1}" : "${ACORE_PROGRESSION_SIMPLE_CONFIG_OVERRIDE:=1}" : "${ACORE_PROGRESSION_STARTING_PROGRESSION:=0}" : "${ACORE_PROGRESSION_LIMIT:=0}" : "${ACORE_PROGRESSION_ENFORCE_GROUP_RULES:=1}" : "${ACORE_PROGRESSION_TBC_RACES_UNLOCK:=8}" : "${ACORE_PROGRESSION_DK_UNLOCK:=13}" : "${ACORE_PROGRESSION_DK_STARTING:=13}" : "${ACORE_PROGRESSION_MC_AQUAL_COOLDOWN_REDUCTION:=60}" : "${ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS:=0}" : "${ACORE_AOE_LOOT_ENABLE:=1}" : "${ACORE_AOE_LOOT_MESSAGE:=0}" : "${ACORE_AOE_LOOT_RANGE:=55.0}" : "${ACORE_AOE_LOOT_GROUP:=1}" : "${ACORE_ELUNA_ENABLED:=true}" : "${ACORE_ELUNA_TRACEBACK:=false}" : "${ACORE_ELUNA_SCRIPT_PATH:=lua_scripts}" : "${ACORE_ELUNA_PLAYER_ANNOUNCE_RELOAD:=false}" : "${ACORE_ELUNA_REQUIRE_PATHS:=}" : "${ACORE_ELUNA_REQUIRE_CPATHS:=}" : "${ACORE_LEARNSPELLS_GAMEMASTERS:=0}" : "${ACORE_LEARNSPELLS_CLASS_SPELLS:=1}" : "${ACORE_LEARNSPELLS_PROFICIENCIES:=1}" : "${ACORE_LEARNSPELLS_SPELLS_FROM_QUESTS:=1}" : "${ACORE_LEARNSPELLS_RIDING_APPRENTICE:=0}" : "${ACORE_LEARNSPELLS_RIDING_JOURNEYMAN:=0}" : "${ACORE_LEARNSPELLS_RIDING_EXPERT:=0}" : "${ACORE_LEARNSPELLS_RIDING_ARTISAN:=0}" : "${ACORE_LEARNSPELLS_RIDING_COLD_WEATHER_FLYING:=0}" : "${ACORE_PLAYERBOTS_ENABLED:=1}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_AUTOLOGIN:=${AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN:-1}}" : "${ACORE_PLAYERBOTS_MIN_RANDOM_BOTS:=0}" : "${ACORE_PLAYERBOTS_MAX_RANDOM_BOTS:=0}" : "${ACORE_PLAYERBOTS_MAX_ADDED_BOTS:=9}" : "${ACORE_PLAYERBOTS_ADDCLASS_COMMAND:=1}" : "${ACORE_PLAYERBOTS_GROUP_INVITATION_PERMISSION:=2}" : "${ACORE_PLAYERBOTS_KEEP_ALTS_IN_GROUP:=1}" : "${ACORE_PLAYERBOTS_BOT_AUTOLOGIN:=0}" : "${ACORE_PLAYERBOTS_ALLOW_ACCOUNT_BOTS:=1}" : "${ACORE_PLAYERBOTS_ALLOW_GUILD_BOTS:=1}" : "${ACORE_PLAYERBOTS_ALLOW_TRUSTED_ACCOUNT_BOTS:=1}" : "${ACORE_PLAYERBOTS_SUMMON_WHEN_GROUP:=1}" : "${ACORE_PLAYERBOTS_AUTO_TRAIN_SPELLS:=yes}" : "${ACORE_PLAYERBOTS_SYNC_QUEST_WITH_PLAYER:=1}" : "${ACORE_PLAYERBOTS_SYNC_LEVEL_WITH_PLAYERS:=1}" : "${ACORE_PLAYERBOTS_APPLY_INSTANCE_STRATEGIES:=1}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_INVITE_PLAYER:=0}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_JOIN_LFG:=0}" : "${ACORE_PLAYERBOTS_BOT_CHEATS:=food,taxi,raid}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_ARENA_TEAM_2V2_COUNT:=0}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_ARENA_TEAM_3V3_COUNT:=0}" : "${ACORE_PLAYERBOTS_RANDOM_BOT_ARENA_TEAM_5V5_COUNT:=0}" : "${ACORE_PLAYERBOTS_DELETE_RANDOM_BOT_ARENA_TEAMS:=0}" usage() { cat < "$tmp" if [[ -f "$target" ]] && cmp -s "$tmp" "$target"; then rm -f "$tmp" log "unchanged ${target#$ROOT_DIR/}" return fi if (( DRY_RUN )); then rm -f "$tmp" log "would write ${target#$ROOT_DIR/}" return fi mkdir -p "$(dirname "$target")" mv "$tmp" "$target" log "wrote ${target#$ROOT_DIR/}" } sync_file() { local source="$1" local target="$2" if [[ ! -f "$source" ]]; then log "skip missing ${source#$ROOT_DIR/}" return fi if [[ -f "$target" ]] && cmp -s "$source" "$target"; then log "unchanged ${target#$ROOT_DIR/}" return fi if (( DRY_RUN )); then log "would sync ${source#$ROOT_DIR/} -> ${target#$ROOT_DIR/}" return fi mkdir -p "$(dirname "$target")" cp "$source" "$target" log "synced ${target#$ROOT_DIR/}" } escape_regex() { printf '%s' "$1" | sed 's/[][(){}.^$*+?|\\/]/\\&/g' } set_config_value() { local file="$1" local key="$2" local value="$3" local pattern local desired_line pattern="$(escape_regex "$key")" desired_line="${key} = ${value}" if [[ ! -f "$file" ]]; then if (( DRY_RUN )); then log "would create ${file#$ROOT_DIR/}" else mkdir -p "$(dirname "$file")" : > "$file" log "created ${file#$ROOT_DIR/}" fi fi if [[ -f "$file" ]] && grep -Eq "^[[:space:]]*${pattern}[[:space:]]*=" "$file"; then local current_line current_line="$(grep -E "^[[:space:]]*${pattern}[[:space:]]*=" "$file" | head -n 1 | sed -E 's/^[[:space:]]*//')" if [[ "$current_line" == "$desired_line" ]]; then log "unchanged ${key} in ${file#$ROOT_DIR/}" return fi if (( DRY_RUN )); then log "would set ${key} in ${file#$ROOT_DIR/} to ${value}" return fi sed -i -E "0,/^[[:space:]]*${pattern}[[:space:]]*=/{s|^[[:space:]]*${pattern}[[:space:]]*=.*$|${desired_line}|}" "$file" log "set ${key} in ${file#$ROOT_DIR/}" return fi if (( DRY_RUN )); then log "would append ${key} to ${file#$ROOT_DIR/}" return fi printf '\n%s = %s\n' "$key" "$value" >> "$file" log "appended ${key} to ${file#$ROOT_DIR/}" } ensure_world_conf() { if [[ -f "$WORLD_CONF" ]]; then return fi local source="" for candidate in \ "$ETC_DIR/worldserver.conf.dist" \ "$ROOT_DIR/src/server/apps/worldserver/worldserver.conf.dist" do if [[ -f "$candidate" ]]; then source="$candidate" break fi done if [[ -z "$source" ]]; then log "cannot find a worldserver.conf.dist source" exit 1 fi sync_file "$source" "$WORLD_CONF" } sync_module_dists() { sync_file "$ROOT_DIR/modules/mod-aoe-loot/conf/mod_aoe_loot.conf.dist" \ "$MODULES_ETC_DIR/mod_aoe_loot.conf.dist" sync_file "$ROOT_DIR/modules/mod-autobalance/conf/AutoBalance.conf.dist" \ "$MODULES_ETC_DIR/AutoBalance.conf.dist" sync_file "$ROOT_DIR/modules/mod-eluna/conf/mod_eluna.conf.dist" \ "$MODULES_ETC_DIR/mod_eluna.conf.dist" sync_file "$ROOT_DIR/modules/mod-individual-progression/conf/individualProgression.conf.dist" \ "$MODULES_ETC_DIR/individualProgression.conf.dist" sync_file "$ROOT_DIR/modules/mod-learnspells/conf/mod_learnspells.conf.dist" \ "$MODULES_ETC_DIR/mod_learnspells.conf.dist" sync_file "$ROOT_DIR/modules/mod-playerbots/conf/playerbots.conf.dist" \ "$MODULES_ETC_DIR/playerbots.conf.dist" } write_autobalance_conf() { write_file "$MODULES_ETC_DIR/AutoBalance.conf" < TBC -> WotLK. IndividualProgression.Enable = ${ACORE_PROGRESSION_ENABLE} IndividualProgression.SimpleConfigOverride = ${ACORE_PROGRESSION_SIMPLE_CONFIG_OVERRIDE} IndividualProgression.StartingProgression = ${ACORE_PROGRESSION_STARTING_PROGRESSION} IndividualProgression.ProgressionLimit = ${ACORE_PROGRESSION_LIMIT} # Keep progression integrity for grouped players. IndividualProgression.EnforceGroupRules = ${ACORE_PROGRESSION_ENFORCE_GROUP_RULES} # Unlocks. IndividualProgression.TbcRacesUnlockProgression = ${ACORE_PROGRESSION_TBC_RACES_UNLOCK} IndividualProgression.DeathKnightUnlockProgression = ${ACORE_PROGRESSION_DK_UNLOCK} IndividualProgression.DeathKnightStartingProgression = ${ACORE_PROGRESSION_DK_STARTING} # Solo-friendly quality-of-life for MC rune handling. IndividualProgression.MoltenCore.AqualEssenceCooldownReduction = ${ACORE_PROGRESSION_MC_AQUAL_COOLDOWN_REDUCTION} # PvE-focused progression. IndividualProgression.PvPGearRequirements = ${ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS} EOF } write_aoe_loot_conf() { write_file "$MODULES_ETC_DIR/mod_aoe_loot.conf" <&2 usage >&2 exit 1 ;; esac shift done log "root: $ROOT_DIR" log "etc: $ETC_DIR" ensure_runtime_dirs sync_module_dists configure_worldserver write_autobalance_conf write_individual_progression_conf write_aoe_loot_conf write_eluna_conf write_learnspells_conf write_playerbots_conf log "module bootstrap completed" } main "$@"