457 lines
15 KiB
Go
457 lines
15 KiB
Go
package packets
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// InternalOpcode represents the internal opcode enumeration
|
|
type InternalOpcode int32
|
|
|
|
// Internal opcode constants - these map to the C++ EmuOpcode enum
|
|
const (
|
|
OP_Unknown InternalOpcode = iota
|
|
|
|
// Login and authentication operations
|
|
OP_LoginReplyMsg
|
|
OP_LoginByNumRequestMsg
|
|
OP_WSLoginRequestMsg
|
|
|
|
// Server initialization and zone management
|
|
OP_ESInitMsg
|
|
OP_ESReadyForClientsMsg
|
|
OP_CreateZoneInstanceMsg
|
|
OP_ZoneInstanceCreateReplyMsg
|
|
OP_ZoneInstanceDestroyedMsg
|
|
OP_ExpectClientAsCharacterRequest
|
|
OP_ExpectClientAsCharacterReplyMs
|
|
OP_ZoneInfoMsg
|
|
|
|
// Character creation and loading
|
|
OP_CreateCharacterRequestMsg
|
|
OP_DoneLoadingZoneResourcesMsg
|
|
OP_DoneSendingInitialEntitiesMsg
|
|
OP_DoneLoadingEntityResourcesMsg
|
|
OP_DoneLoadingUIResourcesMsg
|
|
|
|
// Game state updates
|
|
OP_PredictionUpdateMsg
|
|
OP_RemoteCmdMsg
|
|
OP_SetRemoteCmdsMsg
|
|
OP_GameWorldTimeMsg
|
|
OP_MOTDMsg
|
|
OP_ZoneMOTDMsg
|
|
|
|
// Command dispatching
|
|
OP_ClientCmdMsg
|
|
OP_DispatchClientCmdMsg
|
|
OP_DispatchESMsg
|
|
|
|
// Character sheet and inventory updates
|
|
OP_UpdateCharacterSheetMsg
|
|
OP_UpdateSpellBookMsg
|
|
OP_UpdateInventoryMsg
|
|
|
|
// Zone transitions
|
|
OP_ChangeZoneMsg
|
|
OP_ClientTeleportRequestMsg
|
|
OP_TeleportWithinZoneMsg
|
|
OP_ReadyToZoneMsg
|
|
|
|
// Chat system
|
|
OP_ChatTellChannelMsg
|
|
OP_ChatTellUserMsg
|
|
|
|
// Position updates
|
|
OP_UpdatePositionMsg
|
|
|
|
// Achievement system
|
|
OP_AchievementUpdateMsg
|
|
OP_CharacterAchievements
|
|
|
|
// Faction system
|
|
OP_FactionUpdateMsg
|
|
|
|
// Title system
|
|
OP_TitleUpdateMsg
|
|
OP_CharacterTitles
|
|
OP_SetActiveTitleMsg
|
|
|
|
// NPC system
|
|
OP_NPCAttackMsg
|
|
OP_NPCTargetMsg
|
|
OP_NPCInfoMsg
|
|
OP_NPCSpellCastMsg
|
|
OP_NPCMovementMsg
|
|
|
|
// Item system
|
|
OP_ItemMoveMsg
|
|
OP_ItemEquipMsg
|
|
OP_ItemUnequipMsg
|
|
OP_ItemPickupMsg
|
|
OP_ItemDropMsg
|
|
OP_ItemExamineMsg
|
|
OP_ItemUpdateMsg
|
|
|
|
// EverQuest specific commands - Core
|
|
OP_EqHearChatCmd
|
|
OP_EqDisplayTextCmd
|
|
OP_EqCreateGhostCmd
|
|
OP_EqCreateWidgetCmd
|
|
OP_EqDestroyGhostCmd
|
|
OP_EqUpdateGhostCmd
|
|
OP_EqSetControlGhostCmd
|
|
OP_EqSetPOVGhostCmd
|
|
|
|
// Alt Advancement opcodes
|
|
OP_AdventureList
|
|
OP_AdvancementRequestMsg
|
|
OP_CommitAATemplate
|
|
OP_ExamineAASpellInfo
|
|
|
|
// Appearance system opcodes
|
|
OP_DressingRoom
|
|
OP_ReskinCharacterRequestMsg
|
|
|
|
// Chat system opcodes (additional ones beyond existing)
|
|
OP_ChatRelationshipUpdateMsg
|
|
OP_ChatCreateChannelMsg
|
|
OP_ChatJoinChannelMsg
|
|
OP_ChatWhoChannelMsg
|
|
OP_ChatLeaveChannelMsg
|
|
OP_ChatToggleFriendMsg
|
|
OP_ChatToggleIgnoreMsg
|
|
OP_ChatSendFriendsMsg
|
|
OP_ChatSendIgnoresMsg
|
|
OP_ChatFiltersMsg
|
|
OP_EqChatChannelUpdateCmd
|
|
|
|
// Collection system opcodes
|
|
OP_EqCollectionUpdateCmd
|
|
OP_EqCollectionFilterCmd
|
|
OP_EqCollectionItemCmd
|
|
|
|
// Entity system opcodes (additional ones beyond existing)
|
|
OP_EntityVerbsRequestMsg
|
|
OP_EntityVerbsReplyMsg
|
|
OP_EntityVerbsVerbMsg
|
|
|
|
// Group system opcodes
|
|
OP_GroupUpdateMsg
|
|
OP_RaidUpdateMsg
|
|
OP_GroupInviteMsg
|
|
OP_GroupDeclineMsg
|
|
OP_GroupChatMsg
|
|
OP_GroupMessageMsg
|
|
OP_GroupOptionsMsg
|
|
OP_DefaultGroupOptionsMsg
|
|
OP_DefaultGroupOptionsRequestMsg
|
|
|
|
// Guild system opcodes
|
|
OP_GuildUpdateMsg
|
|
OP_GuildEventListMsg
|
|
OP_GuildEventDetailsMsg
|
|
OP_GuildEventAddMsg
|
|
OP_GuildMembershipResponseMsg
|
|
OP_JoinGuildNotifyMsg
|
|
OP_LeaveGuildNotifyMsg
|
|
OP_GuildInviteMsg
|
|
OP_GuildDeclineMsg
|
|
OP_GuildRecruitingMsg
|
|
OP_GuildRecruitingDetailsMsg
|
|
OP_ModifyGuildMsg
|
|
OP_RequestGuildInfoMsg
|
|
|
|
// Heroic Opportunity system opcodes
|
|
OP_HeroicOpportunityMsg
|
|
OP_HeroicOpportunityStartMsg
|
|
OP_HeroicOpportunityCompleteMsg
|
|
OP_HeroicOpportunityTimerMsg
|
|
OP_HeroicOpportunityWheelMsg
|
|
OP_HeroicOpportunityProgressMsg
|
|
OP_HeroicOpportunityErrorMsg
|
|
OP_HeroicOpportunityShiftMsg
|
|
|
|
// Add more opcodes as needed...
|
|
_maxInternalOpcode // Sentinel value
|
|
)
|
|
|
|
// OpcodeNames maps internal opcodes to their string names for debugging
|
|
var OpcodeNames = map[InternalOpcode]string{
|
|
OP_Unknown: "OP_Unknown",
|
|
OP_LoginReplyMsg: "OP_LoginReplyMsg",
|
|
OP_LoginByNumRequestMsg: "OP_LoginByNumRequestMsg",
|
|
OP_WSLoginRequestMsg: "OP_WSLoginRequestMsg",
|
|
OP_ESInitMsg: "OP_ESInitMsg",
|
|
OP_ESReadyForClientsMsg: "OP_ESReadyForClientsMsg",
|
|
OP_CreateZoneInstanceMsg: "OP_CreateZoneInstanceMsg",
|
|
OP_ZoneInstanceCreateReplyMsg: "OP_ZoneInstanceCreateReplyMsg",
|
|
OP_ZoneInstanceDestroyedMsg: "OP_ZoneInstanceDestroyedMsg",
|
|
OP_ExpectClientAsCharacterRequest: "OP_ExpectClientAsCharacterRequest",
|
|
OP_ExpectClientAsCharacterReplyMs: "OP_ExpectClientAsCharacterReplyMs",
|
|
OP_ZoneInfoMsg: "OP_ZoneInfoMsg",
|
|
OP_CreateCharacterRequestMsg: "OP_CreateCharacterRequestMsg",
|
|
OP_DoneLoadingZoneResourcesMsg: "OP_DoneLoadingZoneResourcesMsg",
|
|
OP_DoneSendingInitialEntitiesMsg: "OP_DoneSendingInitialEntitiesMsg",
|
|
OP_DoneLoadingEntityResourcesMsg: "OP_DoneLoadingEntityResourcesMsg",
|
|
OP_DoneLoadingUIResourcesMsg: "OP_DoneLoadingUIResourcesMsg",
|
|
OP_PredictionUpdateMsg: "OP_PredictionUpdateMsg",
|
|
OP_RemoteCmdMsg: "OP_RemoteCmdMsg",
|
|
OP_SetRemoteCmdsMsg: "OP_SetRemoteCmdsMsg",
|
|
OP_GameWorldTimeMsg: "OP_GameWorldTimeMsg",
|
|
OP_MOTDMsg: "OP_MOTDMsg",
|
|
OP_ZoneMOTDMsg: "OP_ZoneMOTDMsg",
|
|
OP_ClientCmdMsg: "OP_ClientCmdMsg",
|
|
OP_DispatchClientCmdMsg: "OP_DispatchClientCmdMsg",
|
|
OP_DispatchESMsg: "OP_DispatchESMsg",
|
|
OP_UpdateCharacterSheetMsg: "OP_UpdateCharacterSheetMsg",
|
|
OP_UpdateSpellBookMsg: "OP_UpdateSpellBookMsg",
|
|
OP_UpdateInventoryMsg: "OP_UpdateInventoryMsg",
|
|
OP_ChangeZoneMsg: "OP_ChangeZoneMsg",
|
|
OP_ClientTeleportRequestMsg: "OP_ClientTeleportRequestMsg",
|
|
OP_TeleportWithinZoneMsg: "OP_TeleportWithinZoneMsg",
|
|
OP_ReadyToZoneMsg: "OP_ReadyToZoneMsg",
|
|
OP_ChatTellChannelMsg: "OP_ChatTellChannelMsg",
|
|
OP_ChatTellUserMsg: "OP_ChatTellUserMsg",
|
|
OP_UpdatePositionMsg: "OP_UpdatePositionMsg",
|
|
OP_AchievementUpdateMsg: "OP_AchievementUpdateMsg",
|
|
OP_CharacterAchievements: "OP_CharacterAchievements",
|
|
OP_FactionUpdateMsg: "OP_FactionUpdateMsg",
|
|
OP_TitleUpdateMsg: "OP_TitleUpdateMsg",
|
|
OP_CharacterTitles: "OP_CharacterTitles",
|
|
OP_SetActiveTitleMsg: "OP_SetActiveTitleMsg",
|
|
OP_NPCAttackMsg: "OP_NPCAttackMsg",
|
|
OP_NPCTargetMsg: "OP_NPCTargetMsg",
|
|
OP_NPCInfoMsg: "OP_NPCInfoMsg",
|
|
OP_NPCSpellCastMsg: "OP_NPCSpellCastMsg",
|
|
OP_NPCMovementMsg: "OP_NPCMovementMsg",
|
|
OP_ItemMoveMsg: "OP_ItemMoveMsg",
|
|
OP_ItemEquipMsg: "OP_ItemEquipMsg",
|
|
OP_ItemUnequipMsg: "OP_ItemUnequipMsg",
|
|
OP_ItemPickupMsg: "OP_ItemPickupMsg",
|
|
OP_ItemDropMsg: "OP_ItemDropMsg",
|
|
OP_ItemExamineMsg: "OP_ItemExamineMsg",
|
|
OP_ItemUpdateMsg: "OP_ItemUpdateMsg",
|
|
OP_EqHearChatCmd: "OP_EqHearChatCmd",
|
|
OP_EqDisplayTextCmd: "OP_EqDisplayTextCmd",
|
|
OP_EqCreateGhostCmd: "OP_EqCreateGhostCmd",
|
|
OP_EqCreateWidgetCmd: "OP_EqCreateWidgetCmd",
|
|
OP_EqDestroyGhostCmd: "OP_EqDestroyGhostCmd",
|
|
OP_EqUpdateGhostCmd: "OP_EqUpdateGhostCmd",
|
|
OP_EqSetControlGhostCmd: "OP_EqSetControlGhostCmd",
|
|
OP_EqSetPOVGhostCmd: "OP_EqSetPOVGhostCmd",
|
|
OP_AdventureList: "OP_AdventureList",
|
|
OP_AdvancementRequestMsg: "OP_AdvancementRequestMsg",
|
|
OP_CommitAATemplate: "OP_CommitAATemplate",
|
|
OP_ExamineAASpellInfo: "OP_ExamineAASpellInfo",
|
|
OP_DressingRoom: "OP_DressingRoom",
|
|
OP_ReskinCharacterRequestMsg: "OP_ReskinCharacterRequestMsg",
|
|
OP_ChatRelationshipUpdateMsg: "OP_ChatRelationshipUpdateMsg",
|
|
OP_ChatCreateChannelMsg: "OP_ChatCreateChannelMsg",
|
|
OP_ChatJoinChannelMsg: "OP_ChatJoinChannelMsg",
|
|
OP_ChatWhoChannelMsg: "OP_ChatWhoChannelMsg",
|
|
OP_ChatLeaveChannelMsg: "OP_ChatLeaveChannelMsg",
|
|
OP_ChatToggleFriendMsg: "OP_ChatToggleFriendMsg",
|
|
OP_ChatToggleIgnoreMsg: "OP_ChatToggleIgnoreMsg",
|
|
OP_ChatSendFriendsMsg: "OP_ChatSendFriendsMsg",
|
|
OP_ChatSendIgnoresMsg: "OP_ChatSendIgnoresMsg",
|
|
OP_ChatFiltersMsg: "OP_ChatFiltersMsg",
|
|
OP_EqChatChannelUpdateCmd: "OP_EqChatChannelUpdateCmd",
|
|
|
|
// Collection system opcodes
|
|
OP_EqCollectionUpdateCmd: "OP_EqCollectionUpdateCmd",
|
|
OP_EqCollectionFilterCmd: "OP_EqCollectionFilterCmd",
|
|
OP_EqCollectionItemCmd: "OP_EqCollectionItemCmd",
|
|
|
|
// Entity system opcodes (additional ones beyond existing)
|
|
OP_EntityVerbsRequestMsg: "OP_EntityVerbsRequestMsg",
|
|
OP_EntityVerbsReplyMsg: "OP_EntityVerbsReplyMsg",
|
|
OP_EntityVerbsVerbMsg: "OP_EntityVerbsVerbMsg",
|
|
|
|
// Group system opcodes
|
|
OP_GroupUpdateMsg: "OP_GroupUpdateMsg",
|
|
OP_RaidUpdateMsg: "OP_RaidUpdateMsg",
|
|
OP_GroupInviteMsg: "OP_GroupInviteMsg",
|
|
OP_GroupDeclineMsg: "OP_GroupDeclineMsg",
|
|
OP_GroupChatMsg: "OP_GroupChatMsg",
|
|
OP_GroupMessageMsg: "OP_GroupMessageMsg",
|
|
OP_GroupOptionsMsg: "OP_GroupOptionsMsg",
|
|
OP_DefaultGroupOptionsMsg: "OP_DefaultGroupOptionsMsg",
|
|
OP_DefaultGroupOptionsRequestMsg: "OP_DefaultGroupOptionsRequestMsg",
|
|
|
|
// Guild system opcodes
|
|
OP_GuildUpdateMsg: "OP_GuildUpdateMsg",
|
|
OP_GuildEventListMsg: "OP_GuildEventListMsg",
|
|
OP_GuildEventDetailsMsg: "OP_GuildEventDetailsMsg",
|
|
OP_GuildEventAddMsg: "OP_GuildEventAddMsg",
|
|
OP_GuildMembershipResponseMsg: "OP_GuildMembershipResponseMsg",
|
|
OP_JoinGuildNotifyMsg: "OP_JoinGuildNotifyMsg",
|
|
OP_LeaveGuildNotifyMsg: "OP_LeaveGuildNotifyMsg",
|
|
OP_GuildInviteMsg: "OP_GuildInviteMsg",
|
|
OP_GuildDeclineMsg: "OP_GuildDeclineMsg",
|
|
OP_GuildRecruitingMsg: "OP_GuildRecruitingMsg",
|
|
OP_GuildRecruitingDetailsMsg: "OP_GuildRecruitingDetailsMsg",
|
|
OP_ModifyGuildMsg: "OP_ModifyGuildMsg",
|
|
OP_RequestGuildInfoMsg: "OP_RequestGuildInfoMsg",
|
|
|
|
// Heroic Opportunity system opcodes
|
|
OP_HeroicOpportunityMsg: "OP_HeroicOpportunityMsg",
|
|
OP_HeroicOpportunityStartMsg: "OP_HeroicOpportunityStartMsg",
|
|
OP_HeroicOpportunityCompleteMsg: "OP_HeroicOpportunityCompleteMsg",
|
|
OP_HeroicOpportunityTimerMsg: "OP_HeroicOpportunityTimerMsg",
|
|
OP_HeroicOpportunityWheelMsg: "OP_HeroicOpportunityWheelMsg",
|
|
OP_HeroicOpportunityProgressMsg: "OP_HeroicOpportunityProgressMsg",
|
|
OP_HeroicOpportunityErrorMsg: "OP_HeroicOpportunityErrorMsg",
|
|
OP_HeroicOpportunityShiftMsg: "OP_HeroicOpportunityShiftMsg",
|
|
}
|
|
|
|
// OpcodeManager handles the mapping between client-specific opcodes and internal opcodes
|
|
type OpcodeManager struct {
|
|
// Maps client version -> (client opcode -> internal opcode)
|
|
clientToInternal map[int32]map[uint16]InternalOpcode
|
|
// Maps internal opcode -> client version -> client opcode
|
|
internalToClient map[InternalOpcode]map[int32]uint16
|
|
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
// NewOpcodeManager creates a new opcode manager
|
|
func NewOpcodeManager() *OpcodeManager {
|
|
return &OpcodeManager{
|
|
clientToInternal: make(map[int32]map[uint16]InternalOpcode),
|
|
internalToClient: make(map[InternalOpcode]map[int32]uint16),
|
|
}
|
|
}
|
|
|
|
// LoadOpcodeMap loads opcode mappings for a specific client version
|
|
func (om *OpcodeManager) LoadOpcodeMap(clientVersion int32, opcodeMap map[string]uint16) error {
|
|
om.mutex.Lock()
|
|
defer om.mutex.Unlock()
|
|
|
|
// Initialize maps for this client version
|
|
if om.clientToInternal[clientVersion] == nil {
|
|
om.clientToInternal[clientVersion] = make(map[uint16]InternalOpcode)
|
|
}
|
|
|
|
// Process each opcode mapping
|
|
for opcodeName, clientOpcode := range opcodeMap {
|
|
// Find the internal opcode for this name
|
|
internalOpcode := OP_Unknown
|
|
for intOp, name := range OpcodeNames {
|
|
if name == opcodeName {
|
|
internalOpcode = intOp
|
|
break
|
|
}
|
|
}
|
|
|
|
if internalOpcode == OP_Unknown && opcodeName != "OP_Unknown" {
|
|
// Log warning for unknown opcode but don't fail
|
|
fmt.Printf("Warning: Unknown internal opcode name: %s\n", opcodeName)
|
|
continue
|
|
}
|
|
|
|
// Set client -> internal mapping
|
|
om.clientToInternal[clientVersion][clientOpcode] = internalOpcode
|
|
|
|
// Set internal -> client mapping
|
|
if om.internalToClient[internalOpcode] == nil {
|
|
om.internalToClient[internalOpcode] = make(map[int32]uint16)
|
|
}
|
|
om.internalToClient[internalOpcode][clientVersion] = clientOpcode
|
|
}
|
|
|
|
fmt.Printf("Loaded %d opcode mappings for client version %d\n", len(opcodeMap), clientVersion)
|
|
return nil
|
|
}
|
|
|
|
// ClientOpcodeToInternal converts a client opcode to internal opcode
|
|
func (om *OpcodeManager) ClientOpcodeToInternal(clientVersion int32, clientOpcode uint16) InternalOpcode {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
if versionMap, exists := om.clientToInternal[clientVersion]; exists {
|
|
if internalOp, found := versionMap[clientOpcode]; found {
|
|
return internalOp
|
|
}
|
|
}
|
|
|
|
return OP_Unknown
|
|
}
|
|
|
|
// InternalOpcodeToClient converts an internal opcode to client opcode
|
|
func (om *OpcodeManager) InternalOpcodeToClient(internalOpcode InternalOpcode, clientVersion int32) uint16 {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
if versionMap, exists := om.internalToClient[internalOpcode]; exists {
|
|
if clientOp, found := versionMap[clientVersion]; found {
|
|
return clientOp
|
|
}
|
|
}
|
|
|
|
return 0 // Invalid client opcode
|
|
}
|
|
|
|
// GetOpcodeName returns the human-readable name for an internal opcode
|
|
func (om *OpcodeManager) GetOpcodeName(internalOpcode InternalOpcode) string {
|
|
if name, exists := OpcodeNames[internalOpcode]; exists {
|
|
return name
|
|
}
|
|
return "OP_Unknown"
|
|
}
|
|
|
|
// GetSupportedVersions returns all client versions with loaded opcodes
|
|
func (om *OpcodeManager) GetSupportedVersions() []int32 {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
versions := make([]int32, 0, len(om.clientToInternal))
|
|
for version := range om.clientToInternal {
|
|
versions = append(versions, version)
|
|
}
|
|
|
|
return versions
|
|
}
|
|
|
|
// GetOpcodeCount returns the number of opcodes loaded for a client version
|
|
func (om *OpcodeManager) GetOpcodeCount(clientVersion int32) int {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
if versionMap, exists := om.clientToInternal[clientVersion]; exists {
|
|
return len(versionMap)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// Global opcode manager instance
|
|
var globalOpcodeManager = NewOpcodeManager()
|
|
|
|
// GetOpcodeManager returns the global opcode manager
|
|
func GetOpcodeManager() *OpcodeManager {
|
|
return globalOpcodeManager
|
|
}
|
|
|
|
// Convenience functions for global access
|
|
|
|
// LoadGlobalOpcodeMap loads opcodes into the global manager
|
|
func LoadGlobalOpcodeMap(clientVersion int32, opcodeMap map[string]uint16) error {
|
|
return globalOpcodeManager.LoadOpcodeMap(clientVersion, opcodeMap)
|
|
}
|
|
|
|
// ClientToInternal converts using the global manager
|
|
func ClientToInternal(clientVersion int32, clientOpcode uint16) InternalOpcode {
|
|
return globalOpcodeManager.ClientOpcodeToInternal(clientVersion, clientOpcode)
|
|
}
|
|
|
|
// InternalToClient converts using the global manager
|
|
func InternalToClient(internalOpcode InternalOpcode, clientVersion int32) uint16 {
|
|
return globalOpcodeManager.InternalOpcodeToClient(internalOpcode, clientVersion)
|
|
}
|
|
|
|
// GetInternalOpcodeName returns name using the global manager
|
|
func GetInternalOpcodeName(internalOpcode InternalOpcode) string {
|
|
return globalOpcodeManager.GetOpcodeName(internalOpcode)
|
|
}
|