小助手内嵌 GM 页:网页重定向 + 物品分页 + 装备强化增幅发放

- 90CN.dll 新增 IE 控件接管,把腾讯小助手页面重定向到本地 GM 页
- gmweb:物品列表改分页(每页 10/20/50,列表固定 6 行高)
- 发放走 grant-options,支持强化 0-31、增幅属性+等级、锻造 0-8、品质
- 服务端补齐 amplify_level:等级统一写入 ext_data0/packed_flag_byte/reinforce/upgrade_level
- 增幅与强化互斥校验,避免写出客户端无法创建的装备
This commit is contained in:
hanfuit committed 2026-11-05 12:20:46 +08:00
1 parent 6e570fe820
commit 18e98ca7c9
13 files changed
+1206 -11

No files matched your search

@@ -825,6 +825,7 @@ func registerGMAdminRoutes(env *appkit.Env, service *dnfbridge.Service) {
ExpireDays int `json:"expire_days"`
Reinforce int `json:"reinforce"`
AmplifyType int `json:"amplify_type"`
AmplifyLevel int `json:"amplify_level"`
EquipmentQuality string `json:"equipment_quality"`
Forging int `json:"forging"`
EmblemItemIDs []uint32 `json:"emblem_item_ids"`
@@ -845,6 +846,7 @@ func registerGMAdminRoutes(env *appkit.Env, service *dnfbridge.Service) {
ExpireDays: request.ExpireDays,
Reinforce: request.Reinforce,
AmplifyType: request.AmplifyType,
AmplifyLevel: request.AmplifyLevel,
EquipmentQuality: request.EquipmentQuality,
Forging: request.Forging,
EmblemItemIDs: request.EmblemItemIDs,
@@ -10,8 +10,8 @@ const (
boundMovieSHA256 = "D62E3623C39CD9FCED5F6C4C7F5E6E18BEB69EB269ABF5D713EDE08EE8C926A1"
boundMovieBytes = int64(22846532)
boundDllSHA256 = "C7BE377CC2795612074CAE70F071523E19E32964CED355762A2B59F48247071A"
boundDllBytes = int64(1215488)
boundDllSHA256 = "E947A0C2E36EA24C3F2F557E7D3207C76F9B6E63192E2555B6D4B73593EE17F1"
boundDllBytes = int64(1223168)
)
+19 -5
View File
@@ -324,6 +324,7 @@ type GMMailOptions struct {
ExpireDays int `json:"expire_days,omitempty"`
Reinforce int `json:"reinforce,omitempty"`
AmplifyType int `json:"amplify_type,omitempty"`
AmplifyLevel int `json:"amplify_level,omitempty"`
EquipmentQuality string `json:"equipment_quality,omitempty"`
Forging int `json:"forging,omitempty"`
EmblemItemIDs []uint32 `json:"emblem_item_ids,omitempty"`
@@ -2288,9 +2289,15 @@ func (s *Service) GMSendItemMailWithOptions(
return GMMailDeliveryResult{}, fmt.Errorf("%w: equipment mail count must be 1", ErrGMInvalidRequest)
}
quality := strings.ToLower(strings.TrimSpace(options.EquipmentQuality))
if options.ExpireDays < 0 || options.ExpireDays > 3650 || options.Reinforce < 0 || options.Reinforce > 31 || options.AmplifyType < 0 || options.AmplifyType > 4 || options.Forging < 0 || options.Forging > 8 {
if options.ExpireDays < 0 || options.ExpireDays > 3650 || options.Reinforce < 0 || options.Reinforce > 31 || options.AmplifyType < 0 || options.AmplifyType > 4 || options.AmplifyLevel < 0 || options.AmplifyLevel > 31 || options.Forging < 0 || options.Forging > 8 {
return GMMailDeliveryResult{}, fmt.Errorf("%w: expire_days, reinforce, amplify_type or forging is out of range", ErrGMInvalidRequest)
}
if options.AmplifyType == 0 && options.AmplifyLevel != 0 {
return GMMailDeliveryResult{}, fmt.Errorf("%w: amplify_level requires amplify_type", ErrGMInvalidRequest)
}
if options.AmplifyType > 0 && options.Reinforce != 0 {
return GMMailDeliveryResult{}, fmt.Errorf("%w: reinforce and amplify are mutually exclusive", ErrGMInvalidRequest)
}
if quality != "" && quality != "default" && quality != "random" && quality != "top" {
return GMMailDeliveryResult{}, fmt.Errorf("%w: equipment_quality must be default, random or top", ErrGMInvalidRequest)
}
@@ -2345,10 +2352,17 @@ func (s *Service) GMSendItemMailWithOptions(
stack.Extra["durability"] = strconv.FormatUint(uint64(definition.Durability), 10)
stack.Extra["max_durability"] = strconv.FormatUint(uint64(definition.Durability), 10)
}
if options.Reinforce > 0 {
value := strconv.Itoa(options.Reinforce)
stack.Extra["reinforce"] = value
stack.Extra["ext_data0"] = value
// 等级字节只写一次:带增幅时它表示增幅等级,否则是强化等级。
level := options.Reinforce
if options.AmplifyType > 0 {
level = options.AmplifyLevel
}
if level > 0 {
levelText := strconv.Itoa(level)
stack.Extra["ext_data0"] = levelText
stack.Extra["packed_flag_byte"] = levelText
stack.Extra["reinforce"] = levelText
stack.Extra["upgrade_level"] = levelText
}
if options.AmplifyType > 0 {
// An amplification option is only real to the game once the row
@@ -38,6 +38,7 @@ type GMItemGrantOptionsRequest struct {
Count uint32 `json:"count,omitempty"`
Reinforce int `json:"reinforce,omitempty"`
AmplifyType int `json:"amplify_type,omitempty"`
AmplifyLevel int `json:"amplify_level,omitempty"`
Forging int `json:"forging,omitempty"`
EquipmentQuality string `json:"equipment_quality,omitempty"`
Durability *int `json:"durability,omitempty"`
@@ -92,10 +93,18 @@ func (s *Service) GMGrantItemWithOptions(
// 与邮件发放相同的取值范围校验,避免写出客户端读不了的装备。
if request.Reinforce < 0 || request.Reinforce > 31 ||
request.AmplifyType < 0 || request.AmplifyType > 4 ||
request.AmplifyLevel < 0 || request.AmplifyLevel > 31 ||
request.Forging < 0 || request.Forging > 8 ||
request.ExpireDays < 0 || request.ExpireDays > 3650 {
return result, fmt.Errorf("%w: reinforce/amplify/forging/expire_days out of range", ErrGMRequestInvalid)
}
// 红字装不能同时是白字强化:两者写进同一个等级字节,静默取一个比报错更难查。
if request.AmplifyType == 0 && request.AmplifyLevel != 0 {
return result, fmt.Errorf("%w: amplify_level requires amplify_type", ErrGMRequestInvalid)
}
if request.AmplifyType != 0 && request.Reinforce != 0 {
return result, fmt.Errorf("%w: reinforce and amplify are mutually exclusive", ErrGMRequestInvalid)
}
catalog, err := s.currentPVFItemCatalog()
if err != nil {
@@ -119,6 +128,7 @@ func (s *Service) GMGrantItemWithOptions(
stack, buildErr := s.buildGMGrantItemStack(ctx, catalog, definition, count, gmGrantItemOptions{
Reinforce: request.Reinforce,
AmplifyType: request.AmplifyType,
AmplifyLevel: request.AmplifyLevel,
Forging: request.Forging,
EquipmentQuality: request.EquipmentQuality,
Durability: request.Durability,
@@ -181,6 +191,7 @@ func (s *Service) GMGrantItemWithOptions(
type gmGrantItemOptions struct {
Reinforce int
AmplifyType int
AmplifyLevel int
Forging int
EquipmentQuality string
Durability *int
@@ -262,10 +273,19 @@ func (s *Service) buildGMGrantItemStack(
stack.Extra["max_durability"] = strconv.FormatInt(int64(definition.Durability), 10)
}
if options.Reinforce > 0 {
value := strconv.Itoa(options.Reinforce)
stack.Extra["reinforce"] = value
stack.Extra["ext_data0"] = value
// 强化等级与增幅等级共用 raw[0x0A] 这一个字节:带增幅时它就是增幅等级。
// 这组键与 gm_admin.go 的系统邮件路径(线上已验证过的那条)完全一致,
// 少了 packed_flag_byte/upgrade_level 时,客户端重登重建列会落回 0 级。
level := options.Reinforce
if options.AmplifyType > 0 {
level = options.AmplifyLevel
}
if level > 0 {
text := strconv.Itoa(level)
stack.Extra["ext_data0"] = text
stack.Extra["packed_flag_byte"] = text
stack.Extra["reinforce"] = text
stack.Extra["upgrade_level"] = text
}
if options.Forging > 0 {
stack.Extra["forging"] = strconv.Itoa(options.Forging)