本地新增活动时,配置表数据是如何被读取的

核心结论

配置表数据和服务端数据不是两份独立的东西,而是同一条数据链路:

1
2
3
4
5
6
7
你在 Excel 里填配置表
↓ 导表
生成 txt/lua 文件(本地)
↓ 服务端也读取同一份配置表(或服务端自己有对应配置)
服务端组装数据 → 通过 Ntf 推送给客户端
↓
客户端 Model 接收 → 存储 → View 读取

关键点:你在本地配置表填的数据(activityName、activityUIDetail、tagId 等),服务端也会读取,然后服务端把这些配置表字段和玩家的动态数据打包在一起,通过协议推送给客户端。


完整链路:从你填配置表到 View 显示

第 1 步:你填配置表

1
2
3
4
5
6
7
8
H_活动中心配置.xlsx 新增一行:
id: 30999
activityName: 春日兑换
activityUIDetail: UI_ActivityTemplate_ExchangeLinearView
tagId: 1
activityShopType: 142
backgroundUrl: spring_bg.astc
...

第 2 步:导表

导表后生成两个文件:

1
2
server/data/ActivityMainConfigForLetsGo_common.txt  ← 服务端读
Export/pbin/lua/ActivityMainConfigForLetsGo_common.lua ← 客户端本地读

第 3 步:服务端读取配置 + 组装动态数据 + 推送

服务端做了什么:

1
2
3
1. 服务端读取配置表 → 拿到 activityName, activityUIDetail, tagId, timeInfo... 等静态字段
2. 服务端查询玩家进度 → 拿到 任务完成状态, 积分, 奖励领取进度... 等动态数据
3. 合并成一条数据 → 通过 ActivityListAll_C2S_Msg 协议推给客户端

推送的数据长这样(一条活动数据):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
-- ↓↓↓ 来自配置表的静态字段 ↓↓↓
id = 30999,
activityName = "春日兑换",
activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView", -- ← 这个决定了加载哪个UMG
activityType = "ATLinearRedeem",
activityNameType = "ANTLinearRedeem",
tagId = 1,
activityShopType = {142},
backgroundUrl = {"spring_bg.astc", "spring_title.astc"},
timeInfo = { beginTime={...}, endTime={...} },
clientParams = {"每日任务", "兑换商店"},
activityTaskGroup = {570011, 570021},

-- ↓↓↓ 来自服务端的动态数据 ↓↓↓
-- 这些字段在不同活动类型中不同,由服务端运行时计算
-- 比如: 任务完成状态、积分、奖励领取进度等
}

第 4 步:客户端 Model 接收并存储

源码对应 ActivityModel_TabData.lua 第 160-180 行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
local function HandleActivityData(activityModel, callback)
-- 存储服务端推送的活动列表
ActivityModel_TabData.gameServerActivityList = ActivityModel_TabData.gameServerActivityInfo.activityLabel
-- ↑ 这就是服务端推下来的所有活动数据
-- 每条活动数据 = 配置表字段 + 玩家动态数据

-- 验证每条数据
for _, singleActivity in pairs(gameServerActivityList) do
ValidateActivityListData(singleActivity)
-- ↑ 验证: activityName 字段是否存在
end

-- 初始化活动数据(分类存储)
InitAllActivityData(activityModel)
end

源码对应 ActivityModel_TabData.lua 第 367-410 行 — 存储到映射表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function ActivityModel_TabData.InitActivityMap(activityModel)
ActivityModel_TabData.m_ActivityInfoMap = {} -- 按 tagId 分组
ActivityModel_TabData.m_ActivityInfoMapById = {} -- 按 activityId 索引

for i, v in ipairs(gameServerActivityList) do
-- v 就是服务端推下来的每条活动数据
-- v.id = 30999
-- v.activityName = "春日兑换"
-- v.activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView"
-- v.activityShopType = {142}
-- ... 包含所有配置表字段 + 动态数据

-- 存储: 按 ID 索引
m_ActivityInfoMapById[v.id] = v

-- 存储: 按 tagId 分组
for index, tagId in ipairs(v.tagIds or {v.tagId}) do
local activityInfo = DeepCopyTable(v)
table.insert(m_ActivityInfoMap[tagId], activityInfo)
end
end
end

第 5 步:View 通过 Model 读取

源码对应 UI_Activity_BaseView.lua 第 22-36 行:

1
2
3
4
5
6
7
function UI_Activity_BaseView:InitData()
-- 从 Model 读取活动数据
self.ActivityInfo = _MOE.Models.ActivityModel:GetActivityInfo(self.Data.tagId, self.Data.id)
-- ↑ 返回的就是服务端推送的那条数据
-- 包含: activityName, activityUIDetail, activityShopType, backgroundUrl, ...
-- 也包含: 任务状态, 积分, 奖励进度等动态数据
end

源码对应 ActivityModel_DataQuery.lua 第 34-50 行 — 查询函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
function ActivityModel_DataQuery.GetActivityInfo(activityModel, tagId, activityId)
local activityInfoMap = activityModel:GetActivityInfoMapByTagId()
-- ↑ 返回 m_ActivityInfoMap[tagId] 列表

local activityList = activityInfoMap[tagId]
if activityList then
for i, v in ipairs(activityList) do
if v.id == activityId then
return v -- ← 返回这条活动的完整数据
end
end
end
end

第 6 步:View 根据 activityUIDetail 加载 UMG

源码对应 UI_Events_Main.lua 第 810-819 行 + 第 1081 行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 1. 从活动数据中取出 activityUIDetail 字段
local activityUIDetail = v.activityUIDetail
-- v 就是服务端推送的活动数据
-- activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView" ← 你在配置表填的值!

tabInfo.UIName = activityUIDetail -- UMG 蓝图名

-- 2. 用这个 UMG 名加载 Widget
self.SubView = self:AddWidget(tabInfo.UIName, self.bp.w_nameSlot_Activity, info)
-- ↑ AddWidget 内部:
-- 1. 用 UMG 名加载 UE4 蓝图资产
-- 2. 从蓝图的 View 属性读取 Lua 路径
-- 3. 加载 Lua 模块
-- 4. 创建 Lua View 实例
-- 5. 调用 View:Open(info) ← info 就是 Data

配置表字段 → UMG 加载的完整对应关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
你在 Excel 配置表填:
activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView"
│
↓ 导表 → 服务端读取 → 推送给客户端
│
客户端 Model 收到:
v.activityUIDetail = "UI_ActivityTemplate_ExchangeLinearView"
│
↓ View 层取出
│
UI_Events_Main.lua:
tabInfo.UIName = "UI_ActivityTemplate_ExchangeLinearView"
│
↓ AddWidget(UIName, ...)
│
BaseViewClass.lua 第 1482-1496 行:
1. 先查 UIWndNameToLuaPath[UIName] → 找不到
2. 加载 UE4 蓝图资产 (用 UIName 作为资产名)
3. 读取蓝图的 View 属性 → 得到 Lua 路径
View = "Feature.System.Script.System.Activity.Public.Template.View.UI_ActivityTemplate_ExchangeLinearView"
4. requireLuaView(View) → 加载 Lua 文件
5. 创建实例 → Open(Data)
│
↓
UI_ActivityTemplate_ExchangeLinearView:OnOpen(Data)
→ 渲染界面

如果服务端没推送怎么办(本地 fallback)

ActivityModel_DataQuery.lua 第 72-81 行有一个兜底函数:

1
2
3
4
5
6
7
8
9
10
function ActivityModel_DataQuery.GetActInfoOrCfgByActId(activityModel, activityId)
-- 1. 先从服务端推送的数据中找
local actInfo = GetInfoByActivityId(activityModel, activityId)
if not actInfo then
-- 2. 找不到 → 直接读本地配置表
actInfo = _MOE.Config.ActivityMainConfig:GetDataByKey(activityId)
-- ↑ 这就是直接从导表后的 lua 文件读
end
return actInfo
end

⚠️ 但注意:基类 InitData() 用的是 GetActivityInfo()(走服务端数据),不是 GetActInfoOrCfgByActId()(带 fallback)。所以如果服务端没推送这个活动,ActivityInfo 就是 nil。


完整图:你的配置表数据怎么到 View 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌──────────────────────────────────────────────────────────────────┐
│ 配置表数据 → 服务端 → Model → View 的完整链路 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 你在 Excel 填: │
│ id=30999, activityUIDetail="UI_ActivityTemplate_ExchangeLinearView",│
│ activityName="春日兑换", tagId=1, activityShopType=142, ... │
│ │ │
│ ↓ 导表 │
│ 生成 txt + lua 文件 │
│ │ │
│ ↓ 服务端也读这份配置表 (或服务端有自己的配置系统同步) │
│ 服务端组装: │
│ { id=30999, │
│ activityName="春日兑换", ← 来自配置表 │
│ activityUIDetail="UI_ActivityTemplate_...", ← 来自配置表 │
│ tagId=1, ← 来自配置表 │
│ activityShopType={142}, ← 来自配置表 │
│ backgroundUrl={"spring_bg.astc"},← 来自配置表 │
│ timeInfo={...}, ← 来自配置表 │
│ taskProgress={570011=已完成}, ← 服务端运行时计算 │
│ score=30, ← 服务端运行时计算 │
│ rewardStatus={reward1=已领}, ← 服务端运行时计算 │
│ } │
│ │ │
│ ↓ 通过 ActivityListAll_C2S_Msg 协议推送 │
│ 客户端 Model 接收: │
│ m_ActivityInfoMapById[30999] = { 整条数据 } │
│ │ │
│ ↓ View: GetActivityInfo(tagId, activityId) │
│ View 拿到 ActivityInfo: │
│ self.ActivityInfo = { 整条数据 } │
│ ├─ activityName → 写入 w_txt_title (标题文本) │
│ ├─ backgroundUrl → 加载到 w_img_Activity_BG (背景图) │
│ ├─ activityUIDetail → 已用于加载 UMG (在 AddWidget 时) │
│ ├─ activityShopType → 用于查询商城商品 (RefreshExchangeInfo) │
│ ├─ activityTaskGroup → 用于查询任务列表 (TaskView) │
│ ├─ taskProgress → 判断任务是否完成, 显示打勾 │
│ ├─ score → 显示积分数字 │
│ └─ rewardStatus → 判断领奖按钮是否可点 │
│ │
└──────────────────────────────────────────────────────────────────┘

一句话总结

你在配置表填的每个字段(activityUIDetail、activityName、tagId…),服务端都会读取并和玩家动态数据一起打包推送。客户端 Model 收到后统一存储,View 通过 GetActivityInfo 读取。其中 activityUIDetail 字段决定了加载哪个 UMG 蓝图和 Lua 脚本,其他字段用于界面渲染和业务逻辑。


本文从 LearnByCompany 原始文档 自动同步。