How to Use#

下面我们将介绍如何使用这个项目. 我们假设我们刚来到一个 WotLK 巫妖王之怒版本的服务器, 我们建立了 10 个游戏账号, 每个账号上有 1 个角色. 每个职业我们都有一个角色.

Note

这个项目支持多个不同的 WOW 版本, 因为它们的客户端 WTF 目录结构大同小异. 虽然这篇文档是以人气较高的 WotLK 版本为例, 但是你在使用其他版本时依然可以参考本文.

Note

wow_wtf/tests/exp03_wotlk 目录有一个完整的如何使用这个库项目来管理你的 WTF 配置的例子. 请在阅读本文时跟这个例子互相印证.

1. Account and Character Configuration#

Note

这部分的功能由另一个库 wow_acc 提供. 你可以参考 这篇文档 来了解如何枚举你的账号和角色.

首先你要枚举你的账号和角色.

我们在 acc_dataset.yml 文件中定义了我们的游戏账号和角色.

wow_wtf/tests/exp03_wotlk/acc_dataset.yml
 1acc01:
 2  realm1:
 3    - mywarrior
 4acc02:
 5  realm1:
 6    - mypaladin
 7acc03:
 8  realm1:
 9    - mydk
10acc04:
11  realm1:
12    - myhunter
13acc05:
14  realm1:
15    - myshaman
16acc06:
17  realm1:
18    - myrogue
19acc07:
20  realm1:
21    - mydruid
22acc08:
23  realm1:
24    - mywarlock
25acc09:
26  realm1:
27    - mymage
28acc10:
29  realm1:
30    - mypriest

然后我们编写了一个 acc_dataset.py 脚本用于生成对所有账号和角色的 enum 模块.

wow_wtf/tests/exp03_wotlk/acc_dataset.py
 1# -*- coding: utf-8 -*-
 2
 3from pathlib_mate import Path
 4from wow_acc.api import Dataset
 5
 6dir_here = Path(__file__).absolute().parent
 7ds = Dataset.from_yaml(dir_here.joinpath("acc_dataset.yml").read_text())
 8
 9if __name__ == "__main__":
10    path = dir_here.joinpath("acc_enum.py")
11    content = ds.to_module(
12        import_line="from wow_wtf.tests.exp03_wotlk.acc_dataset import ds",
13    )
14    path.write_text(content)

现在我们就得到了一个可以用 Python 变量引用我们的账号和角色的 acc_enum.py 模块了.

wow_wtf/tests/exp03_wotlk/acc_enum.py
 1# -*- coding: utf-8 -*-
 2
 3from wow_wtf.tests.exp03_wotlk.acc_dataset import ds
 4
 5
 6# fmt: off
 7class AccountEnum:
 8    acc01 = ds.accounts["acc01"]
 9    acc02 = ds.accounts["acc02"]
10    acc03 = ds.accounts["acc03"]
11    acc04 = ds.accounts["acc04"]
12    acc05 = ds.accounts["acc05"]
13    acc06 = ds.accounts["acc06"]
14    acc07 = ds.accounts["acc07"]
15    acc08 = ds.accounts["acc08"]
16    acc09 = ds.accounts["acc09"]
17    acc10 = ds.accounts["acc10"]
18
19
20class RealmEnum:
21    acc01_realm1 = ds.accounts["acc01"].realms_mapper["realm1"]
22    acc02_realm1 = ds.accounts["acc02"].realms_mapper["realm1"]
23    acc03_realm1 = ds.accounts["acc03"].realms_mapper["realm1"]
24    acc04_realm1 = ds.accounts["acc04"].realms_mapper["realm1"]
25    acc05_realm1 = ds.accounts["acc05"].realms_mapper["realm1"]
26    acc06_realm1 = ds.accounts["acc06"].realms_mapper["realm1"]
27    acc07_realm1 = ds.accounts["acc07"].realms_mapper["realm1"]
28    acc08_realm1 = ds.accounts["acc08"].realms_mapper["realm1"]
29    acc09_realm1 = ds.accounts["acc09"].realms_mapper["realm1"]
30    acc10_realm1 = ds.accounts["acc10"].realms_mapper["realm1"]
31
32
33class CharacterEnum:
34    acc01_realm1_mywarrior = ds.accounts["acc01"].realms_mapper["realm1"].characters_mapper["mywarrior"]
35    acc02_realm1_mypaladin = ds.accounts["acc02"].realms_mapper["realm1"].characters_mapper["mypaladin"]
36    acc03_realm1_mydk = ds.accounts["acc03"].realms_mapper["realm1"].characters_mapper["mydk"]
37    acc04_realm1_myhunter = ds.accounts["acc04"].realms_mapper["realm1"].characters_mapper["myhunter"]
38    acc05_realm1_myshaman = ds.accounts["acc05"].realms_mapper["realm1"].characters_mapper["myshaman"]
39    acc06_realm1_myrogue = ds.accounts["acc06"].realms_mapper["realm1"].characters_mapper["myrogue"]
40    acc07_realm1_mydruid = ds.accounts["acc07"].realms_mapper["realm1"].characters_mapper["mydruid"]
41    acc08_realm1_mywarlock = ds.accounts["acc08"].realms_mapper["realm1"].characters_mapper["mywarlock"]
42    acc09_realm1_mymage = ds.accounts["acc09"].realms_mapper["realm1"].characters_mapper["mymage"]
43    acc10_realm1_mypriest = ds.accounts["acc10"].realms_mapper["realm1"].characters_mapper["mypriest"]
44# fmt: on

更进一步地, 你可以按照逻辑对这些账号和角色的 enum 进行分组, 这样在之后引用的时候就可以批量引用而不用一个个的手打了. acc_group.py 模块是一个很好的例子.

wow_wtf/tests/exp03_wotlk/acc_group.py
 1# -*- coding: utf-8 -*-
 2
 3"""
 4This module can help you organize your enum into group, made it easier to
 5construct mappings later.
 6"""
 7
 8from wow_wtf.api import get_values
 9from wow_wtf.tests.exp03_wotlk.acc_enum import AccountEnum, CharacterEnum
10
11
12# ==============================================================================
13# START of manual editing
14# ==============================================================================
15class AccountGroupEnum:
16    all_accounts = get_values(AccountEnum)
17
18
19class CharacterGroupEnum:
20    all_characters = get_values(CharacterEnum)
21
22    multiboxer_master_paladin = [
23        CharacterEnum.acc02_realm1_mypaladin,
24    ]
25    multiboxer_master_non_paladin = [
26        CharacterEnum.acc03_realm1_mydk,
27    ]
28    multiboxer_slave_paladin = []
29    multiboxer_slave_non_paladin = (
30        all_characters.difference(multiboxer_master_paladin)
31        .difference(multiboxer_master_non_paladin)
32        .difference(multiboxer_slave_paladin)
33    )
34
35    warrior_and_dk = [
36        CharacterEnum.acc01_realm1_mywarrior,
37        CharacterEnum.acc03_realm1_mydk,
38    ]
39    non_warrior_and_dk = all_characters.difference(warrior_and_dk)
40
41
42# ==============================================================================
43# END of manual editing
44# ==============================================================================

2. Craft Your WTF Config Template#

下面我们就需要准备好一些 WTF 配置模板文件了. 一般你是通过登录游戏, 将游戏内的配置调整到你满意的状态, 然后将对应的配置文件拷贝出来, 作为你的配置模板.

exp03_wotlk 目录下有一些文件夹. 每个文件夹都保存了你的一些 WTF 配置模板文件. 例如在 01_client_config 中就保存了用多个不同的分辨率, 图像和音频来运行你的客户端的配置文件. 这个目录的结构必须跟 to_module() 中的定义严格一致, 不然后续的自动化操作就无法进行了.

我建议你查看 exp03_wotlk 下面那些以数字开头的文件夹中的配置文件, 了解如何组织你自己的配置文件. 特别是对于 AddOns 插件配置的 lua 文件部分. 例如 13_account_saved_variables/AtlasLoot.lua 文件, 它不是简单的把你客户端中的 lua 文件拷贝过来就可以的, 而是要用 jinja2 模板语言来将原始 lua 文件模板化, 以便随着你的 account 和 character 数量的增加而动态更新.

wow_wtf/tests/exp03_wotlk/13_account_saved_variables/AtlasLoot.lua
 1AtlasLootOptions = nil
 2AtlasLootDB = {
 3	["profileKeys"] = {
 4		{%- for character in account.characters %}
 5		["{{ character.titled_character_name }} - {{ character.realm_name }}"] = "MyDefault",
 6		{%- endfor %}
 7	},
 8	["profiles"] = {
 9		["MyDefault"] = {
10			["CraftingLink"] = 2,
11			["ItemIDs"] = 1,
12			["AllLinks"] = false,
13			["AtlasLootVersion"] = "51103",
14		},
15	},
16}
17AtlasLootWishList = {
18	["Options"] = {
19	},
20	["Shared"] = {
21	},
22	["Own"] = {
23	},
24}

3. Generate You WTF Config Enum Module#

1. Account and Character Configuration 类似, 我们也要将我们的配置文件数据转化成一个 Python 模块, 使得每一个配置文件都是一个 enum.

我们编写了一个 wtf_dataset.py 脚本用于生成对所有配置文件的 enum 模块.

wow_wtf/tests/exp03_wotlk/wtf_dataset.py
 1# -*- coding: utf-8 -*-
 2
 3from pathlib_mate import Path
 4from wow_wtf.api import exp03_wotlk
 5
 6dir_root = Path.dir_here(__file__)
 7if __name__ == "__main__":
 8    content = exp03_wotlk.to_module(
 9        dir_root=dir_root,
10        import_dir_root_line="from .wtf_dataset import dir_root",
11    )
12    dir_root.joinpath("wtf_enum.py").write_text(content, encoding="utf-8")

现在我们就得到了一个可以用 Python 变量引用我们的配置文件的 wtf_enum.py 模块了.

wow_wtf/tests/exp03_wotlk/wtf_enum.py
 1# -*- coding: utf-8 -*-
 2
 3from wow_wtf.tests.exp03_wotlk.wtf_dataset import dir_root
 4# fmt: off
 5
 6class ClientConfigEnum:
 7    f_1176_664_minimal_graphic_sound = dir_root.joinpath("01_client_config", "1176-664-minimal-graphic-sound.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/01_client_config/1176-664-minimal-graphic-sound.txt
 8    f_1600_900_minimal_graphic_sound = dir_root.joinpath("01_client_config", "1600-900-minimal-graphic-sound.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/01_client_config/1600-900-minimal-graphic-sound.txt
 9    f_1920_1080_max_graphic_sound = dir_root.joinpath("01_client_config", "1920-1080-max-graphic-sound.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/01_client_config/1920-1080-max-graphic-sound.txt
10    f_1920_1080_minimal_graphic_sound = dir_root.joinpath("01_client_config", "1920-1080-minimal-graphic-sound.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/01_client_config/1920-1080-minimal-graphic-sound.txt
11    f_3840_2160_max_graphic_sound = dir_root.joinpath("01_client_config", "3840-2160-max-graphic-sound.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/01_client_config/3840-2160-max-graphic-sound.txt
12
13
14class AccountUserInterfaceEnum:
15    default = dir_root.joinpath("11_account_user_interface", "default.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/11_account_user_interface/default.txt
16
17
18class AccountMacrosEnum:
19    pass
20
21
22class AccountSavedVariablesEnum:
23    AtlasLoot = dir_root.joinpath("13_account_saved_variables", "AtlasLoot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/AtlasLoot.lua
24    Combuctor = dir_root.joinpath("13_account_saved_variables", "Combuctor.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Combuctor.lua
25    Dominos = dir_root.joinpath("13_account_saved_variables", "Dominos.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Dominos.lua
26    HealBot = dir_root.joinpath("13_account_saved_variables", "HealBot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/HealBot.lua
27    Mappy = dir_root.joinpath("13_account_saved_variables", "Mappy.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Mappy.lua
28    MobInfo2 = dir_root.joinpath("13_account_saved_variables", "MobInfo2.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/MobInfo2.lua
29    Omen = dir_root.joinpath("13_account_saved_variables", "Omen.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Omen.lua
30    PallyPower = dir_root.joinpath("13_account_saved_variables", "PallyPower.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/PallyPower.lua
31    Parrot = dir_root.joinpath("13_account_saved_variables", "Parrot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Parrot.lua
32    Postal = dir_root.joinpath("13_account_saved_variables", "Postal.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Postal.lua
33    Quartz = dir_root.joinpath("13_account_saved_variables", "Quartz.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Quartz.lua
34    RatingBuster = dir_root.joinpath("13_account_saved_variables", "RatingBuster.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/RatingBuster.lua
35    Skada = dir_root.joinpath("13_account_saved_variables", "Skada.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/Skada.lua
36    SlideBar = dir_root.joinpath("13_account_saved_variables", "SlideBar.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/SlideBar.lua
37    oCC = dir_root.joinpath("13_account_saved_variables", "oCC.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/13_account_saved_variables/oCC.lua
38
39
40class CharacterUserInterfaceEnum:
41    default = dir_root.joinpath("21_character_user_interface", "default.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/21_character_user_interface/default.txt
42
43
44class CharacterChatEnum:
45    default = dir_root.joinpath("22_character_chat", "default.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/22_character_chat/default.txt
46
47
48class CharacterKeybindingsEnum:
49    default = dir_root.joinpath("23_character_keybindings", "default.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/23_character_keybindings/default.txt
50    warrior_and_dk = dir_root.joinpath("23_character_keybindings", "warrior-and-dk.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/23_character_keybindings/warrior-and-dk.txt
51
52
53class CharacterLayoutEnum:
54    default = dir_root.joinpath("24_character_layout", "default.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/24_character_layout/default.txt
55
56
57class CharacterAddonsEnum:
58    f_01_multiboxer_master_paladin = dir_root.joinpath("25_character_addons", "01-multiboxer-master-paladin.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/25_character_addons/01-multiboxer-master-paladin.txt
59    f_02_multiboxer_master_non_paladin = dir_root.joinpath("25_character_addons", "02-multiboxer-master-non-paladin.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/25_character_addons/02-multiboxer-master-non-paladin.txt
60    f_03_multiboxer_slave_paladin = dir_root.joinpath("25_character_addons", "03-multiboxer-slave-paladin.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/25_character_addons/03-multiboxer-slave-paladin.txt
61    f_04_multiboxer_slave_non_paladin = dir_root.joinpath("25_character_addons", "04-multiboxer-slave-non-paladin.txt") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/25_character_addons/04-multiboxer-slave-non-paladin.txt
62
63
64class CharacterMacrosEnum:
65    pass
66
67
68class CharacterSavedVariablesEnum:
69    Atlas = dir_root.joinpath("27_character_saved_variables", "Atlas.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/27_character_saved_variables/Atlas.lua
70    AtlasLoot = dir_root.joinpath("27_character_saved_variables", "AtlasLoot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/27_character_saved_variables/AtlasLoot.lua
71    Vendomatic = dir_root.joinpath("27_character_saved_variables", "Vendomatic.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/27_character_saved_variables/Vendomatic.lua
72    paladin__Healbot = dir_root.joinpath("27_character_saved_variables", "paladin", "Healbot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/27_character_saved_variables/paladin/Healbot.lua
73    priest__Healbot = dir_root.joinpath("27_character_saved_variables", "priest", "Healbot.lua") # file:///Users/sanhehu/Documents/GitHub/wow_wtf-project/wow_wtf/tests/exp03_wotlk/27_character_saved_variables/priest/Healbot.lua
74
75
76# fmt: on

更进一步地, 你可以按照逻辑对这些 enum 进行分组, 这样在之后引用的时候就可以批量引用而不用一个个的手打了. wtf_group.py 模块是一个很好的例子.

wow_wtf/tests/exp03_wotlk/wtf_group.py
 1# -*- coding: utf-8 -*-
 2
 3"""
 4This module can help you organize your enum into group, made it easier to
 5construct mappings later.
 6"""
 7
 8from wow_wtf.api import get_values
 9from wow_wtf.tests.exp03_wotlk.wtf_enum import (
10    ClientConfigEnum,
11    AccountUserInterfaceEnum as AuiEnum,
12    AccountMacrosEnum as AmEnum,
13    AccountSavedVariablesEnum as AsvEnum,
14    CharacterUserInterfaceEnum as CuiEnum,
15    CharacterChatEnum as CcEnum,
16    CharacterKeybindingsEnum as CkEnum,
17    CharacterLayoutEnum as ClEnum,
18    CharacterAddonsEnum as CaEnum,
19    CharacterMacrosEnum as CmEnum,
20    CharacterSavedVariablesEnum as CsvEnum,
21)
22
23
24# ==============================================================================
25# START of manual editing
26# ==============================================================================
27class AsvGroupEnum:
28    common = get_values(AsvEnum)
29
30
31class CsvGroupEnum:
32    common = [
33        CsvEnum.Atlas,
34        CsvEnum.AtlasLoot,
35        CsvEnum.Vendomatic,
36    ]
37
38
39# ==============================================================================
40# End of manual editing
41# ==============================================================================

4. Define Your Account / Character and WTF Config Mapping#

我们有了 Account / Character 的 Enum, 也有了 WTF Config 的 Enum, 下面就是要指定哪些账号和角色应该使用哪些配置了. 这个映射关系叫做 mapping.

我们需要编写一个 wtf_mapping.py 模块, 里面导入了我们之前定义的 acc_enum.pywtf_enum.py Enum.

wow_wtf/tests/exp03_wotlk/wtf_mapping.py
  1# -*- coding: utf-8 -*-
  2
  3from pathlib_mate import Path
  4from wow_wtf.api import get_values, concat_lists, exp03_wotlk
  5from wow_wtf.tests.exp03_wotlk.acc_enum import (
  6    AccountEnum as AccEnum,
  7    CharacterEnum as CharEnum,
  8)
  9from wow_wtf.tests.exp03_wotlk.acc_group import (
 10    AccountGroupEnum as AccGrpEnum,
 11    CharacterGroupEnum as CharGrpEnum,
 12)
 13from wow_wtf.tests.exp03_wotlk.wtf_enum import (
 14    ClientConfigEnum,
 15    AccountUserInterfaceEnum as AuiEnum,
 16    AccountMacrosEnum as AmEnum,
 17    AccountSavedVariablesEnum as AsvEnum,
 18    CharacterUserInterfaceEnum as CuiEnum,
 19    CharacterChatEnum as CcEnum,
 20    CharacterKeybindingsEnum as CkEnum,
 21    CharacterLayoutEnum as ClEnum,
 22    CharacterAddonsEnum as CaEnum,
 23    CharacterMacrosEnum as CmEnum,
 24    CharacterSavedVariablesEnum as CsvEnum,
 25)
 26from wow_wtf.tests.exp03_wotlk.wtf_group import (
 27    AsvGroupEnum,
 28    CsvGroupEnum,
 29)
 30
 31Client = exp03_wotlk.Client
 32AccMap = exp03_wotlk.AccLvlMapping
 33CharMap = exp03_wotlk.CharLvlMapping
 34WtfMapping = exp03_wotlk.WtfMapping
 35
 36dir_here = Path.dir_here(__file__)
 37dir_game_client = dir_here.joinpath("world_of_warcraft_enUS")
 38
 39client = exp03_wotlk.Client(
 40    locale="enUS",
 41    dir=dir_game_client,
 42)
 43all_accounts = AccGrpEnum.all_accounts
 44all_characters = CharGrpEnum.all_characters
 45
 46# ==============================================================================
 47# START of manual editing
 48# ==============================================================================
 49# ------------------------------------------------------------------------------
 50# client_config
 51# ------------------------------------------------------------------------------
 52# client_config = ClientConfigEnum.f_1176_664_minimal_graphic_sound
 53# client_config = ClientConfigEnum.f_1600_900_minimal_graphic_sound
 54client_config = ClientConfigEnum.f_1920_1080_max_graphic_sound
 55# client_config = ClientConfigEnum.f_1920_1080_minimal_graphic_sound
 56# client_config = ClientConfigEnum.f_3840_2160_max_graphic_sound
 57
 58# ------------------------------------------------------------------------------
 59# acc_user_interface
 60# ------------------------------------------------------------------------------
 61# The AccLvlMapping.make_many and CharLvlMapping.make_many are very helpful
 62acc_user_interface = AccMap.make_many(all_accounts, AuiEnum.default)
 63
 64# ------------------------------------------------------------------------------
 65# acc_saved_variables
 66# ------------------------------------------------------------------------------
 67acc_saved_variables = AccMap.make_many(all_accounts, AsvGroupEnum.common)
 68
 69# ------------------------------------------------------------------------------
 70# char_user_interface
 71# ------------------------------------------------------------------------------
 72char_user_interface = CharMap.make_many(all_characters, CuiEnum.default)
 73
 74# ------------------------------------------------------------------------------
 75# char_chat
 76# ------------------------------------------------------------------------------
 77char_chat = CharMap.make_many(all_characters, CcEnum.default)
 78
 79# ------------------------------------------------------------------------------
 80# char_keybinding
 81# ------------------------------------------------------------------------------
 82war_and_dk_chars = [
 83    CharEnum.acc01_realm1_mywarrior,
 84    CharEnum.acc03_realm1_mydk,
 85]
 86char_keybinding = CharMap.make_many(
 87    all_characters.difference(war_and_dk_chars), CkEnum.default
 88) + CharMap.make_many(war_and_dk_chars, CkEnum.warrior_and_dk)
 89
 90# ------------------------------------------------------------------------------
 91# char_layout
 92# ------------------------------------------------------------------------------
 93char_layout = CharMap.make_many(all_characters, ClEnum.default)
 94
 95# ------------------------------------------------------------------------------
 96# char_addons
 97# ------------------------------------------------------------------------------
 98char_addons = concat_lists(
 99    CharMap.make_many(
100        CharGrpEnum.multiboxer_master_paladin,
101        CaEnum.f_01_multiboxer_master_paladin,
102    ),
103    CharMap.make_many(
104        CharGrpEnum.multiboxer_master_non_paladin,
105        CaEnum.f_02_multiboxer_master_non_paladin,
106    ),
107    CharMap.make_many(
108        CharGrpEnum.multiboxer_slave_paladin,
109        CaEnum.f_03_multiboxer_slave_paladin,
110    ),
111    CharMap.make_many(
112        CharGrpEnum.multiboxer_slave_non_paladin,
113        CaEnum.f_04_multiboxer_slave_non_paladin,
114    ),
115)
116
117# ------------------------------------------------------------------------------
118# char_saved_variables
119# ------------------------------------------------------------------------------
120char_saved_variables = concat_lists(
121    CharMap.make_many(
122        all_characters,
123        CsvGroupEnum.common,
124    ),
125    [
126        CharMap(char=CharEnum.acc02_realm1_mypaladin, file=CsvEnum.paladin__Healbot),
127        CharMap(char=CharEnum.acc10_realm1_mypriest, file=CsvEnum.priest__Healbot),
128    ],
129)
130
131# ==============================================================================
132# END of manual editing
133# ==============================================================================
134# ------------------------------------------------------------------------------
135# wtf_mapping
136# ------------------------------------------------------------------------------
137wtf_mapping = exp03_wotlk.WtfMapping(
138    client=client,
139    all_accounts=all_accounts,
140    all_characters=all_characters,
141    client_config=client_config,
142    acc_user_interface=acc_user_interface,
143    # acc_macros=acc_macros,
144    acc_saved_variables=acc_saved_variables,
145    char_user_interface=char_user_interface,
146    char_chat=char_chat,
147    char_keybinding=char_keybinding,
148    char_layout=char_layout,
149    char_addons=char_addons,
150    # char_macros=char_macros,
151    char_saved_variables=char_saved_variables,
152)

接下来就是定义 WtfMapping 对象, 它是一个 mapping 数据的容器. 里面定义了例如针对 account 级别的 UI 设置, 哪些账号使用哪些配置文件, 以及针对 character 级别的 UI 设置, 哪些角色使用哪些配置文件, 等等.

wow_wtf 库还提供了一些函数能让你更方便地定义这些 mapping 数据. 例如:

  • make_many()make_many() 方法可以方便地让你将多个账户或角色和多个配置文件建立映射关系.

  • get_values() 方法可以方便地让你获得一个 enum 类的所有 member 的集合. 注意这里是集合, 也就是说你可以用 difference (取差异), intersection (取交集), union (取并集) 这些集合操作进行筛选.

  • concat_lists() 方法可以方便地让你将多个 list 连接起来.

5. Apply WTF Configuration#

有了 WtfMapping 对象之后, 你就可以将你的配置批量应用到你的客户端了. 你可以使用下面这些方法来将你的配置写入到你的客户端的 WTF 目录中:

wtf_apply.py 文件是一个例子, 它展示了如何将我们的配置应用到我们的客户端.

wow_wtf/tests/exp03_wotlk/wtf_apply.py
 1# -*- coding: utf-8 -*-
 2
 3from wow_wtf.tests.exp03_wotlk.wtf_mapping import wtf_mapping
 4
 5real_run = True
 6
 7wtf_mapping.apply_client_config(real_run)
 8
 9wtf_mapping.apply_account_user_interface(real_run)
10wtf_mapping.apply_account_saved_variables(real_run)
11
12wtf_mapping.apply_character_user_interface(real_run)
13wtf_mapping.apply_character_chat(real_run)
14wtf_mapping.apply_character_keybinding(real_run)
15wtf_mapping.apply_character_layout(real_run)
16wtf_mapping.apply_character_addons(real_run)
17wtf_mapping.apply_character_saved_variables(real_run)
18
19# wtf_mapping.apply_account_macros(real_run)  ## MOST LIKELY NOT NEEDED, we use SDM AddOns to manage macro
20# wtf_mapping.apply_character_macros(real_run) ## MOST LIKELY NOT NEEDED, we use SDM AddOns to manage macro

How to debug before you apply

由于 apply 操作会覆盖 WTF 中已有的文件. 所以在你还不确定你的代码是否正确之前, 你希望能提前进行验证, 或是确保出现问题时能够回滚到之前的状态. 我推荐下面几种方法:

  1. 备份你客户端中的 WTF 文件夹, 以备不时之需.

  2. 在定义 Client 时将目录指向到一个临时目录, 而不是真正的魔兽世界客户端目录. 这样你可以检查生成后的文件, 然后拷贝一小部分到客户端中看看是否惯用.

  3. 在调用 apply_xyz(...) 这些方法时, 将 real_run 参数设为 False. 这样它只会渲染最终要写入的内容而不会真正写入. 这样可以确定你至少你的 lua 文件没有问题.

Important

有些配置是无法通过回滚 WTF 文件来恢复的. 例如 macro 宏命令, 以及你的动作条数据在你每次进入游戏的时候会将数据保存在服务器端. 而如果你覆盖了原来的 macro 之后又登录游戏导致宏命令丢失或是动作条按钮丢失, 那么你即使回滚了 WTF 文件你再次登录游戏时也无法恢复到之前的状态.

所以我个人不会用这个工具来管理 macros (虽然它可以), 我更倾向于用 SDM (SupderDopeMacro) 和 MySlots 这样的插件来管理我的宏命令和动作条.

Manage Multiple Servers and Mappings#

本节介绍了在你同时玩多个服务器的时候, 有多个客户端, 有多套人物角色和配置的组合的时候, 如何组织你的文件目录来管理数量庞大的 WTF 配置.

首先我们要知道一些规范:

  • 用不同的客户端玩不同的服务器. 例如你在用一个客户端玩 2 个私服. 那么建议把你的游戏客户端拷贝一份, 每一个客户端玩不同的私服. 因为不同的私服的服务器名和游戏角色名可能会出现冲突.

  • 不要用多个 mapping 来分别管理一个账号下的不同角色. 举例来说, 你一个账号下有 10 个角色, 你用一套 mapping 管理其中的 5 个, 另一套 mapping 管理另外 5 个. 这样是不可以的. 因为很多插件的 lua 文件需要知道你账号下全部的角色名. 如果你切换到其中的 5 个, 那么另外 5 个角色名对于插件来说就是不可知的, 就会损害另外 5 个角色的配置.

  • 跟上一条对应, 你可以用多个 mapping 来管理一个服务器上的不同账号. 例如用一套 mapping 管理 5 个账号, 用另一套 mapping 管理另外 5 个账号, 这样做是可以的.

根据这些规范, 我们可以创建下面的目录结构. 在 workspace 下的每一个子目录都应该是一套独立的 mapping. 子目录的文件夹名可以是 ${server_name}_${description}. 其中 server_name 是服务器的名字, description 是你这套 mapping 的描述. 而这些子目录的结构就跟 exp03_wotlk 目录下的结构一样了.

workspace/
workspace/myserver1_mapping1/
workspace/myserver1_mapping1/01_client_config/
workspace/myserver1_mapping1/11_account_user_interface/
workspace/myserver1_mapping1/12_account_macros/
workspace/myserver1_mapping1/13_account_saved_variables/
workspace/myserver1_mapping1/21_character_user_interface/
workspace/myserver1_mapping1/22_character_chat/
workspace/myserver1_mapping1/23_character_keybindings/
workspace/myserver1_mapping1/24_character_layout/
workspace/myserver1_mapping1/25_character_addons/
workspace/myserver1_mapping1/26_character_macros/
workspace/myserver1_mapping1/27_character_saved_variables/
workspace/myserver1_mapping1/acc_dataset.py
workspace/myserver1_mapping1/acc_dataset.yml
workspace/myserver1_mapping1/acc_enum.py
workspace/myserver1_mapping1/README.rst
workspace/myserver1_mapping1/wtf_apply.py
workspace/myserver1_mapping1/wtf_dataset.py
workspace/myserver1_mapping1/wtf_enum.py
workspace/myserver1_mapping1/wtf_mapping.py
workspace/myserver1_mapping1/
workspace/myserver1_mapping2/
workspace/myserver2_mapping1/
workspace/myserver2_mapping2/