90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
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
|
|
}
|