package eq2net import ( "fmt" "sync" ) // VersionRange represents a client version range mapped to an opcode version type VersionRange struct { MinVersion uint16 // version_range1 in database MaxVersion uint16 // version_range2 in database OpcodeVersion uint16 // The opcode version for this range (usually MinVersion) } // OpcodeVersionMap maps version ranges for opcode lookups // This replaces the C++ EQOpcodeVersions map type OpcodeVersionMap map[uint16]uint16 // Key: version_range1, Value: version_range2 // GetOpcodeVersion returns the opcode version for a given client version // This is a direct port of the C++ GetOpcodeVersion function func GetOpcodeVersion(clientVersion uint16, versionMap OpcodeVersionMap) uint16 { ret := clientVersion // Iterate through version ranges to find a match for minVersion, maxVersion := range versionMap { if clientVersion >= minVersion && clientVersion <= maxVersion { ret = minVersion break } } return ret } // EmuOpcode represents an emulator-side opcode type EmuOpcode uint16 // Common emulator opcodes - these match the C++ emu_opcodes.h const ( OP_Unknown EmuOpcode = 0x0000 OP_LoginRequestMsg EmuOpcode = 0x0001 OP_LoginByNumRequestMsg EmuOpcode = 0x0002 OP_WSLoginRequestMsg EmuOpcode = 0x0003 OP_ESLoginRequestMsg EmuOpcode = 0x0004 OP_LoginReplyMsg EmuOpcode = 0x0005 OP_WSStatusReplyMsg EmuOpcode = 0x0006 OP_WorldListMsg EmuOpcode = 0x0007 OP_WorldStatusMsg EmuOpcode = 0x0008 OP_DeleteCharacterRequestMsg EmuOpcode = 0x0009 OP_DeleteCharacterReplyMsg EmuOpcode = 0x000A OP_CreateCharacterRequestMsg EmuOpcode = 0x000B OP_CreateCharacterReplyMsg EmuOpcode = 0x000C OP_PlayCharacterRequestMsg EmuOpcode = 0x000D OP_PlayCharacterReplyMsg EmuOpcode = 0x000E OP_ServerListRequestMsg EmuOpcode = 0x000F OP_ServerListReplyMsg EmuOpcode = 0x0010 OP_CharacterListRequestMsg EmuOpcode = 0x0011 OP_CharacterListReplyMsg EmuOpcode = 0x0012 // Add more opcodes as needed ) // OpcodeNames maps emulator opcodes to their string names // This matches the C++ OpcodeNames array var OpcodeNames = map[EmuOpcode]string{ OP_Unknown: "OP_Unknown", OP_LoginRequestMsg: "OP_LoginRequestMsg", OP_LoginByNumRequestMsg: "OP_LoginByNumRequestMsg", OP_WSLoginRequestMsg: "OP_WSLoginRequestMsg", OP_ESLoginRequestMsg: "OP_ESLoginRequestMsg", OP_LoginReplyMsg: "OP_LoginReplyMsg", OP_WSStatusReplyMsg: "OP_WSStatusReplyMsg", OP_WorldListMsg: "OP_WorldListMsg", OP_WorldStatusMsg: "OP_WorldStatusMsg", OP_DeleteCharacterRequestMsg: "OP_DeleteCharacterRequestMsg", OP_DeleteCharacterReplyMsg: "OP_DeleteCharacterReplyMsg", OP_CreateCharacterRequestMsg: "OP_CreateCharacterRequestMsg", OP_CreateCharacterReplyMsg: "OP_CreateCharacterReplyMsg", OP_PlayCharacterRequestMsg: "OP_PlayCharacterRequestMsg", OP_PlayCharacterReplyMsg: "OP_PlayCharacterReplyMsg", OP_ServerListRequestMsg: "OP_ServerListRequestMsg", OP_ServerListReplyMsg: "OP_ServerListReplyMsg", OP_CharacterListRequestMsg: "OP_CharacterListRequestMsg", OP_CharacterListReplyMsg: "OP_CharacterListReplyMsg", } // RegularOpcodeManager manages opcode mappings for a specific version // This is equivalent to the C++ RegularOpcodeManager class type RegularOpcodeManager struct { version uint16 emuToEQ map[EmuOpcode]uint16 eqToEmu map[uint16]EmuOpcode mu sync.RWMutex } // NewRegularOpcodeManager creates a new opcode manager func NewRegularOpcodeManager(version uint16) *RegularOpcodeManager { return &RegularOpcodeManager{ version: version, emuToEQ: make(map[EmuOpcode]uint16), eqToEmu: make(map[uint16]EmuOpcode), } } // LoadOpcodes loads opcode mappings from a map // Input format matches database: map[opcode_name]opcode_value func (om *RegularOpcodeManager) LoadOpcodes(opcodes map[string]uint16) bool { om.mu.Lock() defer om.mu.Unlock() // Clear existing mappings om.emuToEQ = make(map[EmuOpcode]uint16) om.eqToEmu = make(map[uint16]EmuOpcode) // Build bidirectional mappings for name, eqOpcode := range opcodes { // Find the emulator opcode by name var emuOpcode EmuOpcode = OP_Unknown for emu, opcName := range OpcodeNames { if opcName == name { emuOpcode = emu break } } if emuOpcode != OP_Unknown { om.emuToEQ[emuOpcode] = eqOpcode om.eqToEmu[eqOpcode] = emuOpcode } } return true } // EmuToEQ converts an emulator opcode to EQ network opcode func (om *RegularOpcodeManager) EmuToEQ(emu EmuOpcode) uint16 { om.mu.RLock() defer om.mu.RUnlock() if eq, exists := om.emuToEQ[emu]; exists { return eq } return 0xCDCD // Invalid opcode marker (matches C++) } // EQToEmu converts an EQ network opcode to emulator opcode func (om *RegularOpcodeManager) EQToEmu(eq uint16) EmuOpcode { om.mu.RLock() defer om.mu.RUnlock() if emu, exists := om.eqToEmu[eq]; exists { return emu } return OP_Unknown } // EmuToName returns the name of an emulator opcode func (om *RegularOpcodeManager) EmuToName(emu EmuOpcode) string { if name, exists := OpcodeNames[emu]; exists { return name } return "OP_Unknown" } // EQToName returns the name of an EQ network opcode func (om *RegularOpcodeManager) EQToName(eq uint16) string { emu := om.EQToEmu(eq) return om.EmuToName(emu) } // NameSearch finds an emulator opcode by name func NameSearch(name string) EmuOpcode { for opcode, opcName := range OpcodeNames { if opcName == name { return opcode } } return OP_Unknown } // EQOpcodeManager is the global opcode manager map // Maps opcode version to manager instance // This replaces the C++ map EQOpcodeManager type EQOpcodeManagerMap map[uint16]*RegularOpcodeManager // NewEQOpcodeManager creates and initializes the global opcode manager func NewEQOpcodeManager() EQOpcodeManagerMap { return make(EQOpcodeManagerMap) } // LoadFromDatabase simulates loading opcodes from database results // This would be called by your application after querying the database func (m EQOpcodeManagerMap) LoadFromDatabase(versions OpcodeVersionMap, opcodesByVersion map[uint16]map[string]uint16) error { // For each version range, create an opcode manager for minVersion := range versions { manager := NewRegularOpcodeManager(minVersion) // Load opcodes for this version if opcodes, exists := opcodesByVersion[minVersion]; exists { if !manager.LoadOpcodes(opcodes) { return fmt.Errorf("failed to load opcodes for version %d", minVersion) } } else { return fmt.Errorf("no opcodes found for version %d", minVersion) } m[minVersion] = manager } return nil } // GetManagerForClient returns the appropriate opcode manager for a client version func (m EQOpcodeManagerMap) GetManagerForClient(clientVersion uint16, versionMap OpcodeVersionMap) *RegularOpcodeManager { opcodeVersion := GetOpcodeVersion(clientVersion, versionMap) return m[opcodeVersion] } // Example helper functions for database integration // These would be implemented by the application using this library // LoadVersionsFromDB would execute: // SELECT DISTINCT version_range1, version_range2 FROM opcodes func LoadVersionsFromDB() OpcodeVersionMap { // This is just an example - actual implementation would query the database return OpcodeVersionMap{ 1: 546, // Version range 1-546 uses opcode version 1 547: 889, // Version range 547-889 uses opcode version 547 890: 1027, // etc. 1028: 1048, 1049: 1095, 1096: 1184, 1185: 1197, 1198: 1207, 1208: 1211, 1212: 9999, } } // LoadOpcodesFromDB would execute: // SELECT name, opcode FROM opcodes WHERE ? BETWEEN version_range1 AND version_range2 func LoadOpcodesFromDB(version uint16) map[string]uint16 { // This is just an example - actual implementation would query the database return map[string]uint16{ "OP_LoginRequestMsg": 0x00B3, "OP_LoginReplyMsg": 0x00B6, // ... etc } } // InitializeOpcodeSystem shows how to initialize the opcode system // This would be called during server startup func InitializeOpcodeSystem() (EQOpcodeManagerMap, OpcodeVersionMap, error) { // Load version ranges from database versions := LoadVersionsFromDB() // Create the global opcode manager opcodeManager := NewEQOpcodeManager() // Load opcodes for each version opcodesByVersion := make(map[uint16]map[string]uint16) for minVersion := range versions { opcodes := LoadOpcodesFromDB(minVersion) opcodesByVersion[minVersion] = opcodes } // Initialize the manager if err := opcodeManager.LoadFromDatabase(versions, opcodesByVersion); err != nil { return nil, nil, err } return opcodeManager, versions, nil }