You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为魔兽世界正式服插件计算CurseForge API指纹?

CurseForge魔兽世界插件指纹计算修正方案

核心计算规则(非官方但验证有效)

  • 使用Murmur2 32位哈希算法,哈希种子固定为0
  • 需哈希插件目录下所有文件的内容(并非仅lua/toc/xml类型)
  • 文件路径采用相对于插件根目录的相对路径(例如AutoRepair.lua,而非完整的Interface/Addons/AutoRepair/AutoRepair.lua)
  • 按相对路径的字典序对文件排序,依次计算每个文件内容的Murmur2哈希
  • 将所有哈希值按升序排序后,直接以uint32的字节形式拼接(不转为字符串),最后对该字节流再做一次Murmur2哈希(种子0),得到最终指纹

你的代码问题分析

  1. 哈希种子错误:使用了1,正确应为0
  2. 文件路径使用完整WoW路径,而非相对插件根目录的路径
  3. 最终拼接哈希值时转为字符串再哈希,而非直接用uint32字节序列拼接
  4. 仅过滤lua/toc/xml文件,未覆盖插件目录下所有文件

修正后的Go代码

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/joho/godotenv"
	mm "github.com/mistweaverco/go-murmur/murmur2"
)

func ReadFileAsBytes(path string) []byte {
	b, err := os.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return b
}

// 获取插件目录下的所有相对路径文件
func GetRelativeFileList(addonRootDir string) ([]string, error) {
	var relativePaths []string
	err := filepath.Walk(addonRootDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		// 计算相对路径
		relPath, err := filepath.Rel(addonRootDir, path)
		if err != nil {
			return err
		}
		// 统一路径分隔符为/(符合WoW插件路径格式)
		relPath = strings.ReplaceAll(relPath, "\\", "/")
		relativePaths = append(relativePaths, relPath)
		return nil
	})
	return relativePaths, err
}

type Fingerprint struct {
	AddonDir    string
	Fingerprint uint32
}

func GetFingerprint(addonDir string) Fingerprint {
	fingerprint := Fingerprint{AddonDir: addonDir}
	fullAddonDir := wowDirPrefix + "/Interface/Addons/" + addonDir + "/"

	// 获取所有相对路径文件
	relativeFiles, err := GetRelativeFileList(fullAddonDir)
	if err != nil {
		log.Fatal(err)
	}
	// 按字典序排序文件路径
	sort.Strings(relativeFiles)
	fmt.Println("Sorted relative files:", relativeFiles)

	var fileHashes []uint32
	for _, relPath := range relativeFiles {
		fullPath := filepath.Join(fullAddonDir, relPath)
		fc := ReadFileAsBytes(fullPath)
		// 使用种子0计算文件内容的Murmur2哈希
		hash := mm.MurmurHash2(fc, 0)
		fileHashes = append(fileHashes, hash)
	}
	fmt.Println("File hashes:", fileHashes)

	// 对哈希值升序排序
	sort.Slice(fileHashes, func(i, j int) bool { return fileHashes[i] < fileHashes[j] })
	fmt.Println("Sorted hashes:", fileHashes)

	// 将排序后的哈希值转为字节流(小端序)
	var buf bytes.Buffer
	for _, hash := range fileHashes {
		err := binary.Write(&buf, binary.LittleEndian, hash)
		if err != nil {
			log.Fatal(err)
		}
	}

	// 对字节流计算最终Murmur2哈希,种子0
	fingerprint.Fingerprint = mm.MurmurHash2(buf.Bytes(), 0)
	return fingerprint
}

var wowDirPrefix string

func env(k string, failOnMissing bool) string {
	value := os.Getenv(k)
	if value != "" {
		return value
	}
	if failOnMissing {
		log.Fatalf("%v environment variable is not set.", k)
	}
	return ""
}

func main() {
	godotenv.Load()

	wowDirPrefix = env("DEBUG_WOW_RETAIL_DIR", false)

	fp := GetFingerprint("AutoRepair")
	fmt.Printf("{AddonDir: %s, Fingerprint: %d}\n", fp.AddonDir, fp.Fingerprint)
}

关键修正点说明

  • 改用相对路径:确保与CurseForge服务器端计算的路径基准一致
  • 哈希种子改为0:匹配CurseForge使用的标准种子值
  • 直接拼接uint32字节:避免字符串转换导致的哈希值偏差
  • 遍历所有文件:覆盖CurseForge计算时纳入的全部文件类型

内容的提问来源于stack exchange,提问作者GorillaMoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 21:30:50