{ config, lib, pkgs, ... }: with lib; let cfg = config.services.azerothcore; in { options.services.azerothcore = { enable = mkEnableOption "AzerothCore service"; openFirewall = mkOption { type = types.bool; default = false; description = mdDoc "Whether to open the firewall for the auth & world server ports."; }; serverPackage = mkPackageOption pkgs "azerothcore" { }; clientDataPackage = mkPackageOption pkgs "azerothcore-data" { }; srcDir = mkOption { type = types.path; default = "${pkgs.azerothcore}/share"; description = mdDoc "The data directory"; }; dataDir = mkOption { type = types.path; default = "${pkgs.azerothcore-data}"; description = mdDoc "The data directory"; }; logDir = mkOption { type = types.path; default = "/var/log/azerothcore"; description = mdDoc "The log directory"; }; tmpDir = mkOption { type = types.path; default = "/tmp/azerothcore"; description = mdDoc "The tmp directory to use"; }; hostname = mkOption { type = types.str; default = "127.0.0.1"; description = lib.mdDoc "The hostname to reach the server."; }; database = mkOption { type = types.submodule { options = { user = mkOption { type = types.str; description = mdDoc "The MySQL database user to use for auth & world servers."; }; password = mkOption { type = types.str; description = mdDoc "The MySQL database password to use for auth & world servers."; }; host = mkOption { type = types.str; description = mdDoc "The MySQL host to use for auth & world servers."; }; port = mkOption { type = types.port; description = mdDoc "The port host to use for auth & world servers."; }; }; }; default = { user = "acore"; password = ""; host = "127.0.0.1"; port = 3306; }; description = mdDoc "Database configuration."; }; auth = mkOption { type = types.submodule { options = { port = mkOption { type = types.port; description = lib.mdDoc "Port to listen on for the auth server."; }; address = mkOption { type = types.str; description = mdDoc "Address to listen on for the auth server."; }; database = mkOption { type = types.str; description = "Database name for the auth server."; }; }; }; default = { port = 3724; address = "0.0.0.0"; database = "acore_auth"; }; description = mdDoc "Auth server configuration."; }; world = mkOption { type = types.submodule { options = { realmId = mkOption { type = types.int; description = lib.mdDoc "Port to listen on for the worl server."; }; port = mkOption { type = types.port; description = lib.mdDoc "Port to listen on for the worl server."; }; address = mkOption { type = types.str; description = mdDoc "Address to listen on for the world server."; }; database = mkOption { type = types.str; description = "Database name for the world server."; }; charactersDatabase = mkOption { type = types.str; description = "Characters database name for the world server."; }; }; }; default = { realmId = 1; port = 8085; address = "0.0.0.0"; database = "acore_world"; charactersDatabase = "acore_characters"; }; description = mdDoc "World server configuration."; }; npcbots = mkOption { type = types.submodule { options = { botgiver = { filterRaces = mkOption { type = types.int; }; }; classes = { warrior = mkOption { type = types.bool; }; paladin = mkOption { type = types.bool; }; hunter = mkOption { type = types.bool; }; rogue = mkOption { type = types.bool; }; priest = mkOption { type = types.bool; }; deathKnight = mkOption { type = types.bool; }; shaman = mkOption { type = types.bool; }; mage = mkOption { type = types.bool; }; warlock = mkOption { type = types.bool; }; druid = mkOption { type = types.bool; }; blademaster = mkOption { type = types.bool; }; obsidianDestroyer = mkOption { type = types.bool; }; archmage = mkOption { type = types.bool; }; dreadlord = mkOption { type = types.bool; }; spellBreaker = mkOption { type = types.bool; }; darkRanger = mkOption { type = types.bool; }; necromancer = mkOption { type = types.bool; }; seaWitch = mkOption { type = types.bool; }; cryptLord = mkOption { type = types.bool; }; }; }; }; default = { botgiver = { filterRaces = 0; }; classes = { warrior = true; paladin = true; hunter = true; rogue = true; priest = true; deathKnight = true; shaman = true; mage = true; warlock = true; druid = true; blademaster = false; obsidianDestroyer = true; archmage = true; dreadlord = true; spellBreaker = true; darkRanger = true; necromancer = true; seaWitch = true; cryptLord = true; }; }; }; ahbot = mkOption { type = types.submodule { options = { EnableSeller = mkOption { type = types.int; }; EnableBuyer = mkOption { type = types.int; }; Account = mkOption { type = types.int; }; GUID = mkOption { type = types.int; }; ItemsPerCycle = mkOption { type = types.int; }; ElapsingTimeClass = mkOption { type = types.int; }; VendorItems = mkOption { type = types.int; }; VendorTradeGoods = mkOption { type = types.int; }; LootItems = mkOption { type = types.int; }; LootTradeGoods = mkOption { type = types.int; }; OtherItems = mkOption { type = types.int; }; OtherTradeGoods = mkOption { type = types.int; }; ProfessionItems = mkOption { type = types.int; }; }; }; default = { EnableSeller = 0; EnableBuyer = 0; Account = 0; GUID = 0; ItemsPerCycle = 200; ElapsingTimeClass = 1; VendorItems = 0; VendorTradeGoods = 0; LootItems = 1; LootTradeGoods = 1; OtherItems = 0; OtherTradeGoods = 0; ProfessionItems = 0; }; }; }; config = mkIf cfg.enable { users.users.azerothcore = { home = cfg.dataDir; createHome = true; isSystemUser = true; group = "azerothcore"; }; users.groups.azerothcore.name = "azerothcore"; networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.auth.port cfg.world.port ]; environment.etc = { "azerothcore/authserver.conf".source = pkgs.runCommand "authserver.conf" { } '' cp ${cfg.serverPackage}/etc/authserver.conf.dist $out ''; "azerothcore/worldserver.conf".source = pkgs.runCommand "worldserver.conf" { } '' cp ${cfg.serverPackage}/etc/worldserver.conf.dist $out ''; "azerothcore/dbimport.conf".source = pkgs.runCommand "dbimport.conf" { } '' cp ${cfg.serverPackage}/etc/dbimport.conf.dist $out ''; "azerothcore/modules/mod_ahbot.conf".source = pkgs.runCommand "mod_ahbot.conf" { } '' cp ${cfg.serverPackage}/etc/modules/mod_ahbot.conf.dist $out ''; }; systemd.services = let env_key = prefix: key: "${prefix}_${key}"; ac_env_key = env_key "AC"; npcbot_env_key = env_key "AC_NPC_BOT"; ahbot_env_key = env_key "AC_AUCTION_HOUSE_BOT"; mysqlHost = "${cfg.database.host}"; mysqlPort = toString cfg.database.port; mysqlSocket = "/run/mysqld/mysqld.sock"; databaseInfo = (user: password: database: (if mysqlHost == "127.0.0.1" then ".;${mysqlSocket};${user};${password};${database}" else "${mysqlHost};${mysqlPort};${user};${password};${database}")) cfg.database.user cfg.database.password; authDatabaseInfo = databaseInfo cfg.auth.database; worldDatabaseInfo = databaseInfo cfg.world.database; characterDatabaseInfo = databaseInfo cfg.world.charactersDatabase; in { azerothcore-authserver = { description = "AzerothCore Auth Server"; after = [ "network-online.target" "mysql.service" ]; wants = [ "network-online.target" "mysql.service" ]; environment = { "${ac_env_key "CONSOLE_ENABLE"}" = "0"; "${ac_env_key "DATA_DIR"}" = cfg.clientDataPackage; "${ac_env_key "LOGS_DIR"}" = cfg.logDir; "${ac_env_key "TMP_DIR"}" = cfg.tmpDir; "${ac_env_key "SOURCE_DIRECTORY"}" = toString cfg.serverPackage.srcDir; "${ac_env_key "REALM_SERVER_PORT"}" = toString cfg.auth.port; "${ac_env_key "BIND_IP"}" = cfg.auth.address; "${ac_env_key "LOGIN_DATABASE_INFO"}" = authDatabaseInfo; }; serviceConfig = { Type = "simple"; User = "azerothcore"; Group = "azerothcore"; Restart = "on-failure"; ExecStart = "${cfg.serverPackage}/bin/authserver -c /etc/azerothcore/authserver.conf"; }; }; azerothcore-worldserver = { description = "AzerothCore World Server"; after = [ "network-online.target" "mysql.service" ]; wants = [ "network-online.target" "mysql.service" ]; environment = { "${ac_env_key "REALM_ID"}" = "${toString cfg.world.realmId}"; "${ac_env_key "CONSOLE_ENABLE"}" = "0"; "${ac_env_key "DATA_DIR"}" = "${cfg.clientDataPackage}"; "${ac_env_key "LOGS_DIR"}" = "${cfg.logDir}"; "${ac_env_key "TMP_DIR"}" = cfg.tmpDir; "${ac_env_key "SOURCE_DIRECTORY"}" = cfg.serverPackage.srcDir; "${ac_env_key "WORLD_SERVER_PORT"}" = toString cfg.world.port; "${ac_env_key "BIND_IP"}" = cfg.world.address; "${ac_env_key "LOGIN_DATABASE_INFO"}" = authDatabaseInfo; "${ac_env_key "WORLD_DATABASE_INFO"}" = worldDatabaseInfo; "${ac_env_key "CHARACTER_DATABASE_INFO"}" = characterDatabaseInfo; "${npcbot_env_key "BOTGIVER_FILTER_RACES"}" = toString cfg.npcbots.botgiver.filterRaces; "${npcbot_env_key "CLASSES_WARRIOR_ENABLE"}" = if cfg.npcbots.classes.warrior then "1" else "0"; "${npcbot_env_key "CLASSES_PALADIN_ENABLE"}" = if cfg.npcbots.classes.paladin then "1" else "0"; "${npcbot_env_key "CLASSES_HUNTER_ENABLE"}" = if cfg.npcbots.classes.hunter then "1" else "0"; "${npcbot_env_key "CLASSES_ROGUE_ENABLE"}" = if cfg.npcbots.classes.rogue then "1" else "0"; "${npcbot_env_key "CLASSES_PRIEST_ENABLE"}" = if cfg.npcbots.classes.priest then "1" else "0"; "${npcbot_env_key "CLASSES_DEATH_KNIGHT_ENABLE"}" = if cfg.npcbots.classes.deathKnight then "1" else "0"; "${npcbot_env_key "CLASSES_SHAMAN_ENABLE"}" = if cfg.npcbots.classes.shaman then "1" else "0"; "${npcbot_env_key "CLASSES_MAGE_ENABLE"}" = if cfg.npcbots.classes.mage then "1" else "0"; "${npcbot_env_key "CLASSES_WARLOCK_ENABLE"}" = if cfg.npcbots.classes.warlock then "1" else "0"; "${npcbot_env_key "CLASSES_DRUID_ENABLE"}" = if cfg.npcbots.classes.druid then "1" else "0"; "${npcbot_env_key "CLASSES_BLADEMASTER_ENABLE"}" = if cfg.npcbots.classes.blademaster then "1" else "0"; "${npcbot_env_key "CLASSES_OBSIDIAN_DESTROYER_ENABLE"}" = if cfg.npcbots.classes.obsidianDestroyer then "1" else "0"; "${npcbot_env_key "CLASSES_ARCHMAGE_ENABLE"}" = if cfg.npcbots.classes.archmage then "1" else "0"; "${npcbot_env_key "CLASSES_DREADLORD_ENABLE"}" = if cfg.npcbots.classes.dreadlord then "1" else "0"; "${npcbot_env_key "CLASSES_SPELL_BREAKER_ENABLE"}" = if cfg.npcbots.classes.spellBreaker then "1" else "0"; "${npcbot_env_key "CLASSES_DARK_RANGER_ENABLE"}" = if cfg.npcbots.classes.darkRanger then "1" else "0"; "${npcbot_env_key "CLASSES_NECROMANCER_ENABLE"}" = if cfg.npcbots.classes.necromancer then "1" else "0"; "${npcbot_env_key "CLASSES_SEA_WITCH_ENABLE"}" = if cfg.npcbots.classes.seaWitch then "1" else "0"; "${npcbot_env_key "CLASSES_CRYPT_LORD_ENABLE"}" = if cfg.npcbots.classes.cryptLord then "1" else "0"; "${ahbot_env_key "ENABLE_SELLER"}" = toString cfg.ahbot.EnableSeller; "${ahbot_env_key "ENABLE_BUYER"}" = toString cfg.ahbot.EnableBuyer; "${ahbot_env_key "ACCOUNT"}" = toString cfg.ahbot.Account; "${ahbot_env_key "GUID"}" = toString cfg.ahbot.GUID; "${ahbot_env_key "ITEMS_PER_CYCLE"}" = toString cfg.ahbot.ItemsPerCycle; "${ahbot_env_key "ELAPSING_TIME_CLASS"}" = toString cfg.ahbot.ElapsingTimeClass; "${ahbot_env_key "VENDOR_ITEMS"}" = toString cfg.ahbot.VendorItems; "${ahbot_env_key "VENDOR_TRADE_GOODS"}" = toString cfg.ahbot.VendorTradeGoods; "${ahbot_env_key "LOOT_ITEMS"}" = toString cfg.ahbot.LootItems; "${ahbot_env_key "LOOT_TRADE_GOODS"}" = toString cfg.ahbot.LootTradeGoods; "${ahbot_env_key "OTHER_ITEMS"}" = toString cfg.ahbot.OtherItems; "${ahbot_env_key "OTHER_TRADE_GOODS"}" = toString cfg.ahbot.OtherTradeGoods; "${ahbot_env_key "PROFESSION_ITEMS"}" = toString cfg.ahbot.ProfessionItems; }; serviceConfig = { Type = "simple"; User = "azerothcore"; Group = "azerothcore"; WorkingDirectory = "/etc/azerothcore"; Restart = "on-failure"; ExecStart = "${cfg.serverPackage}/bin/worldserver -c /etc/azerothcore/worldserver.conf"; }; }; azerothcore = rec { description = "AzerothCore"; wantedBy = [ "multi-user.target" ]; wants = [ "azerothcore-auth.service" "azerothcore-world.service" ]; after = wants; }; }; systemd.tmpfiles.rules = [ "d ${cfg.logDir} 1755 azerothcore azerothcore - -" "d ${cfg.tmpDir} 1755 azerothcore azerothcore - -" ]; services.mysql = { enable = true; package = lib.mkDefault pkgs.mysql80; ensureDatabases = [ cfg.auth.database cfg.world.database cfg.world.charactersDatabase ]; ensureUsers = [{ name = cfg.database.user; ensurePermissions = { "${cfg.auth.database}.*" = "ALL PRIVILEGES"; "${cfg.world.database}.*" = "ALL PRIVILEGES"; "${cfg.world.charactersDatabase}.*" = "ALL PRIVILEGES"; }; }]; }; }; }