package protocol import ( "encoding/json" "fmt" "log" "os" "sync" ) type MethodEntry struct { Name string `json:"name"` ID uint32 `json:"id"` Hex string `json:"hex"` } type MethodIdCatalog struct { ClientBuild uint32 `json:"clientBuild"` Source string `json:"source"` Methods []MethodEntry `json:"methods"` } var ( methodRegistryLock sync.RWMutex idToNameMap = make(map[uint32]string) nameToIdMap = make(map[string]uint32) isRegistryLoaded bool ) func init() { for name, id := range canonicalMethodIDs { nameToIdMap[name] = id idToNameMap[id] = name } } // LoadMethodCatalog loads the method catalog from a JSON file (e.g. data/client_data/MethodIds.json). func LoadMethodCatalog(path string) error { data, err := os.ReadFile(path) if err != nil { return fmt.Errorf("failed to read method catalog from %s: %w", path, err) } var catalog MethodIdCatalog if err := json.Unmarshal(data, &catalog); err != nil { return fmt.Errorf("failed to parse method catalog JSON: %w", err) } methodRegistryLock.Lock() defer methodRegistryLock.Unlock() for _, m := range catalog.Methods { idToNameMap[m.ID] = m.Name nameToIdMap[m.Name] = m.ID } isRegistryLoaded = true log.Printf("[PROTOCOL] Loaded %d RPC method definitions from %s (Build %d)", len(catalog.Methods), path, catalog.ClientBuild) return nil } // GetMethodName returns the friendly name of a Method ID, or a formatted fallback. func GetMethodName(id uint32) string { methodRegistryLock.RLock() defer methodRegistryLock.RUnlock() if name, ok := idToNameMap[id]; ok { return name } return fmt.Sprintf("UnknownMethod_0x%08X(%d)", id, id) } // GetMethodID returns the numeric ID for a method name. func GetMethodID(name string) (uint32, bool) { methodRegistryLock.RLock() defer methodRegistryLock.RUnlock() id, ok := nameToIdMap[name] return id, ok } // MethodGetMethodIDDefault resolves a verified name. Legacy callers still pass // a fallback, but guessed IDs must never be sent to an unrelated client handler. func MethodGetMethodIDDefault(name string, fallback uint32) uint32 { if id, ok := GetMethodID(name); ok { return id } return 0 }