1
0

Compare commits

...

10 Commits

Author SHA1 Message Date
4ecdcc868f fix chat encoding 2025-09-03 20:48:08 -05:00
1a9b7effa6 fix simple encoding 2025-09-03 20:41:44 -05:00
fd2ebfd6cc fix packet building order 2025-09-03 14:19:51 -05:00
50d4d9d6f0 fix encryption offsets 2025-09-03 14:09:30 -05:00
d70daa98be compat pass 3 2025-09-03 13:50:03 -05:00
c692b25ac0 compat pass 2 2025-09-03 13:29:04 -05:00
9cc3273a81 work on stream 2025-09-03 13:00:54 -05:00
7a7ff3f6ba start stream 2025-09-03 11:54:26 -05:00
ef7dd9d4ef update packets to use opcodes 2025-09-03 11:38:46 -05:00
868ab62573 build up packets and opcodes 2025-09-03 11:27:01 -05:00
15 changed files with 4393 additions and 0 deletions

View File

@ -55,3 +55,8 @@ func (c *Ciphers) Encrypt(data []byte) {
c.server.XORKeyStream(data, data)
}
}
// IsEncrypted returns true if encryption is active
func (c *Ciphers) IsEncrypted() bool {
return c != nil && c.server != nil
}

646
opcodes/emu.go Normal file
View File

@ -0,0 +1,646 @@
package opcodes
// Emulator/Application opcodes - Complete list from emu_oplist.h
const (
OP_Unknown EmuOpcode = 0x0000
// Login and session management
OP_LoginReplyMsg EmuOpcode = 0x0001
OP_LoginByNumRequestMsg EmuOpcode = 0x0002
OP_WSLoginRequestMsg EmuOpcode = 0x0003
OP_ESInitMsg EmuOpcode = 0x0004
OP_ESReadyForClientsMsg EmuOpcode = 0x0005
// Zone instance management
OP_CreateZoneInstanceMsg EmuOpcode = 0x0010
OP_ZoneInstanceCreateReplyMsg EmuOpcode = 0x0011
OP_ZoneInstanceDestroyedMsg EmuOpcode = 0x0012
OP_ExpectClientAsCharacterRequest EmuOpcode = 0x0013
OP_ExpectClientAsCharacterReplyMs EmuOpcode = 0x0014
OP_ZoneInfoMsg EmuOpcode = 0x0015
// Character creation and loading
OP_CreateCharacterRequestMsg EmuOpcode = 0x0020
OP_CreateCharacterReplyMsg EmuOpcode = 0x0021
OP_CharacterCreateRequestMsg EmuOpcode = 0x0022
OP_CharacterCreateReplyMsg EmuOpcode = 0x0023
OP_DoneLoadingZoneResourcesMsg EmuOpcode = 0x0024
OP_DoneSendingInitialEntitiesMsg EmuOpcode = 0x0025
OP_DoneLoadingEntityResourcesMsg EmuOpcode = 0x0026
OP_DoneLoadingUIResourcesMsg EmuOpcode = 0x0027
// World and time
OP_PredictionUpdateMsg EmuOpcode = 0x0030
OP_RemoteCmdMsg EmuOpcode = 0x0031
OP_SetRemoteCmdsMsg EmuOpcode = 0x0032
OP_GameWorldTimeMsg EmuOpcode = 0x0033
OP_MOTDMsg EmuOpcode = 0x0034
OP_ZoneMOTDMsg EmuOpcode = 0x0035
OP_SetTimeMsg EmuOpcode = 0x0036
// Guild recruitment
OP_GuildRecruitingMemberInfo EmuOpcode = 0x0040
OP_GuildRecruiting EmuOpcode = 0x0041
OP_GuildRecruitingDetails EmuOpcode = 0x0042
OP_GuildRecruitingImage EmuOpcode = 0x0043
// Avatar and camping
OP_AvatarCreatedMsg EmuOpcode = 0x0050
OP_AvatarDestroyedMsg EmuOpcode = 0x0051
OP_RequestCampMsg EmuOpcode = 0x0052
OP_MapRequest EmuOpcode = 0x0053
OP_CampStartedMsg EmuOpcode = 0x0054
OP_CampAbortedMsg EmuOpcode = 0x0055
// Who and monitor
OP_WhoQueryRequestMsg EmuOpcode = 0x0060
OP_WhoQueryReplyMsg EmuOpcode = 0x0061
OP_MonitorReplyMsg EmuOpcode = 0x0062
OP_MonitorCharacterListMsg EmuOpcode = 0x0063
OP_MonitorCharacterListRequestMsg EmuOpcode = 0x0064
// Client commands
OP_ClientCmdMsg EmuOpcode = 0x0070
OP_Lottery EmuOpcode = 0x0071
OP_DispatchClientCmdMsg EmuOpcode = 0x0072
OP_DispatchESMsg EmuOpcode = 0x0073
// Target and opportunity
OP_UpdateTargetMsg EmuOpcode = 0x0080
OP_UpdateOpportunityMsg EmuOpcode = 0x0081
OP_UpdateTargetLocMsg EmuOpcode = 0x0082
OP_RequestTargetLocMsg EmuOpcode = 0x0083
// Character updates
OP_UpdateCharacterSheetMsg EmuOpcode = 0x0090
OP_UpdateSpellBookMsg EmuOpcode = 0x0091
OP_UpdateInventoryMsg EmuOpcode = 0x0092
OP_UpdateRecipeBookMsg EmuOpcode = 0x0093
OP_RequestRecipeDetailsMsg EmuOpcode = 0x0094
OP_RecipeDetailsMsg EmuOpcode = 0x0095
OP_UpdateSkillBookMsg EmuOpcode = 0x0096
OP_UpdateSkillsMsg EmuOpcode = 0x0097
// Zone changes and teleports
OP_ChangeZoneMsg EmuOpcode = 0x00a0
OP_ClientTeleportRequestMsg EmuOpcode = 0x00a1
OP_TeleportWithinZoneMsg EmuOpcode = 0x00a2
OP_TeleportWithinZoneNoReloadMsg EmuOpcode = 0x00a3
OP_MigrateClientToZoneRequestMsg EmuOpcode = 0x00a4
OP_MigrateClientToZoneReplyMsg EmuOpcode = 0x00a5
OP_ReadyToZoneMsg EmuOpcode = 0x00a6
OP_ZoneChangeMsg EmuOpcode = 0x00a7
OP_ZoneUnavailMsg EmuOpcode = 0x00a8
// Login and world interaction
OP_EnterWorldMsg EmuOpcode = 0x00f1
OP_LoginAcceptedMsg EmuOpcode = 0x00f3
OP_LoginFailedMsg2 EmuOpcode = 0x00f4
OP_ExitWorldMsg EmuOpcode = 0x00f5
OP_ClientIdMsg EmuOpcode = 0x00f8
// NPC interaction
OP_QueryHoodMsg EmuOpcode = 0x00f9
OP_QueryNpcMsg EmuOpcode = 0x00fa
OP_AttackMsg EmuOpcode = 0x00fb
OP_DialogOpenMsg EmuOpcode = 0x00fc
OP_DialogSelectMsg EmuOpcode = 0x00fd
OP_ConversationMsg EmuOpcode = 0x00fe
OP_CloseWindowMsg EmuOpcode = 0x00ff
// Group management - detailed
OP_RemoveClientFromGroupMsg EmuOpcode = 0x0200
OP_RemoveGroupFromGroupMsg EmuOpcode = 0x0201
OP_MakeGroupLeaderMsg EmuOpcode = 0x0202
OP_GroupCreatedMsg EmuOpcode = 0x0203
OP_GroupDestroyedMsg EmuOpcode = 0x0204
OP_GroupMemberAddedMsg EmuOpcode = 0x0205
OP_GroupMemberRemovedMsg EmuOpcode = 0x0206
OP_GroupRemovedFromGroupMsg EmuOpcode = 0x0207
OP_GroupLeaderChangedMsg EmuOpcode = 0x0208
OP_GroupSettingsChangedMsg EmuOpcode = 0x0209
OP_GroupOptionsMsg EmuOpcode = 0x020a
OP_GroupInviteRequestMsg EmuOpcode = 0x020b
OP_GroupInviteResponseMsg EmuOpcode = 0x020c
OP_GroupDeclineResponseMsg EmuOpcode = 0x020d
OP_DefaultGroupOptionsRequestMsg EmuOpcode = 0x020e
OP_DefaultGroupOptionsMsg EmuOpcode = 0x020f
OP_DisplayGroupOptionsScreenMsg EmuOpcode = 0x0210
OP_CloseGroupInviteWindowMsg EmuOpcode = 0x0211
OP_UpdateGroupMemberDataMsg EmuOpcode = 0x0212
// ES (Emulator Server) status
OP_SendLatestRequestMsg EmuOpcode = 0x0220
OP_ClearDataMsg EmuOpcode = 0x0221
OP_SetSocialMsg EmuOpcode = 0x0222
OP_ESStatusMsg EmuOpcode = 0x0223
OP_ESZoneInstanceStatusMsg EmuOpcode = 0x0224
OP_ZonesStatusRequestMsg EmuOpcode = 0x0225
OP_ZonesStatusMsg EmuOpcode = 0x0226
OP_ESWeatherRequestMsg EmuOpcode = 0x0227
OP_ESWeatherRequestEndMsg EmuOpcode = 0x0228
// Chat system
OP_ChatMsg EmuOpcode = 0x0300
OP_ChatFiltersMsg EmuOpcode = 0x0301
OP_TellMsg EmuOpcode = 0x0302
OP_ChatRelationshipUpdateMsg EmuOpcode = 0x0303
OP_ChatCreateChannelMsg EmuOpcode = 0x0304
OP_ChatJoinChannelMsg EmuOpcode = 0x0305
OP_ChatWhoChannelMsg EmuOpcode = 0x0306
OP_ChatLeaveChannelMsg EmuOpcode = 0x0307
OP_ChatTellChannelMsg EmuOpcode = 0x0308
OP_ChatTellUserMsg EmuOpcode = 0x0309
OP_ChatToggleFriendMsg EmuOpcode = 0x030a
OP_ChatToggleIgnoreMsg EmuOpcode = 0x030b
OP_ChatSendFriendsMsg EmuOpcode = 0x030c
OP_ChatSendIgnoresMsg EmuOpcode = 0x030d
// Trade system
OP_TradeRequestMsg EmuOpcode = 0x0400
OP_TradeResponseMsg EmuOpcode = 0x0401
OP_TradeStartMsg EmuOpcode = 0x0402
OP_TradeEndMsg EmuOpcode = 0x0403
OP_TradeAddItemMsg EmuOpcode = 0x0404
OP_TradeRemoveItemMsg EmuOpcode = 0x0405
OP_TradeAcceptMsg EmuOpcode = 0x0406
// Inventory and items
OP_InventoryMsg EmuOpcode = 0x0500
OP_EquipItemMsg EmuOpcode = 0x0501
OP_UnequipItemMsg EmuOpcode = 0x0502
OP_DeleteItemMsg EmuOpcode = 0x0503
OP_UseItemMsg EmuOpcode = 0x0504
OP_ExamineItemRequestMsg EmuOpcode = 0x0505
OP_ExamineItemMsg EmuOpcode = 0x0506
OP_ExamineInfoRequestMsg EmuOpcode = 0x0507
OP_LootItemsRequestMsg EmuOpcode = 0x0508
OP_StoppedLootingMsg EmuOpcode = 0x0509
// Combat and skills
OP_ConsiderMsg EmuOpcode = 0x0600
OP_TargetMsg EmuOpcode = 0x0601
OP_AutoAttackMsg EmuOpcode = 0x0602
OP_CastSpellMsg EmuOpcode = 0x0603
OP_InterruptSpellMsg EmuOpcode = 0x0604
OP_SkillInfoRequestMsg EmuOpcode = 0x0605
OP_SkillInfoResponseMsg EmuOpcode = 0x0606
OP_SkillInfoRequest EmuOpcode = 0x0607
OP_SkillInfoResponse EmuOpcode = 0x0608
OP_RemoveSpellEffectMsg EmuOpcode = 0x0609
OP_RemoveConcentrationMsg EmuOpcode = 0x060a
OP_CancelSpellCast EmuOpcode = 0x060b
OP_AttackNotAllowed EmuOpcode = 0x060c
OP_AttackAllowed EmuOpcode = 0x060d
OP_PopulateSkillMapsMsg EmuOpcode = 0x060e
OP_CancelledFeignMsg EmuOpcode = 0x060f
OP_SignalMsg EmuOpcode = 0x0610
OP_DispatchSpellCmdMsg EmuOpcode = 0x0611
// Player status
OP_PlayerStatsMsg EmuOpcode = 0x0700
OP_PlayerBioMsg EmuOpcode = 0x0701
OP_PlayerProfileMsg EmuOpcode = 0x0702
OP_LevelUpMsg EmuOpcode = 0x0703
OP_ExpUpdateMsg EmuOpcode = 0x0704
OP_SkillUpMsg EmuOpcode = 0x0705
OP_LevelChangedMsg EmuOpcode = 0x0706
OP_SpellGainedMsg EmuOpcode = 0x0707
OP_UpdatePositionMsg EmuOpcode = 0x0708
OP_SitMsg EmuOpcode = 0x0709
OP_StandMsg EmuOpcode = 0x070a
OP_SatMsg EmuOpcode = 0x070b
OP_StoodMsg EmuOpcode = 0x070c
OP_AvatarUpdateMsg EmuOpcode = 0x070d
OP_BioUpdateMsg EmuOpcode = 0x070e
OP_InspectPlayerMsg EmuOpcode = 0x070f
OP_AFKUpdateMsg EmuOpcode = 0x0710
OP_AnonUpdateMsg EmuOpcode = 0x0711
OP_LFGUpdateMsg EmuOpcode = 0x0712
// Zone and world
OP_ZoneListMsg EmuOpcode = 0x0800
OP_ZoneEntryMsg EmuOpcode = 0x0801
OP_ZoneExitMsg EmuOpcode = 0x0802
OP_NewZoneMsg EmuOpcode = 0x0803
OP_SendZonePointsMsg EmuOpcode = 0x0804
OP_ZonePointsMsg EmuOpcode = 0x0805
OP_WorldPingMsg EmuOpcode = 0x0806
OP_WorldShutdownUpdateMsg EmuOpcode = 0x0807
OP_RestartZoneMsg EmuOpcode = 0x0808
OP_UpdateActivePublicZonesMsg EmuOpcode = 0x0809
OP_WorldDataUpdateMsg EmuOpcode = 0x080a
OP_WorldDataChangeMsg EmuOpcode = 0x080b
OP_ClientTeleportToLocationMsg EmuOpcode = 0x080c
OP_UpdateMotdMsg EmuOpcode = 0x080d
// Housing system
OP_HousingDataMsg EmuOpcode = 0x0900
OP_HouseItemsList EmuOpcode = 0x0901
OP_HousePurchase EmuOpcode = 0x0902
OP_HousePayUpkeep EmuOpcode = 0x0903
OP_PayHouseUpkeepMsg EmuOpcode = 0x0904
OP_HouseDeletedRemotelyMsg EmuOpcode = 0x0905
OP_UpdateHouseDataMsg EmuOpcode = 0x0906
OP_UpdateHouseAccessDataMsg EmuOpcode = 0x0907
OP_PlayerHouseBaseScreenMsg EmuOpcode = 0x0908
OP_PlayerHousePurchaseScreenMsg EmuOpcode = 0x0909
OP_PlayerHouseAccessUpdateMsg EmuOpcode = 0x090a
OP_PlayerHouseDisplayStatusMsg EmuOpcode = 0x090b
OP_PlayerHouseCloseUIMsg EmuOpcode = 0x090c
OP_BuyPlayerHouseMsg EmuOpcode = 0x090d
OP_BuyPlayerHouseTintMsg EmuOpcode = 0x090e
OP_CollectAllHouseItemsMsg EmuOpcode = 0x090f
OP_RelinquishHouseMsg EmuOpcode = 0x0910
OP_EnterHouseMsg EmuOpcode = 0x0911
OP_ExitHouseMsg EmuOpcode = 0x0912
OP_HouseDefaultAccessSetMsg EmuOpcode = 0x0913
OP_HouseAccessSetMsg EmuOpcode = 0x0914
OP_HouseAccessRemoveMsg EmuOpcode = 0x0915
OP_HousingDataChangedMsg EmuOpcode = 0x0916
OP_HousingRestoreMsg EmuOpcode = 0x0917
OP_HouseCustomizationScreenMsg EmuOpcode = 0x0918
OP_CustomizationPurchaseRequestMs EmuOpcode = 0x0919
OP_CustomizationSetRequestMsg EmuOpcode = 0x091a
OP_CustomizationReplyMsg EmuOpcode = 0x091b
OP_TintWidgetsMsg EmuOpcode = 0x091c
OP_MoveableObjectPlacementCriteri EmuOpcode = 0x091d
OP_EnterMoveObjectModeMsg EmuOpcode = 0x091e
OP_PositionMoveableObject EmuOpcode = 0x091f
OP_CancelMoveObjectModeMsg EmuOpcode = 0x0920
OP_ShaderCustomizationMsg EmuOpcode = 0x0921
OP_ReplaceableSubMeshesMsg EmuOpcode = 0x0922
OP_CharacterHousingList EmuOpcode = 0x0923
OP_CharacterCreatedDungeons EmuOpcode = 0x0924
// Achievement system
OP_AchievementUpdateMsg EmuOpcode = 0x0a00
OP_AchievementCompleteMsg EmuOpcode = 0x0a01
OP_CharacterAchievements EmuOpcode = 0x0a02
// Keybinds, macros and UI
OP_KeybindUpdateMsg EmuOpcode = 0x0b00
OP_KeybindLoadMsg EmuOpcode = 0x0b01
OP_MacroUpdateMsg EmuOpcode = 0x0b02
OP_UISettingsMsg EmuOpcode = 0x0b03
OP_UISettingsResponseMsg EmuOpcode = 0x0b04
OP_UIResetMsg EmuOpcode = 0x0b05
OP_KeymapLoadMsg EmuOpcode = 0x0b06
OP_KeymapNoneMsg EmuOpcode = 0x0b07
OP_KeymapDataMsg EmuOpcode = 0x0b08
OP_KeymapSaveMsg EmuOpcode = 0x0b09
OP_QuickbarInitMsg EmuOpcode = 0x0b0a
OP_QuickbarUpdateMsg EmuOpcode = 0x0b0b
OP_MacroInitMsg EmuOpcode = 0x0b0c
OP_QuestionnaireMsg EmuOpcode = 0x0b0d
OP_DisplayInnVisitScreenMsg EmuOpcode = 0x0b0e
OP_DisplayWarningMsg EmuOpcode = 0x0b0f
OP_OnscreenMsgMsg EmuOpcode = 0x0b10
OP_DisplayMailScreenMsg EmuOpcode = 0x0b11
OP_UIEvent EmuOpcode = 0x0b12
OP_LoadWelcomeWindow EmuOpcode = 0x0b13
OP_LoadCalendarEvents EmuOpcode = 0x0b14
// Mail system
OP_MailGetHeadersRequestMsg EmuOpcode = 0x0c00
OP_MailGetHeadersReplyMsg EmuOpcode = 0x0c01
OP_MailGetMessageRequestMsg EmuOpcode = 0x0c02
OP_MailGetMessageReplyMsg EmuOpcode = 0x0c03
OP_MailSendMessageRequestMsg EmuOpcode = 0x0c04
OP_MailSendMessageReplyMsg EmuOpcode = 0x0c05
OP_MailDeleteMessageRequestMsg EmuOpcode = 0x0c06
OP_MailGetMessageMsg EmuOpcode = 0x0c07
OP_MailSendMessageMsg EmuOpcode = 0x0c08
OP_MailDeleteMessageMsg EmuOpcode = 0x0c09
OP_MailCommitSendMessageMsg EmuOpcode = 0x0c0a
OP_MailSendSystemMessageMsg EmuOpcode = 0x0c0b
OP_MailRemoveAttachFromMailMsg EmuOpcode = 0x0c0c
OP_MailEventNotificationMsg EmuOpcode = 0x0c0d
OP_CorruptedClientMsg EmuOpcode = 0x0c0e
OP_NotifyApprenticeStoppedMentori EmuOpcode = 0x0c0f
// Quest system
OP_QuestJournalUpdateMsg EmuOpcode = 0x0d00
OP_QuestJournalReplyMsg EmuOpcode = 0x0d01
OP_QuestOfferMsg EmuOpcode = 0x0d02
OP_QuestRewardMsg EmuOpcode = 0x0d03
OP_QuestCompleteMsg EmuOpcode = 0x0d04
OP_QuestFailedMsg EmuOpcode = 0x0d05
OP_OfferQuestMsg EmuOpcode = 0x0d06
OP_QuestJournalOpenMsg EmuOpcode = 0x0d07
OP_QuestJournalInspectMsg EmuOpcode = 0x0d08
OP_QuestJournalSetVisibleMsg EmuOpcode = 0x0d09
OP_QuestJournalWaypointMsg EmuOpcode = 0x0d0a
OP_QuestReward EmuOpcode = 0x0d0b
OP_JournalQuestStoryline EmuOpcode = 0x0d0c
OP_DailyObjectives EmuOpcode = 0x0d0d
// Guild operations extended
OP_GuildUpdateMsg EmuOpcode = 0x0e00
OP_CreateGuildRequestMsg EmuOpcode = 0x0e01
OP_CreateGuildReplyMsg EmuOpcode = 0x0e02
OP_GuildInviteMsg EmuOpcode = 0x0e03
OP_GuildInviteResponseMsg EmuOpcode = 0x0e04
OP_GuildKickMsg EmuOpcode = 0x0e05
OP_GuildMotdMsg EmuOpcode = 0x0e06
OP_GuildsayMsg EmuOpcode = 0x0e07
OP_FellowshipExpMsg EmuOpcode = 0x0e08
OP_ModifyGuildMsg EmuOpcode = 0x0e09
OP_GuildEventMsg EmuOpcode = 0x0e0a
OP_GuildEventAddMsg EmuOpcode = 0x0e0b
OP_GuildEventActionMsg EmuOpcode = 0x0e0c
OP_GuildEventListMsg EmuOpcode = 0x0e0d
OP_RequestGuildEventDetailsMsg EmuOpcode = 0x0e0e
OP_GuildEventDetailsMsg EmuOpcode = 0x0e0f
OP_RequestGuildBankEventDetailsMs EmuOpcode = 0x0e10
OP_GuildBankUpdateMsg EmuOpcode = 0x0e11
OP_GuildBankEventListMsg EmuOpcode = 0x0e12
OP_RenameGuildMsg EmuOpcode = 0x0e13
OP_DeleteGuildMsg EmuOpcode = 0x0e14
OP_RequestGuildMembershipMsg EmuOpcode = 0x0e15
OP_GuildMembershipResponseMsg EmuOpcode = 0x0e16
OP_LeaveGuildNotifyMsg EmuOpcode = 0x0e17
OP_JoinGuildNotifyMsg EmuOpcode = 0x0e18
OP_RequestGuildInfoMsg EmuOpcode = 0x0e19
// Consignment and broker
OP_ConsignmentCloseStoreMsg EmuOpcode = 0x0f00
OP_ConsignItemRequestMsg EmuOpcode = 0x0f01
OP_ConsignItemResponseMsg EmuOpcode = 0x0f02
OP_PurchaseConsignmentLoreCheckRe EmuOpcode = 0x0f03
OP_ExamineConsignmentRequestMsg EmuOpcode = 0x0f04
OP_ExamineConsignmentResponseMsg EmuOpcode = 0x0f05
OP_ConsignViewCreateMsg EmuOpcode = 0x0f06
OP_ConsignViewGetPageMsg EmuOpcode = 0x0f07
OP_ConsignViewReleaseMsg EmuOpcode = 0x0f08
OP_ConsignRemoveItemsMsg EmuOpcode = 0x0f09
OP_ConsignViewSortMsg EmuOpcode = 0x0f0a
OP_BrokerAddBag EmuOpcode = 0x0f0b
// Character transfer
OP_CharTransferStartRequestMsg EmuOpcode = 0x1000
OP_CharTransferStartReplyMsg EmuOpcode = 0x1001
OP_CharTransferRequestMsg EmuOpcode = 0x1002
OP_CharTransferReplyMsg EmuOpcode = 0x1003
OP_CharTransferRollbackRequestMsg EmuOpcode = 0x1004
OP_CharTransferCommitRequestMsg EmuOpcode = 0x1005
OP_CharTransferRollbackReplyMsg EmuOpcode = 0x1006
OP_CharTransferCommitReplyMsg EmuOpcode = 0x1007
OP_GetCharacterSerializedRequestM EmuOpcode = 0x1008
OP_GetCharacterSerializedReplyMsg EmuOpcode = 0x1009
OP_CreateCharFromCBBRequestMsg EmuOpcode = 0x100a
OP_CreateCharFromCBBReplyMsg EmuOpcode = 0x100b
OP_CharTransferValidateRequestMsg EmuOpcode = 0x100c
OP_CharTransferValidateReplyMsg EmuOpcode = 0x100d
OP_CharacterLinkdeadMsg EmuOpcode = 0x100e
// Auction system
OP_AuctionItem EmuOpcode = 0x1100
OP_AuctionItemReply EmuOpcode = 0x1101
OP_AuctionCoin EmuOpcode = 0x1102
OP_AuctionCoinReply EmuOpcode = 0x1103
OP_AuctionCharacter EmuOpcode = 0x1104
OP_AuctionCharacterReply EmuOpcode = 0x1105
OP_AuctionCommitMsg EmuOpcode = 0x1106
OP_AuctionAbortMsg EmuOpcode = 0x1107
// EQ command system (client commands)
OP_EqHearChatCmd EmuOpcode = 0x1200
OP_EqDisplayTextCmd EmuOpcode = 0x1201
OP_EqCreateGhostCmd EmuOpcode = 0x1202
OP_EqCreateWidgetCmd EmuOpcode = 0x1203
OP_EqCreateSignWidgetCmd EmuOpcode = 0x1204
OP_EqDestroyGhostCmd EmuOpcode = 0x1205
OP_EqUpdateGhostCmd EmuOpcode = 0x1206
OP_EqSetControlGhostCmd EmuOpcode = 0x1207
OP_EqSetPOVGhostCmd EmuOpcode = 0x1208
OP_EqHearCombatCmd EmuOpcode = 0x1209
OP_EqHearSpellCastCmd EmuOpcode = 0x120a
OP_EqHearSpellInterruptCmd EmuOpcode = 0x120b
OP_EqHearSpellFizzleCmd EmuOpcode = 0x120c
OP_EqHearConsiderCmd EmuOpcode = 0x120d
OP_EqUpdateSubClassesCmd EmuOpcode = 0x120e
OP_EqCreateListBoxCmd EmuOpcode = 0x120f
OP_EqSetDebugPathPointsCmd EmuOpcode = 0x1210
OP_EqCannedEmoteCmd EmuOpcode = 0x1211
OP_EqStateCmd EmuOpcode = 0x1212
OP_EqPlaySoundCmd EmuOpcode = 0x1213
OP_EqPlaySound3DCmd EmuOpcode = 0x1214
OP_EqPlayVoiceCmd EmuOpcode = 0x1215
OP_EqHearDrowningCmd EmuOpcode = 0x1216
OP_EqHearDeathCmd EmuOpcode = 0x1217
OP_EqGroupMemberRemovedCmd EmuOpcode = 0x1218
OP_EqHearChainEffectCmd EmuOpcode = 0x1219
OP_EqReceiveOfferCmd EmuOpcode = 0x121a
OP_EqInspectPCResultsCmd EmuOpcode = 0x121b
OP_EqDrawablePathGraphCmd EmuOpcode = 0x121c
OP_EqDialogOpenCmd EmuOpcode = 0x121d
OP_EqDialogCloseCmd EmuOpcode = 0x121e
OP_EqCollectionUpdateCmd EmuOpcode = 0x121f
OP_EqCollectionFilterCmd EmuOpcode = 0x1220
OP_EqCollectionItemCmd EmuOpcode = 0x1221
OP_EqQuestJournalUpdateCmd EmuOpcode = 0x1222
OP_EqQuestJournalReplyCmd EmuOpcode = 0x1223
OP_EqQuestGroupCmd EmuOpcode = 0x1224
OP_EqUpdateMerchantCmd EmuOpcode = 0x1225
OP_EqUpdateStoreCmd EmuOpcode = 0x1226
OP_EqUpdatePlayerTradeCmd EmuOpcode = 0x1227
OP_EqHelpPathCmd EmuOpcode = 0x1228
OP_EqHelpPathClearCmd EmuOpcode = 0x1229
OP_EqUpdateBankCmd EmuOpcode = 0x122a
OP_EqExamineInfoCmd EmuOpcode = 0x122b
OP_EqCloseWindowCmd EmuOpcode = 0x122c
OP_EqUpdateLootCmd EmuOpcode = 0x122d
OP_EqJunctionListCmd EmuOpcode = 0x122e
OP_EqShowDeathWindowCmd EmuOpcode = 0x122f
OP_EqDisplaySpellFailCmd EmuOpcode = 0x1230
OP_EqSpellCastStartCmd EmuOpcode = 0x1231
OP_EqSpellCastEndCmd EmuOpcode = 0x1232
OP_EqResurrectedCmd EmuOpcode = 0x1233
OP_EqChoiceWinCmd EmuOpcode = 0x1234
OP_EqSetDefaultVerbCmd EmuOpcode = 0x1235
OP_EqInstructionWindowCmd EmuOpcode = 0x1236
OP_EqInstructionWindowCloseCmd EmuOpcode = 0x1237
OP_EqInstructionWindowGoalCmd EmuOpcode = 0x1238
OP_EqInstructionWindowTaskCmd EmuOpcode = 0x1239
OP_EqEnableGameEventCmd EmuOpcode = 0x123a
OP_EqShowWindowCmd EmuOpcode = 0x123b
OP_EqEnableWindowCmd EmuOpcode = 0x123c
OP_EqFlashWindowCmd EmuOpcode = 0x123d
OP_EqHearPlayFlavorCmd EmuOpcode = 0x123e
OP_EqUpdateSignWidgetCmd EmuOpcode = 0x123f
OP_EqDebugPVDCmd EmuOpcode = 0x1240
OP_EqShowBookCmd EmuOpcode = 0x1241
OP_EqQuestionnaireCmd EmuOpcode = 0x1242
OP_EqGetProbsCmd EmuOpcode = 0x1243
OP_EqHearHealCmd EmuOpcode = 0x1244
OP_EqChatChannelUpdateCmd EmuOpcode = 0x1245
OP_EqWhoChannelQueryReplyCmd EmuOpcode = 0x1246
OP_EqAvailWorldChannelsCmd EmuOpcode = 0x1247
OP_EqUpdateTargetCmd EmuOpcode = 0x1248
OP_EqConsignmentItemsCmd EmuOpcode = 0x1249
OP_EqStartBrokerCmd EmuOpcode = 0x124a
OP_EqMapExplorationCmd EmuOpcode = 0x124b
OP_EqStoreLogCmd EmuOpcode = 0x124c
OP_EqSpellMoveToRangeAndRetryCmd EmuOpcode = 0x124d
OP_EqUpdatePlayerMailCmd EmuOpcode = 0x124e
OP_EqFactionUpdateCmd EmuOpcode = 0x124f
OP_EQHearThreatCmd EmuOpcode = 0x1250
OP_EqHearSpellNoLandCmd EmuOpcode = 0x1251
OP_EQHearDispellCmd EmuOpcode = 0x1252
OP_EqTargetItemCmd EmuOpcode = 0x1253
// Various game features
OP_DialogCloseMsg EmuOpcode = 0x1300
OP_UpdateTitleCmd EmuOpcode = 0x1301
OP_BadLanguageFilter EmuOpcode = 0x1302
OP_DressingRoom EmuOpcode = 0x1303
OP_TraitsList EmuOpcode = 0x1304
OP_PointOfInterest EmuOpcode = 0x1305
OP_AdventureList EmuOpcode = 0x1306
OP_RecipeList EmuOpcode = 0x1307
OP_BagOptions EmuOpcode = 0x1308
OP_PetOptions EmuOpcode = 0x1309
OP_CharacterPet EmuOpcode = 0x130a
OP_ClearForTakeOffMsg EmuOpcode = 0x130b
OP_CharacterCurrency EmuOpcode = 0x130c
OP_TradeskillList EmuOpcode = 0x130d
OP_RecipeBook EmuOpcode = 0x130e
OP_CharacterMerc EmuOpcode = 0x130f
OP_AfterInvSpellUpdate EmuOpcode = 0x1310
OP_CharacterMounts EmuOpcode = 0x1311
OP_DungeonMakerItemRequest EmuOpcode = 0x1312
OP_SysClient EmuOpcode = 0x1313
OP_LFGGroupSearch EmuOpcode = 0x1314
OP_MarketPlacePrices EmuOpcode = 0x1315
OP_MarketFundsUpdate EmuOpcode = 0x1316
OP_MarketAddFundsRequest EmuOpcode = 0x1317
OP_ZoneBgInstanceList EmuOpcode = 0x1318
OP_Launchpad EmuOpcode = 0x1319
OP_Weakness EmuOpcode = 0x131a
OP_SavageBarInitMsg EmuOpcode = 0x131b
OP_PetOptionsResponse EmuOpcode = 0x131c
OP_CurrentPet EmuOpcode = 0x131d
OP_RecipeListUnknown EmuOpcode = 0x131e
OP_ClearForLandingMsg EmuOpcode = 0x131f
OP_LikeOption EmuOpcode = 0x1320
OP_HeritageMsg EmuOpcode = 0x1321
OP_OpenCharCust EmuOpcode = 0x1322
OP_PaperdollImage EmuOpcode = 0x1323
OP_ReadyForTakeOffMsg EmuOpcode = 0x1324
OP_EarlyLandingRequestMsg EmuOpcode = 0x1325
OP_SubmitCharCust EmuOpcode = 0x1326
OP_DietyAbilityWindow EmuOpcode = 0x1327
OP_ArenaGameTypesMsg EmuOpcode = 0x1328
OP_RaceRestrictionMsg EmuOpcode = 0x1329
OP_SetInstanceDisplayNameMsg EmuOpcode = 0x132a
// Entity and verbs
OP_EntityVerbsRequestMsg EmuOpcode = 0x1400
OP_EntityVerbsReplyMsg EmuOpcode = 0x1401
OP_EntityVerbsVerbMsg EmuOpcode = 0x1402
// Waypoints and navigation
OP_WaypointRequestMsg EmuOpcode = 0x1500
OP_WaypointReplyMsg EmuOpcode = 0x1501
OP_WaypointSelectMsg EmuOpcode = 0x1502
OP_WaypointUpdateMsg EmuOpcode = 0x1503
OP_ZoneToFriendRequestMsg EmuOpcode = 0x1504
OP_ZoneToFriendReplyMsg EmuOpcode = 0x1505
OP_FlightPathsMsg EmuOpcode = 0x1506
OP_ShowZoneTeleporterDestinations EmuOpcode = 0x1507
OP_SelectZoneTeleporterDestinatio EmuOpcode = 0x1508
// CS Tools and tickets
OP_CSTicketHeaderRequestMsg EmuOpcode = 0x1600
OP_CSTicketInfoMsg EmuOpcode = 0x1601
OP_CSTicketCommentRequestMsg EmuOpcode = 0x1602
OP_CSTicketCommentResponseMsg EmuOpcode = 0x1603
OP_CSTicketCreateMsg EmuOpcode = 0x1604
OP_CSTicketAddCommentMsg EmuOpcode = 0x1605
OP_CSTicketDeleteMsg EmuOpcode = 0x1606
OP_CSTicketChangeNotificationMsg EmuOpcode = 0x1607
OP_CSToolsRequestMsg EmuOpcode = 0x1608
OP_CSToolsResponseMsg EmuOpcode = 0x1609
OP_CSToolAccessResponseMsg EmuOpcode = 0x160a
OP_GetAvatarAccessRequestForCSToo EmuOpcode = 0x160b
OP_CsCategoryRequestMsg EmuOpcode = 0x160c
OP_CsCategoryResponseMsg EmuOpcode = 0x160d
// Knowledge base
OP_KnowledgebaseRequestMsg EmuOpcode = 0x1700
OP_KnowledgebaseResponseMsg EmuOpcode = 0x1701
OP_KnowledgeWindowSlotMappingMsg EmuOpcode = 0x1702
OP_KnownLanguagesMsg EmuOpcode = 0x1703
// Tracking
OP_TrackingUpdateMsg EmuOpcode = 0x1800
OP_BeginTrackingMsg EmuOpcode = 0x1801
OP_StopTrackingMsg EmuOpcode = 0x1802
// Advancement
OP_AdvancementRequestMsg EmuOpcode = 0x1900
// Map fog
OP_MapFogDataInitMsg EmuOpcode = 0x1a00
OP_MapFogDataUpdateMsg EmuOpcode = 0x1a01
// Crafting
OP_ShowCreateFromRecipeUIMsg EmuOpcode = 0x1b00
OP_CancelCreateFromRecipeMsg EmuOpcode = 0x1b01
OP_BeginItemCreationMsg EmuOpcode = 0x1b02
OP_StopItemCreationMsg EmuOpcode = 0x1b03
OP_ShowItemCreationProcessUIMsg EmuOpcode = 0x1b04
OP_UpdateItemCreationProcessUIMsg EmuOpcode = 0x1b05
OP_DisplayTSEventReactionMsg EmuOpcode = 0x1b06
OP_ShowRecipeBookMsg EmuOpcode = 0x1b07
// Boat transport
OP_CreateBoatTransportsMsg EmuOpcode = 0x1c00
OP_PositionBoatTransportMsg EmuOpcode = 0x1c01
OP_MigrateBoatTransportMsg EmuOpcode = 0x1c02
OP_MigrateBoatTransportReplyMsg EmuOpcode = 0x1c03
// Debug
OP_DisplayDebugNLLPointsMsg EmuOpcode = 0x1d00
OP_UpdateDebugRadiiMsg EmuOpcode = 0x1d01
OP_DumpSchedulerMsg EmuOpcode = 0x1d02
// Client state
OP_UpdateClientPredFlagsMsg EmuOpcode = 0x1e00
OP_ChangeServerControlFlagMsg EmuOpcode = 0x1e01
OP_ClientIdleBeginMsg EmuOpcode = 0x1e02
OP_ClientIdleEndMsg EmuOpcode = 0x1e03
OP_ClientFellMsg EmuOpcode = 0x1e04
OP_ClientInDeathRegionMsg EmuOpcode = 0x1e05
OP_CampClientMsg EmuOpcode = 0x1e06
// Various
OP_RewardPackMsg EmuOpcode = 0x1f00
OP_CharNameChangedMsg EmuOpcode = 0x1f01
OP_ReloadLocalizedTxtMsg EmuOpcode = 0x1f02
OP_UnknownNpcMsg EmuOpcode = 0x1f03
OP_PromoFlagsDetailsMsg EmuOpcode = 0x1f04
OP_ReportMsg EmuOpcode = 0x1f05
OP_UpdateRaidMsg EmuOpcode = 0x1f06
OP_TitleUpdateMsg EmuOpcode = 0x1f07
OP_MoveLogUpdateMsg EmuOpcode = 0x1f08
OP_PerformPlayerKnockbackMsg EmuOpcode = 0x1f09
OP_PerformCameraShakeMsg EmuOpcode = 0x1f0a
OP_EncounterBrokenMsg EmuOpcode = 0x1f0b
OP_RequestHelpRepathMsg EmuOpcode = 0x1f0c
// Crash logs
OP_LsRequestClientCrashLogMsg EmuOpcode = 0x2000
OP_LsClientBaselogReplyMsg EmuOpcode = 0x2001
OP_LsClientCrashlogReplyMsg EmuOpcode = 0x2002
OP_LsClientAlertlogReplyMsg EmuOpcode = 0x2003
OP_LsClientVerifylogReplyMsg EmuOpcode = 0x2004
// Server lock
OP_WSServerLockMsg EmuOpcode = 0x2100
OP_WSServerHideMsg EmuOpcode = 0x2101
OP_LSServerLockMsg EmuOpcode = 0x2102
OP_WSAcctLockStatusMsg EmuOpcode = 0x2103
OP_LSCheckAcctLockMsg EmuOpcode = 0x2104
)

84
opcodes/gen/main.go Normal file
View File

@ -0,0 +1,84 @@
//go:build ignore
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"sort"
"strings"
"text/template"
)
const outputTemplate = `// Code generated by gen/main.go; DO NOT EDIT.
package opcodes
// initializeNameMappings populates nameToEmu from constants
func (om *OpcodeManager) initializeNameMappings() {
{{- range . }}
om.nameToEmu["{{ .Name }}"] = {{ .Name }}
{{- end }}
}
`
type OpcodeInfo struct {
Name string
}
func main() {
opcodes := []OpcodeInfo{}
// Parse each source file for OP_ constants
files := []string{"login.go", "emu.go"}
for _, filename := range files {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse %s: %v\n", filename, err)
continue
}
// Find all OP_ and LS_ constants
ast.Inspect(node, func(n ast.Node) bool {
spec, ok := n.(*ast.ValueSpec)
if !ok {
return true
}
for _, name := range spec.Names {
if strings.HasPrefix(name.Name, "OP_") || strings.HasPrefix(name.Name, "LS_") {
opcodes = append(opcodes, OpcodeInfo{Name: name.Name})
}
}
return true
})
}
// Sort for consistent output
sort.Slice(opcodes, func(i, j int) bool {
return opcodes[i].Name < opcodes[j].Name
})
// Generate output
tmpl, err := template.New("opcodes").Parse(outputTemplate)
if err != nil {
panic(err)
}
file, err := os.Create("generated.go")
if err != nil {
panic(err)
}
defer file.Close()
if err := tmpl.Execute(file, opcodes); err != nil {
panic(err)
}
fmt.Printf("Generated %d opcode mappings\n", len(opcodes))
}

621
opcodes/generated.go Normal file
View File

@ -0,0 +1,621 @@
// Code generated by gen/main.go; DO NOT EDIT.
package opcodes
// initializeNameMappings populates nameToEmu from constants
func (om *OpcodeManager) initializeNameMappings() {
om.nameToEmu["LS_AccessGrantedMsg"] = LS_AccessGrantedMsg
om.nameToEmu["LS_AllCharactersDescReplyMsg"] = LS_AllCharactersDescReplyMsg
om.nameToEmu["LS_AllCharactersDescRequestMsg"] = LS_AllCharactersDescRequestMsg
om.nameToEmu["LS_AllWSDescRequestMsg"] = LS_AllWSDescRequestMsg
om.nameToEmu["LS_AllWorldsDescReplyMsg"] = LS_AllWorldsDescReplyMsg
om.nameToEmu["LS_AllWorldsDescRequestMsg"] = LS_AllWorldsDescRequestMsg
om.nameToEmu["LS_BadLanguageFilter"] = LS_BadLanguageFilter
om.nameToEmu["LS_CharListReplyMsg"] = LS_CharListReplyMsg
om.nameToEmu["LS_CharListRequestMsg"] = LS_CharListRequestMsg
om.nameToEmu["LS_CharSelectMsg"] = LS_CharSelectMsg
om.nameToEmu["LS_CreateAccountReplyMsg"] = LS_CreateAccountReplyMsg
om.nameToEmu["LS_CreateAccountRequestMsg"] = LS_CreateAccountRequestMsg
om.nameToEmu["LS_CreateCharReplyMsg"] = LS_CreateCharReplyMsg
om.nameToEmu["LS_CreateCharRequestMsg"] = LS_CreateCharRequestMsg
om.nameToEmu["LS_CreateCharacterReplyMsg"] = LS_CreateCharacterReplyMsg
om.nameToEmu["LS_CreateCharacterRequestMsg"] = LS_CreateCharacterRequestMsg
om.nameToEmu["LS_DeleteCharReplyMsg"] = LS_DeleteCharReplyMsg
om.nameToEmu["LS_DeleteCharRequestMsg"] = LS_DeleteCharRequestMsg
om.nameToEmu["LS_DeleteCharacterReplyMsg"] = LS_DeleteCharacterReplyMsg
om.nameToEmu["LS_DeleteCharacterRequestMsg"] = LS_DeleteCharacterRequestMsg
om.nameToEmu["LS_ESLoginRequestMsg"] = LS_ESLoginRequestMsg
om.nameToEmu["LS_KeymapDataMsg"] = LS_KeymapDataMsg
om.nameToEmu["LS_KeymapLoadMsg"] = LS_KeymapLoadMsg
om.nameToEmu["LS_KeymapNoneMsg"] = LS_KeymapNoneMsg
om.nameToEmu["LS_KeymapSaveMsg"] = LS_KeymapSaveMsg
om.nameToEmu["LS_LSCheckAcctLockMsg"] = LS_LSCheckAcctLockMsg
om.nameToEmu["LS_LSServerLockMsg"] = LS_LSServerLockMsg
om.nameToEmu["LS_LSStatusReplyMsg"] = LS_LSStatusReplyMsg
om.nameToEmu["LS_LSStatusRequestMsg"] = LS_LSStatusRequestMsg
om.nameToEmu["LS_LoginBannedMsg"] = LS_LoginBannedMsg
om.nameToEmu["LS_LoginByNumRequestMsg"] = LS_LoginByNumRequestMsg
om.nameToEmu["LS_LoginFailedMsg"] = LS_LoginFailedMsg
om.nameToEmu["LS_LoginReplyMsg"] = LS_LoginReplyMsg
om.nameToEmu["LS_LoginRequestMsg"] = LS_LoginRequestMsg
om.nameToEmu["LS_LsClientAlertlogReplyMsg"] = LS_LsClientAlertlogReplyMsg
om.nameToEmu["LS_LsClientBaselogReplyMsg"] = LS_LsClientBaselogReplyMsg
om.nameToEmu["LS_LsClientCrashlogReplyMsg"] = LS_LsClientCrashlogReplyMsg
om.nameToEmu["LS_LsClientVerifylogReplyMsg"] = LS_LsClientVerifylogReplyMsg
om.nameToEmu["LS_LsRequestClientCrashLogMsg"] = LS_LsRequestClientCrashLogMsg
om.nameToEmu["LS_PlayCharReplyMsg"] = LS_PlayCharReplyMsg
om.nameToEmu["LS_PlayCharRequestMsg"] = LS_PlayCharRequestMsg
om.nameToEmu["LS_PlayCharacterReplyMsg"] = LS_PlayCharacterReplyMsg
om.nameToEmu["LS_PlayCharacterRequestMsg"] = LS_PlayCharacterRequestMsg
om.nameToEmu["LS_ReskinCharacterRequestMsg"] = LS_ReskinCharacterRequestMsg
om.nameToEmu["LS_ServerListReplyMsg"] = LS_ServerListReplyMsg
om.nameToEmu["LS_ServerListRequestMsg"] = LS_ServerListRequestMsg
om.nameToEmu["LS_ServerPlayCharacterReplyMsg"] = LS_ServerPlayCharacterReplyMsg
om.nameToEmu["LS_ServerPlayCharacterRequestMsg"] = LS_ServerPlayCharacterRequestMsg
om.nameToEmu["LS_ServerStatusReplyMsg"] = LS_ServerStatusReplyMsg
om.nameToEmu["LS_ServerStatusRequestMsg"] = LS_ServerStatusRequestMsg
om.nameToEmu["LS_SessionReadyMsg"] = LS_SessionReadyMsg
om.nameToEmu["LS_UpdateCharacterSheetMsg"] = LS_UpdateCharacterSheetMsg
om.nameToEmu["LS_UpdateInventoryMsg"] = LS_UpdateInventoryMsg
om.nameToEmu["LS_UsertoWorldReq"] = LS_UsertoWorldReq
om.nameToEmu["LS_UsertoWorldResp"] = LS_UsertoWorldResp
om.nameToEmu["LS_WSAcctLockStatusMsg"] = LS_WSAcctLockStatusMsg
om.nameToEmu["LS_WSCreateCharacterReplyMsg"] = LS_WSCreateCharacterReplyMsg
om.nameToEmu["LS_WSCreateCharacterRequestMsg"] = LS_WSCreateCharacterRequestMsg
om.nameToEmu["LS_WSLoginRequestMsg"] = LS_WSLoginRequestMsg
om.nameToEmu["LS_WSServerHideMsg"] = LS_WSServerHideMsg
om.nameToEmu["LS_WSServerLockMsg"] = LS_WSServerLockMsg
om.nameToEmu["LS_WSStatusReplyMsg"] = LS_WSStatusReplyMsg
om.nameToEmu["LS_WorldListMsg"] = LS_WorldListMsg
om.nameToEmu["LS_WorldStatusChangeMsg"] = LS_WorldStatusChangeMsg
om.nameToEmu["LS_WorldStatusMsg"] = LS_WorldStatusMsg
om.nameToEmu["OP_AFKUpdateMsg"] = OP_AFKUpdateMsg
om.nameToEmu["OP_AchievementCompleteMsg"] = OP_AchievementCompleteMsg
om.nameToEmu["OP_AchievementUpdateMsg"] = OP_AchievementUpdateMsg
om.nameToEmu["OP_AdvancementRequestMsg"] = OP_AdvancementRequestMsg
om.nameToEmu["OP_AdventureList"] = OP_AdventureList
om.nameToEmu["OP_AfterInvSpellUpdate"] = OP_AfterInvSpellUpdate
om.nameToEmu["OP_AnonUpdateMsg"] = OP_AnonUpdateMsg
om.nameToEmu["OP_ArenaGameTypesMsg"] = OP_ArenaGameTypesMsg
om.nameToEmu["OP_AttackAllowed"] = OP_AttackAllowed
om.nameToEmu["OP_AttackMsg"] = OP_AttackMsg
om.nameToEmu["OP_AttackNotAllowed"] = OP_AttackNotAllowed
om.nameToEmu["OP_AuctionAbortMsg"] = OP_AuctionAbortMsg
om.nameToEmu["OP_AuctionCharacter"] = OP_AuctionCharacter
om.nameToEmu["OP_AuctionCharacterReply"] = OP_AuctionCharacterReply
om.nameToEmu["OP_AuctionCoin"] = OP_AuctionCoin
om.nameToEmu["OP_AuctionCoinReply"] = OP_AuctionCoinReply
om.nameToEmu["OP_AuctionCommitMsg"] = OP_AuctionCommitMsg
om.nameToEmu["OP_AuctionItem"] = OP_AuctionItem
om.nameToEmu["OP_AuctionItemReply"] = OP_AuctionItemReply
om.nameToEmu["OP_AutoAttackMsg"] = OP_AutoAttackMsg
om.nameToEmu["OP_AvatarCreatedMsg"] = OP_AvatarCreatedMsg
om.nameToEmu["OP_AvatarDestroyedMsg"] = OP_AvatarDestroyedMsg
om.nameToEmu["OP_AvatarUpdateMsg"] = OP_AvatarUpdateMsg
om.nameToEmu["OP_BadLanguageFilter"] = OP_BadLanguageFilter
om.nameToEmu["OP_BagOptions"] = OP_BagOptions
om.nameToEmu["OP_BeginItemCreationMsg"] = OP_BeginItemCreationMsg
om.nameToEmu["OP_BeginTrackingMsg"] = OP_BeginTrackingMsg
om.nameToEmu["OP_BioUpdateMsg"] = OP_BioUpdateMsg
om.nameToEmu["OP_BrokerAddBag"] = OP_BrokerAddBag
om.nameToEmu["OP_BuyPlayerHouseMsg"] = OP_BuyPlayerHouseMsg
om.nameToEmu["OP_BuyPlayerHouseTintMsg"] = OP_BuyPlayerHouseTintMsg
om.nameToEmu["OP_CSTicketAddCommentMsg"] = OP_CSTicketAddCommentMsg
om.nameToEmu["OP_CSTicketChangeNotificationMsg"] = OP_CSTicketChangeNotificationMsg
om.nameToEmu["OP_CSTicketCommentRequestMsg"] = OP_CSTicketCommentRequestMsg
om.nameToEmu["OP_CSTicketCommentResponseMsg"] = OP_CSTicketCommentResponseMsg
om.nameToEmu["OP_CSTicketCreateMsg"] = OP_CSTicketCreateMsg
om.nameToEmu["OP_CSTicketDeleteMsg"] = OP_CSTicketDeleteMsg
om.nameToEmu["OP_CSTicketHeaderRequestMsg"] = OP_CSTicketHeaderRequestMsg
om.nameToEmu["OP_CSTicketInfoMsg"] = OP_CSTicketInfoMsg
om.nameToEmu["OP_CSToolAccessResponseMsg"] = OP_CSToolAccessResponseMsg
om.nameToEmu["OP_CSToolsRequestMsg"] = OP_CSToolsRequestMsg
om.nameToEmu["OP_CSToolsResponseMsg"] = OP_CSToolsResponseMsg
om.nameToEmu["OP_CampAbortedMsg"] = OP_CampAbortedMsg
om.nameToEmu["OP_CampClientMsg"] = OP_CampClientMsg
om.nameToEmu["OP_CampStartedMsg"] = OP_CampStartedMsg
om.nameToEmu["OP_CancelCreateFromRecipeMsg"] = OP_CancelCreateFromRecipeMsg
om.nameToEmu["OP_CancelMoveObjectModeMsg"] = OP_CancelMoveObjectModeMsg
om.nameToEmu["OP_CancelSpellCast"] = OP_CancelSpellCast
om.nameToEmu["OP_CancelledFeignMsg"] = OP_CancelledFeignMsg
om.nameToEmu["OP_CastSpellMsg"] = OP_CastSpellMsg
om.nameToEmu["OP_ChangeServerControlFlagMsg"] = OP_ChangeServerControlFlagMsg
om.nameToEmu["OP_ChangeZoneMsg"] = OP_ChangeZoneMsg
om.nameToEmu["OP_CharNameChangedMsg"] = OP_CharNameChangedMsg
om.nameToEmu["OP_CharTransferCommitReplyMsg"] = OP_CharTransferCommitReplyMsg
om.nameToEmu["OP_CharTransferCommitRequestMsg"] = OP_CharTransferCommitRequestMsg
om.nameToEmu["OP_CharTransferReplyMsg"] = OP_CharTransferReplyMsg
om.nameToEmu["OP_CharTransferRequestMsg"] = OP_CharTransferRequestMsg
om.nameToEmu["OP_CharTransferRollbackReplyMsg"] = OP_CharTransferRollbackReplyMsg
om.nameToEmu["OP_CharTransferRollbackRequestMsg"] = OP_CharTransferRollbackRequestMsg
om.nameToEmu["OP_CharTransferStartReplyMsg"] = OP_CharTransferStartReplyMsg
om.nameToEmu["OP_CharTransferStartRequestMsg"] = OP_CharTransferStartRequestMsg
om.nameToEmu["OP_CharTransferValidateReplyMsg"] = OP_CharTransferValidateReplyMsg
om.nameToEmu["OP_CharTransferValidateRequestMsg"] = OP_CharTransferValidateRequestMsg
om.nameToEmu["OP_CharacterAchievements"] = OP_CharacterAchievements
om.nameToEmu["OP_CharacterCreateReplyMsg"] = OP_CharacterCreateReplyMsg
om.nameToEmu["OP_CharacterCreateRequestMsg"] = OP_CharacterCreateRequestMsg
om.nameToEmu["OP_CharacterCreatedDungeons"] = OP_CharacterCreatedDungeons
om.nameToEmu["OP_CharacterCurrency"] = OP_CharacterCurrency
om.nameToEmu["OP_CharacterHousingList"] = OP_CharacterHousingList
om.nameToEmu["OP_CharacterLinkdeadMsg"] = OP_CharacterLinkdeadMsg
om.nameToEmu["OP_CharacterMerc"] = OP_CharacterMerc
om.nameToEmu["OP_CharacterMounts"] = OP_CharacterMounts
om.nameToEmu["OP_CharacterPet"] = OP_CharacterPet
om.nameToEmu["OP_ChatCreateChannelMsg"] = OP_ChatCreateChannelMsg
om.nameToEmu["OP_ChatFiltersMsg"] = OP_ChatFiltersMsg
om.nameToEmu["OP_ChatJoinChannelMsg"] = OP_ChatJoinChannelMsg
om.nameToEmu["OP_ChatLeaveChannelMsg"] = OP_ChatLeaveChannelMsg
om.nameToEmu["OP_ChatMsg"] = OP_ChatMsg
om.nameToEmu["OP_ChatRelationshipUpdateMsg"] = OP_ChatRelationshipUpdateMsg
om.nameToEmu["OP_ChatSendFriendsMsg"] = OP_ChatSendFriendsMsg
om.nameToEmu["OP_ChatSendIgnoresMsg"] = OP_ChatSendIgnoresMsg
om.nameToEmu["OP_ChatTellChannelMsg"] = OP_ChatTellChannelMsg
om.nameToEmu["OP_ChatTellUserMsg"] = OP_ChatTellUserMsg
om.nameToEmu["OP_ChatToggleFriendMsg"] = OP_ChatToggleFriendMsg
om.nameToEmu["OP_ChatToggleIgnoreMsg"] = OP_ChatToggleIgnoreMsg
om.nameToEmu["OP_ChatWhoChannelMsg"] = OP_ChatWhoChannelMsg
om.nameToEmu["OP_ClearDataMsg"] = OP_ClearDataMsg
om.nameToEmu["OP_ClearForLandingMsg"] = OP_ClearForLandingMsg
om.nameToEmu["OP_ClearForTakeOffMsg"] = OP_ClearForTakeOffMsg
om.nameToEmu["OP_ClientCmdMsg"] = OP_ClientCmdMsg
om.nameToEmu["OP_ClientFellMsg"] = OP_ClientFellMsg
om.nameToEmu["OP_ClientIdMsg"] = OP_ClientIdMsg
om.nameToEmu["OP_ClientIdleBeginMsg"] = OP_ClientIdleBeginMsg
om.nameToEmu["OP_ClientIdleEndMsg"] = OP_ClientIdleEndMsg
om.nameToEmu["OP_ClientInDeathRegionMsg"] = OP_ClientInDeathRegionMsg
om.nameToEmu["OP_ClientTeleportRequestMsg"] = OP_ClientTeleportRequestMsg
om.nameToEmu["OP_ClientTeleportToLocationMsg"] = OP_ClientTeleportToLocationMsg
om.nameToEmu["OP_CloseGroupInviteWindowMsg"] = OP_CloseGroupInviteWindowMsg
om.nameToEmu["OP_CloseWindowMsg"] = OP_CloseWindowMsg
om.nameToEmu["OP_CollectAllHouseItemsMsg"] = OP_CollectAllHouseItemsMsg
om.nameToEmu["OP_ConsiderMsg"] = OP_ConsiderMsg
om.nameToEmu["OP_ConsignItemRequestMsg"] = OP_ConsignItemRequestMsg
om.nameToEmu["OP_ConsignItemResponseMsg"] = OP_ConsignItemResponseMsg
om.nameToEmu["OP_ConsignRemoveItemsMsg"] = OP_ConsignRemoveItemsMsg
om.nameToEmu["OP_ConsignViewCreateMsg"] = OP_ConsignViewCreateMsg
om.nameToEmu["OP_ConsignViewGetPageMsg"] = OP_ConsignViewGetPageMsg
om.nameToEmu["OP_ConsignViewReleaseMsg"] = OP_ConsignViewReleaseMsg
om.nameToEmu["OP_ConsignViewSortMsg"] = OP_ConsignViewSortMsg
om.nameToEmu["OP_ConsignmentCloseStoreMsg"] = OP_ConsignmentCloseStoreMsg
om.nameToEmu["OP_ConversationMsg"] = OP_ConversationMsg
om.nameToEmu["OP_CorruptedClientMsg"] = OP_CorruptedClientMsg
om.nameToEmu["OP_CreateBoatTransportsMsg"] = OP_CreateBoatTransportsMsg
om.nameToEmu["OP_CreateCharFromCBBReplyMsg"] = OP_CreateCharFromCBBReplyMsg
om.nameToEmu["OP_CreateCharFromCBBRequestMsg"] = OP_CreateCharFromCBBRequestMsg
om.nameToEmu["OP_CreateCharacterReplyMsg"] = OP_CreateCharacterReplyMsg
om.nameToEmu["OP_CreateCharacterRequestMsg"] = OP_CreateCharacterRequestMsg
om.nameToEmu["OP_CreateGuildReplyMsg"] = OP_CreateGuildReplyMsg
om.nameToEmu["OP_CreateGuildRequestMsg"] = OP_CreateGuildRequestMsg
om.nameToEmu["OP_CreateZoneInstanceMsg"] = OP_CreateZoneInstanceMsg
om.nameToEmu["OP_CsCategoryRequestMsg"] = OP_CsCategoryRequestMsg
om.nameToEmu["OP_CsCategoryResponseMsg"] = OP_CsCategoryResponseMsg
om.nameToEmu["OP_CurrentPet"] = OP_CurrentPet
om.nameToEmu["OP_CustomizationPurchaseRequestMs"] = OP_CustomizationPurchaseRequestMs
om.nameToEmu["OP_CustomizationReplyMsg"] = OP_CustomizationReplyMsg
om.nameToEmu["OP_CustomizationSetRequestMsg"] = OP_CustomizationSetRequestMsg
om.nameToEmu["OP_DailyObjectives"] = OP_DailyObjectives
om.nameToEmu["OP_DefaultGroupOptionsMsg"] = OP_DefaultGroupOptionsMsg
om.nameToEmu["OP_DefaultGroupOptionsRequestMsg"] = OP_DefaultGroupOptionsRequestMsg
om.nameToEmu["OP_DeleteGuildMsg"] = OP_DeleteGuildMsg
om.nameToEmu["OP_DeleteItemMsg"] = OP_DeleteItemMsg
om.nameToEmu["OP_DialogCloseMsg"] = OP_DialogCloseMsg
om.nameToEmu["OP_DialogOpenMsg"] = OP_DialogOpenMsg
om.nameToEmu["OP_DialogSelectMsg"] = OP_DialogSelectMsg
om.nameToEmu["OP_DietyAbilityWindow"] = OP_DietyAbilityWindow
om.nameToEmu["OP_DispatchClientCmdMsg"] = OP_DispatchClientCmdMsg
om.nameToEmu["OP_DispatchESMsg"] = OP_DispatchESMsg
om.nameToEmu["OP_DispatchSpellCmdMsg"] = OP_DispatchSpellCmdMsg
om.nameToEmu["OP_DisplayDebugNLLPointsMsg"] = OP_DisplayDebugNLLPointsMsg
om.nameToEmu["OP_DisplayGroupOptionsScreenMsg"] = OP_DisplayGroupOptionsScreenMsg
om.nameToEmu["OP_DisplayInnVisitScreenMsg"] = OP_DisplayInnVisitScreenMsg
om.nameToEmu["OP_DisplayMailScreenMsg"] = OP_DisplayMailScreenMsg
om.nameToEmu["OP_DisplayTSEventReactionMsg"] = OP_DisplayTSEventReactionMsg
om.nameToEmu["OP_DisplayWarningMsg"] = OP_DisplayWarningMsg
om.nameToEmu["OP_DoneLoadingEntityResourcesMsg"] = OP_DoneLoadingEntityResourcesMsg
om.nameToEmu["OP_DoneLoadingUIResourcesMsg"] = OP_DoneLoadingUIResourcesMsg
om.nameToEmu["OP_DoneLoadingZoneResourcesMsg"] = OP_DoneLoadingZoneResourcesMsg
om.nameToEmu["OP_DoneSendingInitialEntitiesMsg"] = OP_DoneSendingInitialEntitiesMsg
om.nameToEmu["OP_DressingRoom"] = OP_DressingRoom
om.nameToEmu["OP_DumpSchedulerMsg"] = OP_DumpSchedulerMsg
om.nameToEmu["OP_DungeonMakerItemRequest"] = OP_DungeonMakerItemRequest
om.nameToEmu["OP_EQHearDispellCmd"] = OP_EQHearDispellCmd
om.nameToEmu["OP_EQHearThreatCmd"] = OP_EQHearThreatCmd
om.nameToEmu["OP_ESInitMsg"] = OP_ESInitMsg
om.nameToEmu["OP_ESReadyForClientsMsg"] = OP_ESReadyForClientsMsg
om.nameToEmu["OP_ESStatusMsg"] = OP_ESStatusMsg
om.nameToEmu["OP_ESWeatherRequestEndMsg"] = OP_ESWeatherRequestEndMsg
om.nameToEmu["OP_ESWeatherRequestMsg"] = OP_ESWeatherRequestMsg
om.nameToEmu["OP_ESZoneInstanceStatusMsg"] = OP_ESZoneInstanceStatusMsg
om.nameToEmu["OP_EarlyLandingRequestMsg"] = OP_EarlyLandingRequestMsg
om.nameToEmu["OP_EncounterBrokenMsg"] = OP_EncounterBrokenMsg
om.nameToEmu["OP_EnterHouseMsg"] = OP_EnterHouseMsg
om.nameToEmu["OP_EnterMoveObjectModeMsg"] = OP_EnterMoveObjectModeMsg
om.nameToEmu["OP_EnterWorldMsg"] = OP_EnterWorldMsg
om.nameToEmu["OP_EntityVerbsReplyMsg"] = OP_EntityVerbsReplyMsg
om.nameToEmu["OP_EntityVerbsRequestMsg"] = OP_EntityVerbsRequestMsg
om.nameToEmu["OP_EntityVerbsVerbMsg"] = OP_EntityVerbsVerbMsg
om.nameToEmu["OP_EqAvailWorldChannelsCmd"] = OP_EqAvailWorldChannelsCmd
om.nameToEmu["OP_EqCannedEmoteCmd"] = OP_EqCannedEmoteCmd
om.nameToEmu["OP_EqChatChannelUpdateCmd"] = OP_EqChatChannelUpdateCmd
om.nameToEmu["OP_EqChoiceWinCmd"] = OP_EqChoiceWinCmd
om.nameToEmu["OP_EqCloseWindowCmd"] = OP_EqCloseWindowCmd
om.nameToEmu["OP_EqCollectionFilterCmd"] = OP_EqCollectionFilterCmd
om.nameToEmu["OP_EqCollectionItemCmd"] = OP_EqCollectionItemCmd
om.nameToEmu["OP_EqCollectionUpdateCmd"] = OP_EqCollectionUpdateCmd
om.nameToEmu["OP_EqConsignmentItemsCmd"] = OP_EqConsignmentItemsCmd
om.nameToEmu["OP_EqCreateGhostCmd"] = OP_EqCreateGhostCmd
om.nameToEmu["OP_EqCreateListBoxCmd"] = OP_EqCreateListBoxCmd
om.nameToEmu["OP_EqCreateSignWidgetCmd"] = OP_EqCreateSignWidgetCmd
om.nameToEmu["OP_EqCreateWidgetCmd"] = OP_EqCreateWidgetCmd
om.nameToEmu["OP_EqDebugPVDCmd"] = OP_EqDebugPVDCmd
om.nameToEmu["OP_EqDestroyGhostCmd"] = OP_EqDestroyGhostCmd
om.nameToEmu["OP_EqDialogCloseCmd"] = OP_EqDialogCloseCmd
om.nameToEmu["OP_EqDialogOpenCmd"] = OP_EqDialogOpenCmd
om.nameToEmu["OP_EqDisplaySpellFailCmd"] = OP_EqDisplaySpellFailCmd
om.nameToEmu["OP_EqDisplayTextCmd"] = OP_EqDisplayTextCmd
om.nameToEmu["OP_EqDrawablePathGraphCmd"] = OP_EqDrawablePathGraphCmd
om.nameToEmu["OP_EqEnableGameEventCmd"] = OP_EqEnableGameEventCmd
om.nameToEmu["OP_EqEnableWindowCmd"] = OP_EqEnableWindowCmd
om.nameToEmu["OP_EqExamineInfoCmd"] = OP_EqExamineInfoCmd
om.nameToEmu["OP_EqFactionUpdateCmd"] = OP_EqFactionUpdateCmd
om.nameToEmu["OP_EqFlashWindowCmd"] = OP_EqFlashWindowCmd
om.nameToEmu["OP_EqGetProbsCmd"] = OP_EqGetProbsCmd
om.nameToEmu["OP_EqGroupMemberRemovedCmd"] = OP_EqGroupMemberRemovedCmd
om.nameToEmu["OP_EqHearChainEffectCmd"] = OP_EqHearChainEffectCmd
om.nameToEmu["OP_EqHearChatCmd"] = OP_EqHearChatCmd
om.nameToEmu["OP_EqHearCombatCmd"] = OP_EqHearCombatCmd
om.nameToEmu["OP_EqHearConsiderCmd"] = OP_EqHearConsiderCmd
om.nameToEmu["OP_EqHearDeathCmd"] = OP_EqHearDeathCmd
om.nameToEmu["OP_EqHearDrowningCmd"] = OP_EqHearDrowningCmd
om.nameToEmu["OP_EqHearHealCmd"] = OP_EqHearHealCmd
om.nameToEmu["OP_EqHearPlayFlavorCmd"] = OP_EqHearPlayFlavorCmd
om.nameToEmu["OP_EqHearSpellCastCmd"] = OP_EqHearSpellCastCmd
om.nameToEmu["OP_EqHearSpellFizzleCmd"] = OP_EqHearSpellFizzleCmd
om.nameToEmu["OP_EqHearSpellInterruptCmd"] = OP_EqHearSpellInterruptCmd
om.nameToEmu["OP_EqHearSpellNoLandCmd"] = OP_EqHearSpellNoLandCmd
om.nameToEmu["OP_EqHelpPathClearCmd"] = OP_EqHelpPathClearCmd
om.nameToEmu["OP_EqHelpPathCmd"] = OP_EqHelpPathCmd
om.nameToEmu["OP_EqInspectPCResultsCmd"] = OP_EqInspectPCResultsCmd
om.nameToEmu["OP_EqInstructionWindowCloseCmd"] = OP_EqInstructionWindowCloseCmd
om.nameToEmu["OP_EqInstructionWindowCmd"] = OP_EqInstructionWindowCmd
om.nameToEmu["OP_EqInstructionWindowGoalCmd"] = OP_EqInstructionWindowGoalCmd
om.nameToEmu["OP_EqInstructionWindowTaskCmd"] = OP_EqInstructionWindowTaskCmd
om.nameToEmu["OP_EqJunctionListCmd"] = OP_EqJunctionListCmd
om.nameToEmu["OP_EqMapExplorationCmd"] = OP_EqMapExplorationCmd
om.nameToEmu["OP_EqPlaySound3DCmd"] = OP_EqPlaySound3DCmd
om.nameToEmu["OP_EqPlaySoundCmd"] = OP_EqPlaySoundCmd
om.nameToEmu["OP_EqPlayVoiceCmd"] = OP_EqPlayVoiceCmd
om.nameToEmu["OP_EqQuestGroupCmd"] = OP_EqQuestGroupCmd
om.nameToEmu["OP_EqQuestJournalReplyCmd"] = OP_EqQuestJournalReplyCmd
om.nameToEmu["OP_EqQuestJournalUpdateCmd"] = OP_EqQuestJournalUpdateCmd
om.nameToEmu["OP_EqQuestionnaireCmd"] = OP_EqQuestionnaireCmd
om.nameToEmu["OP_EqReceiveOfferCmd"] = OP_EqReceiveOfferCmd
om.nameToEmu["OP_EqResurrectedCmd"] = OP_EqResurrectedCmd
om.nameToEmu["OP_EqSetControlGhostCmd"] = OP_EqSetControlGhostCmd
om.nameToEmu["OP_EqSetDebugPathPointsCmd"] = OP_EqSetDebugPathPointsCmd
om.nameToEmu["OP_EqSetDefaultVerbCmd"] = OP_EqSetDefaultVerbCmd
om.nameToEmu["OP_EqSetPOVGhostCmd"] = OP_EqSetPOVGhostCmd
om.nameToEmu["OP_EqShowBookCmd"] = OP_EqShowBookCmd
om.nameToEmu["OP_EqShowDeathWindowCmd"] = OP_EqShowDeathWindowCmd
om.nameToEmu["OP_EqShowWindowCmd"] = OP_EqShowWindowCmd
om.nameToEmu["OP_EqSpellCastEndCmd"] = OP_EqSpellCastEndCmd
om.nameToEmu["OP_EqSpellCastStartCmd"] = OP_EqSpellCastStartCmd
om.nameToEmu["OP_EqSpellMoveToRangeAndRetryCmd"] = OP_EqSpellMoveToRangeAndRetryCmd
om.nameToEmu["OP_EqStartBrokerCmd"] = OP_EqStartBrokerCmd
om.nameToEmu["OP_EqStateCmd"] = OP_EqStateCmd
om.nameToEmu["OP_EqStoreLogCmd"] = OP_EqStoreLogCmd
om.nameToEmu["OP_EqTargetItemCmd"] = OP_EqTargetItemCmd
om.nameToEmu["OP_EqUpdateBankCmd"] = OP_EqUpdateBankCmd
om.nameToEmu["OP_EqUpdateGhostCmd"] = OP_EqUpdateGhostCmd
om.nameToEmu["OP_EqUpdateLootCmd"] = OP_EqUpdateLootCmd
om.nameToEmu["OP_EqUpdateMerchantCmd"] = OP_EqUpdateMerchantCmd
om.nameToEmu["OP_EqUpdatePlayerMailCmd"] = OP_EqUpdatePlayerMailCmd
om.nameToEmu["OP_EqUpdatePlayerTradeCmd"] = OP_EqUpdatePlayerTradeCmd
om.nameToEmu["OP_EqUpdateSignWidgetCmd"] = OP_EqUpdateSignWidgetCmd
om.nameToEmu["OP_EqUpdateStoreCmd"] = OP_EqUpdateStoreCmd
om.nameToEmu["OP_EqUpdateSubClassesCmd"] = OP_EqUpdateSubClassesCmd
om.nameToEmu["OP_EqUpdateTargetCmd"] = OP_EqUpdateTargetCmd
om.nameToEmu["OP_EqWhoChannelQueryReplyCmd"] = OP_EqWhoChannelQueryReplyCmd
om.nameToEmu["OP_EquipItemMsg"] = OP_EquipItemMsg
om.nameToEmu["OP_ExamineConsignmentRequestMsg"] = OP_ExamineConsignmentRequestMsg
om.nameToEmu["OP_ExamineConsignmentResponseMsg"] = OP_ExamineConsignmentResponseMsg
om.nameToEmu["OP_ExamineInfoRequestMsg"] = OP_ExamineInfoRequestMsg
om.nameToEmu["OP_ExamineItemMsg"] = OP_ExamineItemMsg
om.nameToEmu["OP_ExamineItemRequestMsg"] = OP_ExamineItemRequestMsg
om.nameToEmu["OP_ExitHouseMsg"] = OP_ExitHouseMsg
om.nameToEmu["OP_ExitWorldMsg"] = OP_ExitWorldMsg
om.nameToEmu["OP_ExpUpdateMsg"] = OP_ExpUpdateMsg
om.nameToEmu["OP_ExpectClientAsCharacterReplyMs"] = OP_ExpectClientAsCharacterReplyMs
om.nameToEmu["OP_ExpectClientAsCharacterRequest"] = OP_ExpectClientAsCharacterRequest
om.nameToEmu["OP_FellowshipExpMsg"] = OP_FellowshipExpMsg
om.nameToEmu["OP_FlightPathsMsg"] = OP_FlightPathsMsg
om.nameToEmu["OP_GameWorldTimeMsg"] = OP_GameWorldTimeMsg
om.nameToEmu["OP_GetAvatarAccessRequestForCSToo"] = OP_GetAvatarAccessRequestForCSToo
om.nameToEmu["OP_GetCharacterSerializedReplyMsg"] = OP_GetCharacterSerializedReplyMsg
om.nameToEmu["OP_GetCharacterSerializedRequestM"] = OP_GetCharacterSerializedRequestM
om.nameToEmu["OP_GroupCreatedMsg"] = OP_GroupCreatedMsg
om.nameToEmu["OP_GroupDeclineResponseMsg"] = OP_GroupDeclineResponseMsg
om.nameToEmu["OP_GroupDestroyedMsg"] = OP_GroupDestroyedMsg
om.nameToEmu["OP_GroupInviteRequestMsg"] = OP_GroupInviteRequestMsg
om.nameToEmu["OP_GroupInviteResponseMsg"] = OP_GroupInviteResponseMsg
om.nameToEmu["OP_GroupLeaderChangedMsg"] = OP_GroupLeaderChangedMsg
om.nameToEmu["OP_GroupMemberAddedMsg"] = OP_GroupMemberAddedMsg
om.nameToEmu["OP_GroupMemberRemovedMsg"] = OP_GroupMemberRemovedMsg
om.nameToEmu["OP_GroupOptionsMsg"] = OP_GroupOptionsMsg
om.nameToEmu["OP_GroupRemovedFromGroupMsg"] = OP_GroupRemovedFromGroupMsg
om.nameToEmu["OP_GroupSettingsChangedMsg"] = OP_GroupSettingsChangedMsg
om.nameToEmu["OP_GuildBankEventListMsg"] = OP_GuildBankEventListMsg
om.nameToEmu["OP_GuildBankUpdateMsg"] = OP_GuildBankUpdateMsg
om.nameToEmu["OP_GuildEventActionMsg"] = OP_GuildEventActionMsg
om.nameToEmu["OP_GuildEventAddMsg"] = OP_GuildEventAddMsg
om.nameToEmu["OP_GuildEventDetailsMsg"] = OP_GuildEventDetailsMsg
om.nameToEmu["OP_GuildEventListMsg"] = OP_GuildEventListMsg
om.nameToEmu["OP_GuildEventMsg"] = OP_GuildEventMsg
om.nameToEmu["OP_GuildInviteMsg"] = OP_GuildInviteMsg
om.nameToEmu["OP_GuildInviteResponseMsg"] = OP_GuildInviteResponseMsg
om.nameToEmu["OP_GuildKickMsg"] = OP_GuildKickMsg
om.nameToEmu["OP_GuildMembershipResponseMsg"] = OP_GuildMembershipResponseMsg
om.nameToEmu["OP_GuildMotdMsg"] = OP_GuildMotdMsg
om.nameToEmu["OP_GuildRecruiting"] = OP_GuildRecruiting
om.nameToEmu["OP_GuildRecruitingDetails"] = OP_GuildRecruitingDetails
om.nameToEmu["OP_GuildRecruitingImage"] = OP_GuildRecruitingImage
om.nameToEmu["OP_GuildRecruitingMemberInfo"] = OP_GuildRecruitingMemberInfo
om.nameToEmu["OP_GuildUpdateMsg"] = OP_GuildUpdateMsg
om.nameToEmu["OP_GuildsayMsg"] = OP_GuildsayMsg
om.nameToEmu["OP_HeritageMsg"] = OP_HeritageMsg
om.nameToEmu["OP_HouseAccessRemoveMsg"] = OP_HouseAccessRemoveMsg
om.nameToEmu["OP_HouseAccessSetMsg"] = OP_HouseAccessSetMsg
om.nameToEmu["OP_HouseCustomizationScreenMsg"] = OP_HouseCustomizationScreenMsg
om.nameToEmu["OP_HouseDefaultAccessSetMsg"] = OP_HouseDefaultAccessSetMsg
om.nameToEmu["OP_HouseDeletedRemotelyMsg"] = OP_HouseDeletedRemotelyMsg
om.nameToEmu["OP_HouseItemsList"] = OP_HouseItemsList
om.nameToEmu["OP_HousePayUpkeep"] = OP_HousePayUpkeep
om.nameToEmu["OP_HousePurchase"] = OP_HousePurchase
om.nameToEmu["OP_HousingDataChangedMsg"] = OP_HousingDataChangedMsg
om.nameToEmu["OP_HousingDataMsg"] = OP_HousingDataMsg
om.nameToEmu["OP_HousingRestoreMsg"] = OP_HousingRestoreMsg
om.nameToEmu["OP_InspectPlayerMsg"] = OP_InspectPlayerMsg
om.nameToEmu["OP_InterruptSpellMsg"] = OP_InterruptSpellMsg
om.nameToEmu["OP_InventoryMsg"] = OP_InventoryMsg
om.nameToEmu["OP_JoinGuildNotifyMsg"] = OP_JoinGuildNotifyMsg
om.nameToEmu["OP_JournalQuestStoryline"] = OP_JournalQuestStoryline
om.nameToEmu["OP_KeybindLoadMsg"] = OP_KeybindLoadMsg
om.nameToEmu["OP_KeybindUpdateMsg"] = OP_KeybindUpdateMsg
om.nameToEmu["OP_KeymapDataMsg"] = OP_KeymapDataMsg
om.nameToEmu["OP_KeymapLoadMsg"] = OP_KeymapLoadMsg
om.nameToEmu["OP_KeymapNoneMsg"] = OP_KeymapNoneMsg
om.nameToEmu["OP_KeymapSaveMsg"] = OP_KeymapSaveMsg
om.nameToEmu["OP_KnowledgeWindowSlotMappingMsg"] = OP_KnowledgeWindowSlotMappingMsg
om.nameToEmu["OP_KnowledgebaseRequestMsg"] = OP_KnowledgebaseRequestMsg
om.nameToEmu["OP_KnowledgebaseResponseMsg"] = OP_KnowledgebaseResponseMsg
om.nameToEmu["OP_KnownLanguagesMsg"] = OP_KnownLanguagesMsg
om.nameToEmu["OP_LFGGroupSearch"] = OP_LFGGroupSearch
om.nameToEmu["OP_LFGUpdateMsg"] = OP_LFGUpdateMsg
om.nameToEmu["OP_LSCheckAcctLockMsg"] = OP_LSCheckAcctLockMsg
om.nameToEmu["OP_LSServerLockMsg"] = OP_LSServerLockMsg
om.nameToEmu["OP_Launchpad"] = OP_Launchpad
om.nameToEmu["OP_LeaveGuildNotifyMsg"] = OP_LeaveGuildNotifyMsg
om.nameToEmu["OP_LevelChangedMsg"] = OP_LevelChangedMsg
om.nameToEmu["OP_LevelUpMsg"] = OP_LevelUpMsg
om.nameToEmu["OP_LikeOption"] = OP_LikeOption
om.nameToEmu["OP_LoadCalendarEvents"] = OP_LoadCalendarEvents
om.nameToEmu["OP_LoadWelcomeWindow"] = OP_LoadWelcomeWindow
om.nameToEmu["OP_LoginAcceptedMsg"] = OP_LoginAcceptedMsg
om.nameToEmu["OP_LoginByNumRequestMsg"] = OP_LoginByNumRequestMsg
om.nameToEmu["OP_LoginFailedMsg2"] = OP_LoginFailedMsg2
om.nameToEmu["OP_LoginReplyMsg"] = OP_LoginReplyMsg
om.nameToEmu["OP_LootItemsRequestMsg"] = OP_LootItemsRequestMsg
om.nameToEmu["OP_Lottery"] = OP_Lottery
om.nameToEmu["OP_LsClientAlertlogReplyMsg"] = OP_LsClientAlertlogReplyMsg
om.nameToEmu["OP_LsClientBaselogReplyMsg"] = OP_LsClientBaselogReplyMsg
om.nameToEmu["OP_LsClientCrashlogReplyMsg"] = OP_LsClientCrashlogReplyMsg
om.nameToEmu["OP_LsClientVerifylogReplyMsg"] = OP_LsClientVerifylogReplyMsg
om.nameToEmu["OP_LsRequestClientCrashLogMsg"] = OP_LsRequestClientCrashLogMsg
om.nameToEmu["OP_MOTDMsg"] = OP_MOTDMsg
om.nameToEmu["OP_MacroInitMsg"] = OP_MacroInitMsg
om.nameToEmu["OP_MacroUpdateMsg"] = OP_MacroUpdateMsg
om.nameToEmu["OP_MailCommitSendMessageMsg"] = OP_MailCommitSendMessageMsg
om.nameToEmu["OP_MailDeleteMessageMsg"] = OP_MailDeleteMessageMsg
om.nameToEmu["OP_MailDeleteMessageRequestMsg"] = OP_MailDeleteMessageRequestMsg
om.nameToEmu["OP_MailEventNotificationMsg"] = OP_MailEventNotificationMsg
om.nameToEmu["OP_MailGetHeadersReplyMsg"] = OP_MailGetHeadersReplyMsg
om.nameToEmu["OP_MailGetHeadersRequestMsg"] = OP_MailGetHeadersRequestMsg
om.nameToEmu["OP_MailGetMessageMsg"] = OP_MailGetMessageMsg
om.nameToEmu["OP_MailGetMessageReplyMsg"] = OP_MailGetMessageReplyMsg
om.nameToEmu["OP_MailGetMessageRequestMsg"] = OP_MailGetMessageRequestMsg
om.nameToEmu["OP_MailRemoveAttachFromMailMsg"] = OP_MailRemoveAttachFromMailMsg
om.nameToEmu["OP_MailSendMessageMsg"] = OP_MailSendMessageMsg
om.nameToEmu["OP_MailSendMessageReplyMsg"] = OP_MailSendMessageReplyMsg
om.nameToEmu["OP_MailSendMessageRequestMsg"] = OP_MailSendMessageRequestMsg
om.nameToEmu["OP_MailSendSystemMessageMsg"] = OP_MailSendSystemMessageMsg
om.nameToEmu["OP_MakeGroupLeaderMsg"] = OP_MakeGroupLeaderMsg
om.nameToEmu["OP_MapFogDataInitMsg"] = OP_MapFogDataInitMsg
om.nameToEmu["OP_MapFogDataUpdateMsg"] = OP_MapFogDataUpdateMsg
om.nameToEmu["OP_MapRequest"] = OP_MapRequest
om.nameToEmu["OP_MarketAddFundsRequest"] = OP_MarketAddFundsRequest
om.nameToEmu["OP_MarketFundsUpdate"] = OP_MarketFundsUpdate
om.nameToEmu["OP_MarketPlacePrices"] = OP_MarketPlacePrices
om.nameToEmu["OP_MigrateBoatTransportMsg"] = OP_MigrateBoatTransportMsg
om.nameToEmu["OP_MigrateBoatTransportReplyMsg"] = OP_MigrateBoatTransportReplyMsg
om.nameToEmu["OP_MigrateClientToZoneReplyMsg"] = OP_MigrateClientToZoneReplyMsg
om.nameToEmu["OP_MigrateClientToZoneRequestMsg"] = OP_MigrateClientToZoneRequestMsg
om.nameToEmu["OP_ModifyGuildMsg"] = OP_ModifyGuildMsg
om.nameToEmu["OP_MonitorCharacterListMsg"] = OP_MonitorCharacterListMsg
om.nameToEmu["OP_MonitorCharacterListRequestMsg"] = OP_MonitorCharacterListRequestMsg
om.nameToEmu["OP_MonitorReplyMsg"] = OP_MonitorReplyMsg
om.nameToEmu["OP_MoveLogUpdateMsg"] = OP_MoveLogUpdateMsg
om.nameToEmu["OP_MoveableObjectPlacementCriteri"] = OP_MoveableObjectPlacementCriteri
om.nameToEmu["OP_NewZoneMsg"] = OP_NewZoneMsg
om.nameToEmu["OP_NotifyApprenticeStoppedMentori"] = OP_NotifyApprenticeStoppedMentori
om.nameToEmu["OP_OfferQuestMsg"] = OP_OfferQuestMsg
om.nameToEmu["OP_OnscreenMsgMsg"] = OP_OnscreenMsgMsg
om.nameToEmu["OP_OpenCharCust"] = OP_OpenCharCust
om.nameToEmu["OP_PaperdollImage"] = OP_PaperdollImage
om.nameToEmu["OP_PayHouseUpkeepMsg"] = OP_PayHouseUpkeepMsg
om.nameToEmu["OP_PerformCameraShakeMsg"] = OP_PerformCameraShakeMsg
om.nameToEmu["OP_PerformPlayerKnockbackMsg"] = OP_PerformPlayerKnockbackMsg
om.nameToEmu["OP_PetOptions"] = OP_PetOptions
om.nameToEmu["OP_PetOptionsResponse"] = OP_PetOptionsResponse
om.nameToEmu["OP_PlayerBioMsg"] = OP_PlayerBioMsg
om.nameToEmu["OP_PlayerHouseAccessUpdateMsg"] = OP_PlayerHouseAccessUpdateMsg
om.nameToEmu["OP_PlayerHouseBaseScreenMsg"] = OP_PlayerHouseBaseScreenMsg
om.nameToEmu["OP_PlayerHouseCloseUIMsg"] = OP_PlayerHouseCloseUIMsg
om.nameToEmu["OP_PlayerHouseDisplayStatusMsg"] = OP_PlayerHouseDisplayStatusMsg
om.nameToEmu["OP_PlayerHousePurchaseScreenMsg"] = OP_PlayerHousePurchaseScreenMsg
om.nameToEmu["OP_PlayerProfileMsg"] = OP_PlayerProfileMsg
om.nameToEmu["OP_PlayerStatsMsg"] = OP_PlayerStatsMsg
om.nameToEmu["OP_PointOfInterest"] = OP_PointOfInterest
om.nameToEmu["OP_PopulateSkillMapsMsg"] = OP_PopulateSkillMapsMsg
om.nameToEmu["OP_PositionBoatTransportMsg"] = OP_PositionBoatTransportMsg
om.nameToEmu["OP_PositionMoveableObject"] = OP_PositionMoveableObject
om.nameToEmu["OP_PredictionUpdateMsg"] = OP_PredictionUpdateMsg
om.nameToEmu["OP_PromoFlagsDetailsMsg"] = OP_PromoFlagsDetailsMsg
om.nameToEmu["OP_PurchaseConsignmentLoreCheckRe"] = OP_PurchaseConsignmentLoreCheckRe
om.nameToEmu["OP_QueryHoodMsg"] = OP_QueryHoodMsg
om.nameToEmu["OP_QueryNpcMsg"] = OP_QueryNpcMsg
om.nameToEmu["OP_QuestCompleteMsg"] = OP_QuestCompleteMsg
om.nameToEmu["OP_QuestFailedMsg"] = OP_QuestFailedMsg
om.nameToEmu["OP_QuestJournalInspectMsg"] = OP_QuestJournalInspectMsg
om.nameToEmu["OP_QuestJournalOpenMsg"] = OP_QuestJournalOpenMsg
om.nameToEmu["OP_QuestJournalReplyMsg"] = OP_QuestJournalReplyMsg
om.nameToEmu["OP_QuestJournalSetVisibleMsg"] = OP_QuestJournalSetVisibleMsg
om.nameToEmu["OP_QuestJournalUpdateMsg"] = OP_QuestJournalUpdateMsg
om.nameToEmu["OP_QuestJournalWaypointMsg"] = OP_QuestJournalWaypointMsg
om.nameToEmu["OP_QuestOfferMsg"] = OP_QuestOfferMsg
om.nameToEmu["OP_QuestReward"] = OP_QuestReward
om.nameToEmu["OP_QuestRewardMsg"] = OP_QuestRewardMsg
om.nameToEmu["OP_QuestionnaireMsg"] = OP_QuestionnaireMsg
om.nameToEmu["OP_QuickbarInitMsg"] = OP_QuickbarInitMsg
om.nameToEmu["OP_QuickbarUpdateMsg"] = OP_QuickbarUpdateMsg
om.nameToEmu["OP_RaceRestrictionMsg"] = OP_RaceRestrictionMsg
om.nameToEmu["OP_ReadyForTakeOffMsg"] = OP_ReadyForTakeOffMsg
om.nameToEmu["OP_ReadyToZoneMsg"] = OP_ReadyToZoneMsg
om.nameToEmu["OP_RecipeBook"] = OP_RecipeBook
om.nameToEmu["OP_RecipeDetailsMsg"] = OP_RecipeDetailsMsg
om.nameToEmu["OP_RecipeList"] = OP_RecipeList
om.nameToEmu["OP_RecipeListUnknown"] = OP_RecipeListUnknown
om.nameToEmu["OP_RelinquishHouseMsg"] = OP_RelinquishHouseMsg
om.nameToEmu["OP_ReloadLocalizedTxtMsg"] = OP_ReloadLocalizedTxtMsg
om.nameToEmu["OP_RemoteCmdMsg"] = OP_RemoteCmdMsg
om.nameToEmu["OP_RemoveClientFromGroupMsg"] = OP_RemoveClientFromGroupMsg
om.nameToEmu["OP_RemoveConcentrationMsg"] = OP_RemoveConcentrationMsg
om.nameToEmu["OP_RemoveGroupFromGroupMsg"] = OP_RemoveGroupFromGroupMsg
om.nameToEmu["OP_RemoveSpellEffectMsg"] = OP_RemoveSpellEffectMsg
om.nameToEmu["OP_RenameGuildMsg"] = OP_RenameGuildMsg
om.nameToEmu["OP_ReplaceableSubMeshesMsg"] = OP_ReplaceableSubMeshesMsg
om.nameToEmu["OP_ReportMsg"] = OP_ReportMsg
om.nameToEmu["OP_RequestCampMsg"] = OP_RequestCampMsg
om.nameToEmu["OP_RequestGuildBankEventDetailsMs"] = OP_RequestGuildBankEventDetailsMs
om.nameToEmu["OP_RequestGuildEventDetailsMsg"] = OP_RequestGuildEventDetailsMsg
om.nameToEmu["OP_RequestGuildInfoMsg"] = OP_RequestGuildInfoMsg
om.nameToEmu["OP_RequestGuildMembershipMsg"] = OP_RequestGuildMembershipMsg
om.nameToEmu["OP_RequestHelpRepathMsg"] = OP_RequestHelpRepathMsg
om.nameToEmu["OP_RequestRecipeDetailsMsg"] = OP_RequestRecipeDetailsMsg
om.nameToEmu["OP_RequestTargetLocMsg"] = OP_RequestTargetLocMsg
om.nameToEmu["OP_RestartZoneMsg"] = OP_RestartZoneMsg
om.nameToEmu["OP_RewardPackMsg"] = OP_RewardPackMsg
om.nameToEmu["OP_SatMsg"] = OP_SatMsg
om.nameToEmu["OP_SavageBarInitMsg"] = OP_SavageBarInitMsg
om.nameToEmu["OP_SelectZoneTeleporterDestinatio"] = OP_SelectZoneTeleporterDestinatio
om.nameToEmu["OP_SendLatestRequestMsg"] = OP_SendLatestRequestMsg
om.nameToEmu["OP_SendZonePointsMsg"] = OP_SendZonePointsMsg
om.nameToEmu["OP_SetInstanceDisplayNameMsg"] = OP_SetInstanceDisplayNameMsg
om.nameToEmu["OP_SetRemoteCmdsMsg"] = OP_SetRemoteCmdsMsg
om.nameToEmu["OP_SetSocialMsg"] = OP_SetSocialMsg
om.nameToEmu["OP_SetTimeMsg"] = OP_SetTimeMsg
om.nameToEmu["OP_ShaderCustomizationMsg"] = OP_ShaderCustomizationMsg
om.nameToEmu["OP_ShowCreateFromRecipeUIMsg"] = OP_ShowCreateFromRecipeUIMsg
om.nameToEmu["OP_ShowItemCreationProcessUIMsg"] = OP_ShowItemCreationProcessUIMsg
om.nameToEmu["OP_ShowRecipeBookMsg"] = OP_ShowRecipeBookMsg
om.nameToEmu["OP_ShowZoneTeleporterDestinations"] = OP_ShowZoneTeleporterDestinations
om.nameToEmu["OP_SignalMsg"] = OP_SignalMsg
om.nameToEmu["OP_SitMsg"] = OP_SitMsg
om.nameToEmu["OP_SkillInfoRequest"] = OP_SkillInfoRequest
om.nameToEmu["OP_SkillInfoRequestMsg"] = OP_SkillInfoRequestMsg
om.nameToEmu["OP_SkillInfoResponse"] = OP_SkillInfoResponse
om.nameToEmu["OP_SkillInfoResponseMsg"] = OP_SkillInfoResponseMsg
om.nameToEmu["OP_SkillUpMsg"] = OP_SkillUpMsg
om.nameToEmu["OP_SpellGainedMsg"] = OP_SpellGainedMsg
om.nameToEmu["OP_StandMsg"] = OP_StandMsg
om.nameToEmu["OP_StoodMsg"] = OP_StoodMsg
om.nameToEmu["OP_StopItemCreationMsg"] = OP_StopItemCreationMsg
om.nameToEmu["OP_StopTrackingMsg"] = OP_StopTrackingMsg
om.nameToEmu["OP_StoppedLootingMsg"] = OP_StoppedLootingMsg
om.nameToEmu["OP_SubmitCharCust"] = OP_SubmitCharCust
om.nameToEmu["OP_SysClient"] = OP_SysClient
om.nameToEmu["OP_TargetMsg"] = OP_TargetMsg
om.nameToEmu["OP_TeleportWithinZoneMsg"] = OP_TeleportWithinZoneMsg
om.nameToEmu["OP_TeleportWithinZoneNoReloadMsg"] = OP_TeleportWithinZoneNoReloadMsg
om.nameToEmu["OP_TellMsg"] = OP_TellMsg
om.nameToEmu["OP_TintWidgetsMsg"] = OP_TintWidgetsMsg
om.nameToEmu["OP_TitleUpdateMsg"] = OP_TitleUpdateMsg
om.nameToEmu["OP_TrackingUpdateMsg"] = OP_TrackingUpdateMsg
om.nameToEmu["OP_TradeAcceptMsg"] = OP_TradeAcceptMsg
om.nameToEmu["OP_TradeAddItemMsg"] = OP_TradeAddItemMsg
om.nameToEmu["OP_TradeEndMsg"] = OP_TradeEndMsg
om.nameToEmu["OP_TradeRemoveItemMsg"] = OP_TradeRemoveItemMsg
om.nameToEmu["OP_TradeRequestMsg"] = OP_TradeRequestMsg
om.nameToEmu["OP_TradeResponseMsg"] = OP_TradeResponseMsg
om.nameToEmu["OP_TradeStartMsg"] = OP_TradeStartMsg
om.nameToEmu["OP_TradeskillList"] = OP_TradeskillList
om.nameToEmu["OP_TraitsList"] = OP_TraitsList
om.nameToEmu["OP_UIEvent"] = OP_UIEvent
om.nameToEmu["OP_UIResetMsg"] = OP_UIResetMsg
om.nameToEmu["OP_UISettingsMsg"] = OP_UISettingsMsg
om.nameToEmu["OP_UISettingsResponseMsg"] = OP_UISettingsResponseMsg
om.nameToEmu["OP_UnequipItemMsg"] = OP_UnequipItemMsg
om.nameToEmu["OP_Unknown"] = OP_Unknown
om.nameToEmu["OP_UnknownNpcMsg"] = OP_UnknownNpcMsg
om.nameToEmu["OP_UpdateActivePublicZonesMsg"] = OP_UpdateActivePublicZonesMsg
om.nameToEmu["OP_UpdateCharacterSheetMsg"] = OP_UpdateCharacterSheetMsg
om.nameToEmu["OP_UpdateClientPredFlagsMsg"] = OP_UpdateClientPredFlagsMsg
om.nameToEmu["OP_UpdateDebugRadiiMsg"] = OP_UpdateDebugRadiiMsg
om.nameToEmu["OP_UpdateGroupMemberDataMsg"] = OP_UpdateGroupMemberDataMsg
om.nameToEmu["OP_UpdateHouseAccessDataMsg"] = OP_UpdateHouseAccessDataMsg
om.nameToEmu["OP_UpdateHouseDataMsg"] = OP_UpdateHouseDataMsg
om.nameToEmu["OP_UpdateInventoryMsg"] = OP_UpdateInventoryMsg
om.nameToEmu["OP_UpdateItemCreationProcessUIMsg"] = OP_UpdateItemCreationProcessUIMsg
om.nameToEmu["OP_UpdateMotdMsg"] = OP_UpdateMotdMsg
om.nameToEmu["OP_UpdateOpportunityMsg"] = OP_UpdateOpportunityMsg
om.nameToEmu["OP_UpdatePositionMsg"] = OP_UpdatePositionMsg
om.nameToEmu["OP_UpdateRaidMsg"] = OP_UpdateRaidMsg
om.nameToEmu["OP_UpdateRecipeBookMsg"] = OP_UpdateRecipeBookMsg
om.nameToEmu["OP_UpdateSkillBookMsg"] = OP_UpdateSkillBookMsg
om.nameToEmu["OP_UpdateSkillsMsg"] = OP_UpdateSkillsMsg
om.nameToEmu["OP_UpdateSpellBookMsg"] = OP_UpdateSpellBookMsg
om.nameToEmu["OP_UpdateTargetLocMsg"] = OP_UpdateTargetLocMsg
om.nameToEmu["OP_UpdateTargetMsg"] = OP_UpdateTargetMsg
om.nameToEmu["OP_UpdateTitleCmd"] = OP_UpdateTitleCmd
om.nameToEmu["OP_UseItemMsg"] = OP_UseItemMsg
om.nameToEmu["OP_WSAcctLockStatusMsg"] = OP_WSAcctLockStatusMsg
om.nameToEmu["OP_WSLoginRequestMsg"] = OP_WSLoginRequestMsg
om.nameToEmu["OP_WSServerHideMsg"] = OP_WSServerHideMsg
om.nameToEmu["OP_WSServerLockMsg"] = OP_WSServerLockMsg
om.nameToEmu["OP_WaypointReplyMsg"] = OP_WaypointReplyMsg
om.nameToEmu["OP_WaypointRequestMsg"] = OP_WaypointRequestMsg
om.nameToEmu["OP_WaypointSelectMsg"] = OP_WaypointSelectMsg
om.nameToEmu["OP_WaypointUpdateMsg"] = OP_WaypointUpdateMsg
om.nameToEmu["OP_Weakness"] = OP_Weakness
om.nameToEmu["OP_WhoQueryReplyMsg"] = OP_WhoQueryReplyMsg
om.nameToEmu["OP_WhoQueryRequestMsg"] = OP_WhoQueryRequestMsg
om.nameToEmu["OP_WorldDataChangeMsg"] = OP_WorldDataChangeMsg
om.nameToEmu["OP_WorldDataUpdateMsg"] = OP_WorldDataUpdateMsg
om.nameToEmu["OP_WorldPingMsg"] = OP_WorldPingMsg
om.nameToEmu["OP_WorldShutdownUpdateMsg"] = OP_WorldShutdownUpdateMsg
om.nameToEmu["OP_ZoneBgInstanceList"] = OP_ZoneBgInstanceList
om.nameToEmu["OP_ZoneChangeMsg"] = OP_ZoneChangeMsg
om.nameToEmu["OP_ZoneEntryMsg"] = OP_ZoneEntryMsg
om.nameToEmu["OP_ZoneExitMsg"] = OP_ZoneExitMsg
om.nameToEmu["OP_ZoneInfoMsg"] = OP_ZoneInfoMsg
om.nameToEmu["OP_ZoneInstanceCreateReplyMsg"] = OP_ZoneInstanceCreateReplyMsg
om.nameToEmu["OP_ZoneInstanceDestroyedMsg"] = OP_ZoneInstanceDestroyedMsg
om.nameToEmu["OP_ZoneListMsg"] = OP_ZoneListMsg
om.nameToEmu["OP_ZoneMOTDMsg"] = OP_ZoneMOTDMsg
om.nameToEmu["OP_ZonePointsMsg"] = OP_ZonePointsMsg
om.nameToEmu["OP_ZoneToFriendReplyMsg"] = OP_ZoneToFriendReplyMsg
om.nameToEmu["OP_ZoneToFriendRequestMsg"] = OP_ZoneToFriendRequestMsg
om.nameToEmu["OP_ZoneUnavailMsg"] = OP_ZoneUnavailMsg
om.nameToEmu["OP_ZonesStatusMsg"] = OP_ZonesStatusMsg
om.nameToEmu["OP_ZonesStatusRequestMsg"] = OP_ZonesStatusRequestMsg
}

70
opcodes/login.go Normal file
View File

@ -0,0 +1,70 @@
package opcodes
// Login server opcodes
const (
LS_LoginRequestMsg EmuOpcode = 0x0000
LS_LoginReplyMsg EmuOpcode = 0x0001
LS_WorldListMsg EmuOpcode = 0x0002
LS_WorldStatusMsg EmuOpcode = 0x0003
LS_AccessGrantedMsg EmuOpcode = 0x0004
LS_SessionReadyMsg EmuOpcode = 0x0005
LS_LoginFailedMsg EmuOpcode = 0x0006
LS_LoginBannedMsg EmuOpcode = 0x0007
LS_PlayCharRequestMsg EmuOpcode = 0x0008
LS_PlayCharReplyMsg EmuOpcode = 0x0009
LS_CreateCharRequestMsg EmuOpcode = 0x000a
LS_CreateCharReplyMsg EmuOpcode = 0x000b
LS_DeleteCharRequestMsg EmuOpcode = 0x000c
LS_DeleteCharReplyMsg EmuOpcode = 0x000d
LS_CharSelectMsg EmuOpcode = 0x000e
LS_CharListRequestMsg EmuOpcode = 0x000f
LS_CharListReplyMsg EmuOpcode = 0x0010
LS_ServerListRequestMsg EmuOpcode = 0x0011
LS_ServerListReplyMsg EmuOpcode = 0x0012
LS_ServerStatusRequestMsg EmuOpcode = 0x0013
LS_ServerStatusReplyMsg EmuOpcode = 0x0014
LS_LSStatusRequestMsg EmuOpcode = 0x0015
LS_LSStatusReplyMsg EmuOpcode = 0x0016
LS_UsertoWorldReq EmuOpcode = 0x0017
LS_UsertoWorldResp EmuOpcode = 0x0018
LS_AllWorldsDescRequestMsg EmuOpcode = 0x0019
LS_AllWorldsDescReplyMsg EmuOpcode = 0x001a
LS_CreateAccountRequestMsg EmuOpcode = 0x001b
LS_CreateAccountReplyMsg EmuOpcode = 0x001c
LS_LoginByNumRequestMsg EmuOpcode = 0x001d
LS_WSLoginRequestMsg EmuOpcode = 0x001e
LS_ESLoginRequestMsg EmuOpcode = 0x001f
LS_WorldStatusChangeMsg EmuOpcode = 0x0020
LS_AllWSDescRequestMsg EmuOpcode = 0x0021
LS_WSStatusReplyMsg EmuOpcode = 0x0022
LS_AllCharactersDescRequestMsg EmuOpcode = 0x0023
LS_AllCharactersDescReplyMsg EmuOpcode = 0x0024
LS_CreateCharacterRequestMsg EmuOpcode = 0x0025
LS_CreateCharacterReplyMsg EmuOpcode = 0x0026
LS_ReskinCharacterRequestMsg EmuOpcode = 0x0027
LS_WSCreateCharacterRequestMsg EmuOpcode = 0x0028
LS_WSCreateCharacterReplyMsg EmuOpcode = 0x0029
LS_DeleteCharacterRequestMsg EmuOpcode = 0x002a
LS_DeleteCharacterReplyMsg EmuOpcode = 0x002b
LS_PlayCharacterRequestMsg EmuOpcode = 0x002c
LS_PlayCharacterReplyMsg EmuOpcode = 0x002d
LS_ServerPlayCharacterRequestMsg EmuOpcode = 0x002e
LS_ServerPlayCharacterReplyMsg EmuOpcode = 0x002f
LS_KeymapLoadMsg EmuOpcode = 0x0030
LS_KeymapNoneMsg EmuOpcode = 0x0031
LS_KeymapDataMsg EmuOpcode = 0x0032
LS_KeymapSaveMsg EmuOpcode = 0x0033
LS_LSCheckAcctLockMsg EmuOpcode = 0x0034
LS_WSAcctLockStatusMsg EmuOpcode = 0x0035
LS_LsRequestClientCrashLogMsg EmuOpcode = 0x0036
LS_LsClientBaselogReplyMsg EmuOpcode = 0x0037
LS_LsClientCrashlogReplyMsg EmuOpcode = 0x0038
LS_LsClientAlertlogReplyMsg EmuOpcode = 0x0039
LS_LsClientVerifylogReplyMsg EmuOpcode = 0x003a
LS_BadLanguageFilter EmuOpcode = 0x003b
LS_WSServerLockMsg EmuOpcode = 0x003c
LS_WSServerHideMsg EmuOpcode = 0x003d
LS_LSServerLockMsg EmuOpcode = 0x003e
LS_UpdateCharacterSheetMsg EmuOpcode = 0x003f
LS_UpdateInventoryMsg EmuOpcode = 0x0040
)

257
opcodes/manager.go Normal file
View File

@ -0,0 +1,257 @@
package opcodes
//go:generate go run gen/main.go
import (
"database/sql"
"fmt"
"sort"
"strings"
"sync"
)
// OpcodeManager manages opcode translations for a specific client version
type OpcodeManager struct {
version int16
emuToEQ map[EmuOpcode]uint16
eqToEmu map[uint16]EmuOpcode
emuToName map[EmuOpcode]string
eqToName map[uint16]string
nameToEmu map[string]EmuOpcode
mu sync.RWMutex
}
// NewOpcodeManager creates a new opcode manager for the specified version
func NewOpcodeManager(version int16) *OpcodeManager {
om := &OpcodeManager{
version: version,
emuToEQ: make(map[EmuOpcode]uint16),
eqToEmu: make(map[uint16]EmuOpcode),
emuToName: make(map[EmuOpcode]string),
eqToName: make(map[uint16]string),
nameToEmu: make(map[string]EmuOpcode),
}
om.initializeNameMappings() // Generated by go:generate
return om
}
// LoadFromDatabase loads opcode mappings from database
func (om *OpcodeManager) LoadFromDatabase(db *sql.DB, tableName string) error {
query := `
SELECT name, opcode
FROM ` + tableName + `
WHERE ? >= version_range1
AND (version_range2 = 0 OR ? <= version_range2)
ORDER BY version_range1 DESC, version_range2 DESC
`
rows, err := db.Query(query, om.version, om.version)
if err != nil {
return err
}
defer rows.Close()
om.mu.Lock()
defer om.mu.Unlock()
loaded := make(map[string]bool)
for rows.Next() {
var name string
var opcode int16
if err := rows.Scan(&name, &opcode); err != nil {
continue
}
if loaded[name] {
continue
}
loaded[name] = true
eqOpcode := uint16(opcode)
// Only map if we have a constant for this opcode name
if emuOp, ok := om.nameToEmu[name]; ok {
om.emuToEQ[emuOp] = eqOpcode
om.eqToEmu[eqOpcode] = emuOp
om.emuToName[emuOp] = name
om.eqToName[eqOpcode] = name
}
}
// Map missing opcodes to 0xFFFF
for name, emuOp := range om.nameToEmu {
if _, ok := om.emuToEQ[emuOp]; !ok {
om.emuToEQ[emuOp] = 0xFFFF
om.emuToName[emuOp] = name
}
}
return rows.Err()
}
// EmuToEQ converts emulator opcode to EQ opcode
func (om *OpcodeManager) EmuToEQ(emu EmuOpcode) uint16 {
om.mu.RLock()
defer om.mu.RUnlock()
if eq, ok := om.emuToEQ[emu]; ok {
return eq
}
return 0xFFFF
}
// EQToEmu converts EQ opcode to emulator opcode
func (om *OpcodeManager) EQToEmu(eq uint16) EmuOpcode {
om.mu.RLock()
defer om.mu.RUnlock()
if emu, ok := om.eqToEmu[eq]; ok {
return emu
}
return OP_Unknown
}
// EmuToName returns the name of an emulator opcode
func (om *OpcodeManager) EmuToName(emu EmuOpcode) string {
om.mu.RLock()
defer om.mu.RUnlock()
if name, ok := om.emuToName[emu]; ok {
return name
}
return "OP_Unknown"
}
// EQToName returns the name of an EQ opcode
func (om *OpcodeManager) EQToName(eq uint16) string {
om.mu.RLock()
defer om.mu.RUnlock()
if name, ok := om.eqToName[eq]; ok {
return name
}
return "OP_Unknown"
}
// GetMissingOpcodes returns list of opcodes that map to 0xFFFF
func (om *OpcodeManager) GetMissingOpcodes() []string {
om.mu.RLock()
defer om.mu.RUnlock()
var missing []string
for emu, eq := range om.emuToEQ {
if eq == 0xFFFF {
if name, ok := om.emuToName[emu]; ok {
missing = append(missing, name)
}
}
}
return missing
}
// IsValidOpcode checks if an EQ opcode is valid/known
func (om *OpcodeManager) IsValidOpcode(eq uint16) bool {
om.mu.RLock()
defer om.mu.RUnlock()
_, exists := om.eqToEmu[eq]
return exists
}
// DumpOpcodes returns a formatted string of all loaded opcodes for debugging
func (om *OpcodeManager) DumpOpcodes() string {
om.mu.RLock()
defer om.mu.RUnlock()
var sb strings.Builder
sb.WriteString(fmt.Sprintf("Opcode Manager for version %d:\n", om.version))
sb.WriteString("=====================================\n")
var eqOpcodes []uint16
for eq := range om.eqToEmu {
eqOpcodes = append(eqOpcodes, eq)
}
sort.Slice(eqOpcodes, func(i, j int) bool {
return eqOpcodes[i] < eqOpcodes[j]
})
for _, eq := range eqOpcodes {
emu := om.eqToEmu[eq]
name := om.eqToName[eq]
sb.WriteString(fmt.Sprintf("0x%04x -> 0x%04x : %s\n", eq, emu, name))
}
return sb.String()
}
// GetStats returns statistics about loaded opcodes
func (om *OpcodeManager) GetStats() map[string]int {
om.mu.RLock()
defer om.mu.RUnlock()
return map[string]int{
"version": int(om.version),
"total_opcodes": len(om.eqToEmu),
"named_opcodes": len(om.emuToName),
"mapped_opcodes": len(om.emuToEQ),
}
}
// Global managers cache
var (
managers = make(map[int16]*OpcodeManager)
managersMu sync.RWMutex
)
// GetManager returns opcode manager for a version
func GetManager(version int16, db *sql.DB, tableName string) *OpcodeManager {
managersMu.RLock()
if mgr, ok := managers[version]; ok {
managersMu.RUnlock()
return mgr
}
managersMu.RUnlock()
managersMu.Lock()
defer managersMu.Unlock()
if mgr, ok := managers[version]; ok {
return mgr
}
mgr := NewOpcodeManager(version)
if db != nil {
if err := mgr.LoadFromDatabase(db, tableName); err != nil {
fmt.Printf("Warning: Failed to load opcodes for version %d: %v\n", version, err)
}
}
managers[version] = mgr
return mgr
}
// ClearCache clears the cached managers (useful for testing)
func ClearCache() {
managersMu.Lock()
defer managersMu.Unlock()
managers = make(map[int16]*OpcodeManager)
}
// GetLoadedVersions returns all versions that have managers loaded
func GetLoadedVersions() []int16 {
managersMu.RLock()
defer managersMu.RUnlock()
var versions []int16
for v := range managers {
versions = append(versions, v)
}
sort.Slice(versions, func(i, j int) bool {
return versions[i] < versions[j]
})
return versions
}

18
opcodes/protocol.go Normal file
View File

@ -0,0 +1,18 @@
package opcodes
// Protocol-level opcodes used by the network layer
const (
OP_SessionRequest uint16 = 0x0001
OP_SessionResponse uint16 = 0x0002
OP_Combined uint16 = 0x0003
OP_SessionDisconnect uint16 = 0x0005
OP_KeepAlive uint16 = 0x0006
OP_SessionStatRequest uint16 = 0x0007
OP_SessionStatResponse uint16 = 0x0008
OP_Packet uint16 = 0x0009
OP_Fragment uint16 = 0x000d
OP_Ack uint16 = 0x0015
OP_AppCombined uint16 = 0x0019
OP_OutOfOrderAck uint16 = 0x001d
OP_OutOfSession uint16 = 0x001e
)

12
opcodes/types.go Normal file
View File

@ -0,0 +1,12 @@
package opcodes
// EmuOpcode represents an emulator opcode
type EmuOpcode uint16
// Manager handles opcode translation between emulator and EQ2 formats
type Manager interface {
EmuToEQ(emu EmuOpcode) uint16
EQToEmu(eq uint16) EmuOpcode
EmuToName(emu EmuOpcode) string
EQToName(eq uint16) string
}

208
packets/apppacket.go Normal file
View File

@ -0,0 +1,208 @@
package packets
import (
"encoding/binary"
"git.sharkk.net/EQ2/Protocol/opcodes"
)
// DefaultOpcodeSize is the default opcode size for application packets
var DefaultOpcodeSize uint8 = 2
// AppPacket handles high-level game opcodes (matches EQApplicationPacket)
type AppPacket struct {
*Packet
emuOpcode opcodes.EmuOpcode // Cached emulator opcode
opcodeSize uint8 // Size of opcode in bytes (1 or 2)
manager opcodes.Manager // Opcode manager for translation
}
// NewAppPacket creates a new application packet
func NewAppPacket(manager opcodes.Manager) *AppPacket {
return &AppPacket{
Packet: NewPacket(0, nil),
emuOpcode: opcodes.OP_Unknown,
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
}
// NewAppPacketWithOp creates with opcode
func NewAppPacketWithOp(op opcodes.EmuOpcode, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithSize creates with opcode and size
func NewAppPacketWithSize(op opcodes.EmuOpcode, size uint32, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, make([]byte, size)),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithData creates with opcode and data
func NewAppPacketWithData(op opcodes.EmuOpcode, data []byte, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, data),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketFromRaw creates from raw buffer (matches EQApplicationPacket constructor)
func NewAppPacketFromRaw(buf []byte, opcodeSize uint8, manager opcodes.Manager) *AppPacket {
if opcodeSize == 0 {
opcodeSize = DefaultOpcodeSize
}
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: opcodeSize,
manager: manager,
}
offset := 0
// Extract opcode based on size
if opcodeSize == 1 && len(buf) >= 1 {
app.Opcode = uint16(buf[0])
offset = 1
} else if len(buf) >= 2 {
app.Opcode = binary.BigEndian.Uint16(buf[:2])
offset = 2
}
// Copy remaining data as payload
if len(buf) > offset {
app.Buffer = make([]byte, len(buf)-offset)
copy(app.Buffer, buf[offset:])
}
// Convert EQ opcode to emulator opcode
if app.manager != nil {
app.emuOpcode = app.manager.EQToEmu(app.Opcode)
} else {
app.emuOpcode = opcodes.EmuOpcode(app.Opcode)
}
return app
}
// Size returns total packet size including opcode
func (a *AppPacket) Size() uint32 {
// Handle special encoding where low byte = 0x00 needs extra byte
extraBytes := uint32(0)
if a.opcodeSize == 2 && (a.Opcode&0x00ff) == 0 {
extraBytes = 1
}
return uint32(len(a.Buffer)) + uint32(a.opcodeSize) + extraBytes
}
// SetOpcode sets the emulator opcode and translates to EQ opcode
func (a *AppPacket) SetOpcode(op opcodes.EmuOpcode) {
a.emuOpcode = op
if a.manager != nil {
a.Opcode = a.manager.EmuToEQ(op)
} else {
a.Opcode = uint16(op)
}
}
// GetOpcode returns the emulator opcode
func (a *AppPacket) GetOpcode() opcodes.EmuOpcode {
if a.emuOpcode == opcodes.OP_Unknown && a.manager != nil {
a.emuOpcode = a.manager.EQToEmu(a.Opcode)
}
return a.emuOpcode
}
// GetOpcodeName returns the name of the current opcode
func (a *AppPacket) GetOpcodeName() string {
if a.manager != nil {
return a.manager.EmuToName(a.GetOpcode())
}
return "OP_Unknown"
}
// SetManager sets the opcode manager
func (a *AppPacket) SetManager(manager opcodes.Manager) {
a.manager = manager
// Re-translate if we have an opcode
if a.emuOpcode != opcodes.OP_Unknown && a.manager != nil {
a.Opcode = a.manager.EmuToEQ(a.emuOpcode)
}
}
// Serialize writes to destination (matches EQApplicationPacket::serialize)
func (a *AppPacket) Serialize(dest []byte) uint32 {
opcodeBytes := a.opcodeSize
pos := 0
if a.opcodeSize == 1 {
// Single-byte opcode
dest[pos] = byte(a.Opcode)
pos++
} else {
// Two-byte opcode with special encoding rules
// Application opcodes with low byte = 0x00 need extra 0x00 prefix
if (a.Opcode & 0x00ff) == 0 {
dest[pos] = 0
pos++
binary.BigEndian.PutUint16(dest[pos:], a.Opcode)
pos += 2
opcodeBytes++
} else {
binary.BigEndian.PutUint16(dest[pos:], a.Opcode)
pos += 2
}
}
// Copy packet data after opcode
copy(dest[pos:], a.Buffer)
return uint32(len(a.Buffer)) + uint32(opcodeBytes)
}
// Combine combines with another app packet (matches EQApplicationPacket::combine)
func (a *AppPacket) Combine(rhs *AppPacket) bool {
// Application packet combining is not implemented in original
// Use protocol-level combining instead
return false
}
// Copy creates a deep copy
func (a *AppPacket) Copy() *AppPacket {
newApp := &AppPacket{
Packet: NewPacket(a.Opcode, a.Buffer),
emuOpcode: a.emuOpcode,
opcodeSize: a.opcodeSize,
manager: a.manager,
}
newApp.Packet.CopyInfo(a.Packet)
return newApp
}
// ToProto converts to protocol packet
func (a *AppPacket) ToProto() *ProtoPacket {
// Serialize the application packet
tmpBuf := make([]byte, a.Size())
a.Serialize(tmpBuf)
proto := NewProtoPacket(opcodes.OP_Packet, tmpBuf, a.manager)
proto.LoginOp = a.emuOpcode
proto.CopyInfo(a.Packet)
return proto
}

347
packets/helpers.go Normal file
View File

@ -0,0 +1,347 @@
package packets
import (
"bytes"
"compress/zlib"
"encoding/binary"
"fmt"
"io"
"git.sharkk.net/EQ2/Protocol/crypto"
"git.sharkk.net/EQ2/Protocol/opcodes"
)
// ValidateCRC validates packet CRC using EQ2's custom CRC16 (matches C++ EQProtocolPacket::ValidateCRC)
func ValidateCRC(buffer []byte, key uint32) bool {
if len(buffer) < 2 {
return false
}
// Session packets are not CRC protected
if len(buffer) >= 2 && buffer[0] == 0x00 &&
(buffer[1] == byte(opcodes.OP_SessionRequest) ||
buffer[1] == byte(opcodes.OP_SessionResponse) ||
buffer[1] == byte(opcodes.OP_OutOfSession)) {
return true
}
// Combined application packets are also exempt (OP_AppCombined = 0x0019)
if len(buffer) >= 4 && buffer[2] == 0x00 && buffer[3] == 0x19 {
return true
}
// All other packets must have valid CRC
// Extract CRC from last 2 bytes (network byte order)
packetCRC := binary.BigEndian.Uint16(buffer[len(buffer)-2:])
// Calculate CRC on data portion (excluding CRC bytes)
data := buffer[:len(buffer)-2]
calculatedCRC := crypto.CalculateCRC(data, key)
// Valid if no CRC present (packetCRC == 0) or CRCs match
return packetCRC == 0 || calculatedCRC == packetCRC
}
// AppendCRC appends CRC16 to packet buffer using EQ2's custom CRC
func AppendCRC(buffer []byte, key uint32) []byte {
crc := crypto.CalculateCRC(buffer, key)
result := make([]byte, len(buffer)+2)
copy(result, buffer)
binary.BigEndian.PutUint16(result[len(buffer):], crc)
return result
}
// StripCRC removes CRC16 from packet buffer
func StripCRC(buffer []byte) []byte {
if len(buffer) < 2 {
return buffer
}
return buffer[:len(buffer)-2]
}
// Compress compresses packet data using zlib or simple encoding (matches C++ EQProtocolPacket::Compress)
// Uses zlib for packets > 30 bytes, simple encoding for smaller packets
func Compress(src []byte) ([]byte, error) {
if len(src) == 0 {
return src, nil
}
// C++ uses 30 bytes as threshold between simple encoding and zlib
if len(src) > 30 {
// Use zlib compression for larger packets
var buf bytes.Buffer
// Write uncompressed length first (4 bytes) - EQ protocol requirement
if err := binary.Write(&buf, binary.BigEndian, uint32(len(src))); err != nil {
return nil, err
}
// Compress the data
w := zlib.NewWriter(&buf)
if _, err := w.Write(src); err != nil {
w.Close()
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Use simple encoding for smaller packets (just return data as-is)
// The 0xa5 flag is added by the caller, not here
return src, nil
}
// Decompress decompresses packet data using zlib
// This is called after the compression flag has already been checked and removed
func Decompress(src []byte) ([]byte, error) {
if len(src) < 4 {
return nil, fmt.Errorf("compressed data too small")
}
// Read uncompressed length (first 4 bytes)
uncompressedLen := binary.BigEndian.Uint32(src[:4])
// Sanity check
if uncompressedLen > MaxPacketSize {
return nil, fmt.Errorf("uncompressed size %d exceeds max packet size", uncompressedLen)
}
// Create reader for compressed data (skip length prefix)
r, err := zlib.NewReader(bytes.NewReader(src[4:]))
if err != nil {
return nil, err
}
defer r.Close()
// Read decompressed data
decompressed := make([]byte, uncompressedLen)
if _, err := io.ReadFull(r, decompressed); err != nil {
return nil, err
}
return decompressed, nil
}
// CompressPacket adds compression with proper flag handling (matches C++ EQProtocolPacket::Compress)
// Uses zlib (0x5a) for packets > 30 bytes, simple encoding (0xa5) for smaller packets
func CompressPacket(buffer []byte) ([]byte, error) {
if len(buffer) < 2 {
return buffer, nil
}
// Determine flag offset based on opcode size
flagOffset := 1
if buffer[0] == 0x00 {
flagOffset = 2 // Two-byte opcode
}
if len(buffer) <= flagOffset {
return buffer, nil // Too small to compress
}
// Get data to compress (after opcode)
dataToCompress := buffer[flagOffset:]
// Choose compression method based on size (C++ uses 30 byte threshold)
if len(dataToCompress) > 30 {
// Use zlib compression
compressed, err := Compress(dataToCompress)
if err != nil {
return nil, err
}
// Only use if compression actually reduced size
if len(compressed) < len(dataToCompress) {
result := make([]byte, flagOffset+1+len(compressed))
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
result[flagOffset] = 0x5a // Zlib flag
copy(result[flagOffset+1:], compressed) // Compressed data
return result, nil
}
} else {
// Use simple encoding - just add flag
result := make([]byte, len(buffer)+1)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
result[flagOffset] = 0xa5 // Simple encoding flag
copy(result[flagOffset+1:], dataToCompress) // Original data
return result, nil
}
// No compression if it doesn't help
return buffer, nil
}
// DecompressPacket handles decompression with flag checking (matches C++ EQProtocolPacket::Decompress)
// Supports both zlib compression (0x5a) and simple encoding (0xa5)
func DecompressPacket(buffer []byte) ([]byte, error) {
if len(buffer) < 3 {
return buffer, nil // Too small to be compressed
}
// Determine flag offset based on opcode size
flagOffset := uint32(1)
if buffer[0] == 0x00 {
flagOffset = 2 // Two-byte opcode
}
if uint32(len(buffer)) <= flagOffset {
return buffer, nil // No room for compression flag
}
compressionFlag := buffer[flagOffset]
// Check compression type
if compressionFlag == 0x5a {
// Zlib compression
// Decompress data after flag, excluding last 2 CRC bytes
dataStart := flagOffset + 1
dataEnd := uint32(len(buffer)) - 2
if dataEnd <= dataStart {
return nil, fmt.Errorf("invalid compressed packet size")
}
decompressed, err := Decompress(buffer[dataStart:dataEnd])
if err != nil {
return nil, fmt.Errorf("zlib decompression failed: %w", err)
}
// Rebuild packet: opcode + decompressed data + CRC
result := make([]byte, flagOffset+uint32(len(decompressed))+2)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
copy(result[flagOffset:], decompressed) // Copy decompressed data
// Copy CRC bytes
result[len(result)-2] = buffer[len(buffer)-2]
result[len(result)-1] = buffer[len(buffer)-1]
return result, nil
} else if compressionFlag == 0xa5 {
// Simple encoding - just remove the encoding flag
result := make([]byte, len(buffer)-1)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
copy(result[flagOffset:], buffer[flagOffset+1:]) // Skip flag, copy rest
return result, nil
}
// No compression
return buffer, nil
}
// ChatEncode encodes chat data using EQ's XOR-based encoding (matches C++ EQProtocolPacket::ChatEncode)
// Uses 4-byte block XOR with rolling key that updates from encrypted data
func ChatEncode(buffer []byte, encodeKey int) {
if len(buffer) <= 2 || encodeKey == 0 {
return
}
// Skip encoding for certain packet types (matches C++ conditions)
if buffer[1] == 0x01 || buffer[0] == 0x02 || buffer[0] == 0x1d {
return
}
// Skip first 2 bytes (opcode)
data := buffer[2:]
size := len(data)
key := int32(encodeKey)
// Encode 4-byte blocks with rolling key
i := 0
for ; i+4 <= size; i += 4 {
// Read 4 bytes as int32 (little-endian like C++)
pt := binary.LittleEndian.Uint32(data[i:i+4])
encrypted := pt ^ uint32(key)
key = int32(encrypted) // Update key with encrypted data
binary.LittleEndian.PutUint32(data[i:i+4], encrypted)
}
// Encode remaining bytes with last key byte
keyByte := byte(key & 0xFF)
for ; i < size; i++ {
data[i] ^= keyByte
}
}
// ChatDecode decodes chat data using EQ's XOR-based encoding (matches C++ EQProtocolPacket::ChatDecode)
// Uses 4-byte block XOR with rolling key that updates from encrypted data
func ChatDecode(buffer []byte, decodeKey int) {
if len(buffer) <= 2 || decodeKey == 0 {
return
}
// Skip decoding for certain packet types (matches C++ conditions)
if buffer[1] == 0x01 || buffer[0] == 0x02 || buffer[0] == 0x1d {
return
}
// Skip first 2 bytes (opcode)
data := buffer[2:]
size := len(data)
key := int32(decodeKey)
// Decode 4-byte blocks with rolling key
i := 0
for ; i+4 <= size; i += 4 {
// Read 4 bytes as int32 (little-endian like C++)
encrypted := binary.LittleEndian.Uint32(data[i:i+4])
decrypted := encrypted ^ uint32(key)
key = int32(encrypted) // Update key with encrypted data (before decryption)
binary.LittleEndian.PutUint32(data[i:i+4], decrypted)
}
// Decode remaining bytes with last key byte
keyByte := byte(key & 0xFF)
for ; i < size; i++ {
data[i] ^= keyByte
}
}
// IsChatPacket checks if opcode is a chat-related packet
func IsChatPacket(opcode uint16) bool {
chatOpcodes := map[uint16]bool{
0x0300: true, // OP_ChatMsg
0x0302: true, // OP_TellMsg
0x0307: true, // OP_ChatLeaveChannelMsg
0x0308: true, // OP_ChatTellChannelMsg
0x0309: true, // OP_ChatTellUserMsg
0x0e07: true, // OP_GuildsayMsg
}
return chatOpcodes[opcode]
}
// longToIP converts uint32 IP to string
func longToIP(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d",
byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
}
// IsProtocolPacket checks if buffer contains a valid protocol packet (matches C++ EQProtocolPacket::IsProtocolPacket)
func IsProtocolPacket(buffer []byte) bool {
if len(buffer) < 2 {
return false
}
opcode := binary.BigEndian.Uint16(buffer[:2])
// Check against known protocol opcodes
switch opcode {
case opcodes.OP_SessionRequest,
opcodes.OP_SessionResponse,
opcodes.OP_Combined,
opcodes.OP_SessionDisconnect,
opcodes.OP_KeepAlive,
opcodes.OP_SessionStatRequest,
opcodes.OP_SessionStatResponse,
opcodes.OP_Packet,
opcodes.OP_Fragment,
opcodes.OP_Ack,
opcodes.OP_AppCombined,
opcodes.OP_OutOfOrderAck,
opcodes.OP_OutOfSession:
return true
default:
return false
}
}

89
packets/packet.go Normal file
View File

@ -0,0 +1,89 @@
package packets
import (
"fmt"
"io"
"time"
)
// Packet is the base struct for all EverQuest packet types
type Packet struct {
Buffer []byte
Opcode uint16
SrcIP uint32
DstIP uint32
SrcPort uint16
DstPort uint16
Priority uint32
Timestamp time.Time
Version int16
}
// NewPacket creates a new packet with the given opcode and data
func NewPacket(opcode uint16, data []byte) *Packet {
p := &Packet{
Opcode: opcode,
Timestamp: time.Now(),
}
if len(data) > 0 {
p.Buffer = make([]byte, len(data))
copy(p.Buffer, data)
}
return p
}
// Size returns the total packet size including opcode
func (p *Packet) Size() uint32 {
return uint32(len(p.Buffer)) + 2
}
// RawOpcode returns the raw opcode value
func (p *Packet) RawOpcode() uint16 {
return p.Opcode
}
// SetSrcInfo sets source IP and port
func (p *Packet) SetSrcInfo(ip uint32, port uint16) {
p.SrcIP = ip
p.SrcPort = port
}
// SetDstInfo sets destination IP and port
func (p *Packet) SetDstInfo(ip uint32, port uint16) {
p.DstIP = ip
p.DstPort = port
}
// CopyInfo copies network and timing info from another packet
func (p *Packet) CopyInfo(other *Packet) {
p.SrcIP = other.SrcIP
p.SrcPort = other.SrcPort
p.DstIP = other.DstIP
p.DstPort = other.DstPort
p.Timestamp = other.Timestamp
p.Version = other.Version
}
// DumpRaw writes packet contents to writer for debugging
func (p *Packet) DumpRaw(w io.Writer) {
p.DumpRawHeader(0xffff, w)
if len(p.Buffer) > 0 {
// TODO: Implement dump_message_column equivalent
fmt.Fprintf(w, "[Data: %d bytes]\n", len(p.Buffer))
}
}
// DumpRawHeader writes packet header info to writer
func (p *Packet) DumpRawHeader(seq uint16, w io.Writer) {
if p.SrcIP != 0 {
fmt.Fprintf(w, "[%s:%d->%s:%d] ",
longToIP(p.SrcIP), p.SrcPort,
longToIP(p.DstIP), p.DstPort)
}
if seq != 0xffff {
fmt.Fprintf(w, "[Seq=%d] ", seq)
}
fmt.Fprintf(w, "[OpCode 0x%04x Size=%d]\n", p.Opcode, len(p.Buffer))
}

561
packets/protopacket.go Normal file
View File

@ -0,0 +1,561 @@
package packets
import (
"encoding/binary"
"fmt"
"time"
"git.sharkk.net/EQ2/Protocol/crypto"
"git.sharkk.net/EQ2/Protocol/opcodes"
)
// ProtoPacket handles low-level protocol features (matches EQProtocolPacket/EQ2Packet)
type ProtoPacket struct {
*Packet
// Protocol state flags
eq2Compressed bool
packetPrepared bool
packetEncrypted bool
acked bool
// EQ2-specific
LoginOp opcodes.EmuOpcode
// Reliability and sequencing
Sequence int32
SentTime int32
AttemptCount int8
// Opcode manager for translation
manager opcodes.Manager
// Compression/encoding settings
CompressThreshold int
EncodeKey int
// Client version for protocol compatibility
clientVersion int16
}
const DefaultCompressThreshold = 100
// NewProtoPacket creates a protocol packet with opcode and buffer
func NewProtoPacket(op uint16, buf []byte, manager opcodes.Manager) *ProtoPacket {
return &ProtoPacket{
Packet: NewPacket(op, buf),
manager: manager,
CompressThreshold: DefaultCompressThreshold,
}
}
// NewEQ2Packet creates an EQ2 protocol packet (matches EQ2Packet constructor)
func NewEQ2Packet(loginOp opcodes.EmuOpcode, buf []byte, manager opcodes.Manager) *ProtoPacket {
return &ProtoPacket{
Packet: NewPacket(opcodes.OP_Packet, buf),
LoginOp: loginOp,
manager: manager,
CompressThreshold: DefaultCompressThreshold,
}
}
// NewProtoPacketFromRaw creates from raw buffer (matches EQProtocolPacket constructor)
func NewProtoPacketFromRaw(buf []byte, opcodeOverride int, manager opcodes.Manager) *ProtoPacket {
var offset uint32
var opcode uint16
if opcodeOverride >= 0 {
opcode = uint16(opcodeOverride)
} else if len(buf) >= 2 {
opcode = binary.BigEndian.Uint16(buf[:2])
offset = 2
}
var data []byte
if uint32(len(buf)) > offset {
data = make([]byte, len(buf)-int(offset))
copy(data, buf[offset:])
}
pp := &ProtoPacket{
Packet: &Packet{
Opcode: opcode,
Buffer: data,
Timestamp: time.Now(),
},
manager: manager,
CompressThreshold: DefaultCompressThreshold,
}
if pp.manager != nil {
pp.LoginOp = pp.manager.EQToEmu(opcode)
}
return pp
}
// SetVersion sets the client version
func (p *ProtoPacket) SetVersion(version int16) {
p.clientVersion = version
p.Version = version
}
// SetSequence sets the packet sequence number
func (p *ProtoPacket) SetSequence(seq int32) {
p.Sequence = seq
}
// IsPrepared returns whether packet has been prepared
func (p *ProtoPacket) IsPrepared() bool {
return p.packetPrepared
}
// IsEncrypted returns whether packet is encrypted
func (p *ProtoPacket) IsEncrypted() bool {
return p.packetEncrypted
}
// PreparePacket prepares an EQ2 packet for transmission (matches EQ2Packet::PreparePacket)
func (p *ProtoPacket) PreparePacket(maxLen uint16) int8 {
if p.packetPrepared {
return 0
}
if p.manager == nil {
return -1
}
p.packetPrepared = true
// Convert emulator opcode to network opcode
loginOpcode := p.manager.EmuToEQ(p.LoginOp)
if loginOpcode == 0xcdcd { // Invalid opcode
return -1
}
// NOTE: Compression is NOT done here in C++, it's done separately
// via EQ2_Compress after PreparePacket
// Calculate new size: sequence(2) + compressed_flag(1) + opcode + data
var offset int8
newSize := len(p.Buffer) + 2 + 1 // seq(2) + compress_flag(1)
oversized := false
// Handle different opcode sizes and formats
if loginOpcode != 2 { // Not OP_SessionResponse
newSize++ // for opcode type byte
if loginOpcode >= 255 {
newSize += 2 // oversized opcode needs extra 2 bytes
oversized = true
} else {
// Swap bytes for network order
loginOpcode = (loginOpcode>>8)&0xFF | (loginOpcode<<8)&0xFF00
}
}
// Build new buffer
newBuffer := make([]byte, newSize)
// Leave first 2 bytes for sequence (filled by SequencedPush)
ptr := 2
// Space for compress flag (will be 0 for uncompressed)
ptr++ // This is at position 2, will be overwritten if compressed
if loginOpcode != 2 {
if oversized {
newBuffer[ptr] = 0xff // Oversized marker
ptr++
}
// Write opcode as 2 bytes
binary.BigEndian.PutUint16(newBuffer[ptr:], loginOpcode)
ptr += 2
} else {
// OP_SessionResponse: just 1 byte
newBuffer[ptr] = byte(loginOpcode)
ptr++
}
// Copy original packet data
copy(newBuffer[ptr:], p.Buffer)
// Replace buffer
p.Buffer = newBuffer
offset = int8(newSize - len(p.Buffer) - 1)
return offset
}
// IsCompressed returns whether the packet is compressed
func (p *ProtoPacket) IsCompressed() bool {
return p.eq2Compressed
}
// EQ2Compress compresses packet data (matches EQStream::EQ2_Compress and C++ EQProtocolPacket::Compress)
func (p *ProtoPacket) EQ2Compress(offset int8) int8 {
if offset <= 0 || int(offset) >= len(p.Buffer) {
return 0
}
// Compress data from offset onwards
dataToCompress := p.Buffer[offset:]
dataLen := len(dataToCompress)
// C++ uses 30 bytes as threshold - match this exactly
if dataLen > 30 {
// Use zlib compression for larger packets
compressed, err := Compress(dataToCompress)
if err != nil || len(compressed) >= dataLen {
return 0 // Compression failed or didn't reduce size
}
// Rebuild buffer with zlib compression flag (0x5a)
newSize := int(offset) + len(compressed)
newBuffer := make([]byte, newSize)
// Copy header bytes before offset
copy(newBuffer[:offset], p.Buffer[:offset])
// Set zlib compression flag at offset-1
newBuffer[offset-1] = 0x5a
// Copy compressed data
copy(newBuffer[offset:], compressed)
p.Buffer = newBuffer
p.eq2Compressed = true
return offset - 1 // Return compression flag position
} else {
// Use simple encoding for smaller packets (0xa5)
// Simple encoding just adds a flag, doesn't change data
newSize := len(p.Buffer) + 1
newBuffer := make([]byte, newSize)
// Copy header bytes before offset
copy(newBuffer[:offset], p.Buffer[:offset])
// Set simple encoding flag at offset-1
newBuffer[offset-1] = 0xa5
// Copy data after flag (shift by 1 byte)
copy(newBuffer[offset:], p.Buffer[offset-1:])
p.Buffer = newBuffer
p.eq2Compressed = true
return offset - 1 // Return compression flag position
}
}
// EncryptPacket encrypts packet data (matches EQStream::EncryptPacket)
func (p *ProtoPacket) EncryptPacket(cipher *crypto.Ciphers, compressOffset int8, offset int8) {
if cipher == nil || !cipher.IsEncrypted() || len(p.Buffer) <= 2 {
return
}
p.packetEncrypted = true
if p.eq2Compressed && compressOffset > 0 {
// Encrypted compressed data from compress offset
if int(compressOffset) < len(p.Buffer) {
cipher.Encrypt(p.Buffer[compressOffset:])
}
} else {
// Encrypt uncompressed data from 2 + offset
startPos := 2 + int(offset)
if startPos < len(p.Buffer) {
cipher.Encrypt(p.Buffer[startPos:])
}
}
}
// Serialize writes the protocol packet to a destination buffer
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
pos := 0
// Write compression flag if compressed
if p.eq2Compressed {
dest[pos] = 0x5a
pos++
}
// Write opcode (2 bytes)
binary.BigEndian.PutUint16(dest[pos:], p.Opcode)
pos += 2
// Copy packet data after opcode
if offset < int8(len(p.Buffer)) {
copy(dest[pos:], p.Buffer[offset:])
return uint32(len(p.Buffer)-int(offset)) + uint32(pos)
}
return uint32(pos)
}
// Combine combines this protocol packet with another (matches EQProtocolPacket::combine)
func (p *ProtoPacket) Combine(rhs *ProtoPacket) bool {
const opCombined = 0x03 // OP_Combined
// Case 1: This packet is already combined - append to it
if p.Opcode == opCombined && p.Size()+rhs.Size()+3 < 256 {
newSize := len(p.Buffer) + int(rhs.Size()) + 1
newBuffer := make([]byte, newSize)
// Copy existing combined data
copy(newBuffer, p.Buffer)
offset := len(p.Buffer)
// Add size prefix for new packet
newBuffer[offset] = byte(rhs.Size())
offset++
// Serialize and append new packet
tmpBuf := make([]byte, rhs.Size())
rhs.Serialize(tmpBuf, 0)
copy(newBuffer[offset:], tmpBuf)
p.Buffer = newBuffer
return true
}
// Case 2: Neither packet is combined - create new combined packet
if p.Size()+rhs.Size()+4 < 256 {
totalSize := int(p.Size()) + int(rhs.Size()) + 2
newBuffer := make([]byte, totalSize)
offset := 0
// Add first packet with size prefix
newBuffer[offset] = byte(p.Size())
offset++
tmpBuf := make([]byte, p.Size())
p.Serialize(tmpBuf, 0)
copy(newBuffer[offset:], tmpBuf)
offset += int(p.Size())
// Add second packet with size prefix
newBuffer[offset] = byte(rhs.Size())
offset++
tmpBuf = make([]byte, rhs.Size())
rhs.Serialize(tmpBuf, 0)
copy(newBuffer[offset:], tmpBuf)
// Update buffer and mark as combined
p.Buffer = newBuffer
p.Opcode = opCombined
return true
}
return false
}
// AppCombine combines app-level packets (matches EQ2Packet::AppCombine)
func (p *ProtoPacket) AppCombine(rhs *ProtoPacket) bool {
const opAppCombined = 0x19 // OP_AppCombined
lhsSize := p.Size()
rhsSize := rhs.Size()
// Check max combined size
if lhsSize+rhsSize > MaxCombinedSize {
return false
}
// If already combined, add to it
if p.Opcode == opAppCombined {
tmpSize := rhsSize - 2 // Subtract opcode bytes
var newSize int
if tmpSize >= 255 {
newSize = len(p.Buffer) + int(tmpSize) + 3 // oversized header
} else {
newSize = len(p.Buffer) + int(tmpSize) + 1 // normal header
}
newBuffer := make([]byte, newSize)
copy(newBuffer, p.Buffer)
pos := len(p.Buffer)
// Add size header
if tmpSize >= 255 {
newBuffer[pos] = 255 // Oversized marker
pos++
binary.BigEndian.PutUint16(newBuffer[pos:], uint16(tmpSize))
pos += 2
} else {
newBuffer[pos] = byte(tmpSize)
pos++
}
// Serialize rhs packet (skip first 2 bytes - opcode)
if len(rhs.Buffer) > 2 {
copy(newBuffer[pos:], rhs.Buffer[2:])
}
p.Buffer = newBuffer
return true
}
// Create new combined packet
lSize := lhsSize - 2
rSize := rhsSize - 2
totalSize := 0
if lSize >= 255 {
totalSize += 3 + int(lSize)
} else {
totalSize += 1 + int(lSize)
}
if rSize >= 255 {
totalSize += 3 + int(rSize)
} else {
totalSize += 1 + int(rSize)
}
if totalSize > MaxCombinedSize {
return false
}
newBuffer := make([]byte, totalSize)
pos := 0
// Add first packet
if lSize >= 255 {
newBuffer[pos] = 255
pos++
binary.BigEndian.PutUint16(newBuffer[pos:], uint16(lSize))
pos += 2
} else {
newBuffer[pos] = byte(lSize)
pos++
}
if len(p.Buffer) > 2 {
copy(newBuffer[pos:], p.Buffer[2:])
pos += int(lSize)
}
// Add second packet
if rSize >= 255 {
newBuffer[pos] = 255
pos++
binary.BigEndian.PutUint16(newBuffer[pos:], uint16(rSize))
pos += 2
} else {
newBuffer[pos] = byte(rSize)
pos++
}
if len(rhs.Buffer) > 2 {
copy(newBuffer[pos:], rhs.Buffer[2:])
}
p.Opcode = opAppCombined
p.Buffer = newBuffer
p.packetPrepared = false
return true
}
// MakeApplicationPacket converts to app packet (matches EQProtocolPacket::MakeApplicationPacket)
func (p *ProtoPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
// Decompress if needed - handle both zlib and simple encoding
if p.eq2Compressed && len(p.Buffer) > 2 {
// Check compression type at position 2 (after sequence bytes)
compressionFlag := byte(0)
if len(p.Buffer) > 2 {
compressionFlag = p.Buffer[2]
}
if compressionFlag == 0x5a {
// Zlib compression - decompress data after flag
if len(p.Buffer) > 3 {
if decompressed, err := Decompress(p.Buffer[3:]); err == nil {
// Rebuild buffer without compression
newBuffer := make([]byte, 2+len(decompressed))
copy(newBuffer[:2], p.Buffer[:2]) // Copy sequence
copy(newBuffer[2:], decompressed)
p.Buffer = newBuffer
p.eq2Compressed = false
}
}
} else if compressionFlag == 0xa5 {
// Simple encoding - just remove the flag
if len(p.Buffer) > 3 {
newBuffer := make([]byte, len(p.Buffer)-1)
copy(newBuffer[:2], p.Buffer[:2]) // Copy sequence
copy(newBuffer[2:], p.Buffer[3:]) // Skip flag
p.Buffer = newBuffer
p.eq2Compressed = false
}
}
}
// Decode chat if needed
if p.packetEncrypted && p.EncodeKey != 0 {
ChatDecode(p.Buffer, p.EncodeKey)
p.packetEncrypted = false
}
return NewAppPacketFromRaw(p.Buffer, opcodeSize, p.manager)
}
// Copy creates a deep copy
func (p *ProtoPacket) Copy() *ProtoPacket {
newPacket := &ProtoPacket{
Packet: NewPacket(p.Opcode, p.Buffer),
eq2Compressed: p.eq2Compressed,
packetPrepared: p.packetPrepared,
packetEncrypted: p.packetEncrypted,
acked: p.acked,
LoginOp: p.LoginOp,
Sequence: p.Sequence,
SentTime: p.SentTime,
AttemptCount: p.AttemptCount,
manager: p.manager,
CompressThreshold: p.CompressThreshold,
EncodeKey: p.EncodeKey,
}
newPacket.Packet.CopyInfo(p.Packet)
return newPacket
}
// GetOpcodeName returns human-readable name for the opcode (matches C++ EQ2Packet::GetOpcodeName)
func (p *ProtoPacket) GetOpcodeName() string {
// Use manager to get opcode name if available
if p.manager != nil {
if name := p.manager.EmuToName(p.LoginOp); name != "" {
return name
}
}
// Fall back to protocol opcode names
switch p.Opcode {
case opcodes.OP_SessionRequest:
return "OP_SessionRequest"
case opcodes.OP_SessionResponse:
return "OP_SessionResponse"
case opcodes.OP_Combined:
return "OP_Combined"
case opcodes.OP_SessionDisconnect:
return "OP_SessionDisconnect"
case opcodes.OP_KeepAlive:
return "OP_KeepAlive"
case opcodes.OP_SessionStatRequest:
return "OP_SessionStatRequest"
case opcodes.OP_SessionStatResponse:
return "OP_SessionStatResponse"
case opcodes.OP_Packet:
return "OP_Packet"
case opcodes.OP_Fragment:
return "OP_Fragment"
case opcodes.OP_Ack:
return "OP_Ack"
case opcodes.OP_AppCombined:
return "OP_AppCombined"
case opcodes.OP_OutOfOrderAck:
return "OP_OutOfOrderAck"
case opcodes.OP_OutOfSession:
return "OP_OutOfSession"
default:
return fmt.Sprintf("Unknown(0x%04X)", p.Opcode)
}
}

34
packets/types.go Normal file
View File

@ -0,0 +1,34 @@
package packets
// PacketSerializer interface for common packet operations
type PacketSerializer interface {
Serialize(dest []byte) uint32
Size() uint32
Copy() PacketSerializer
}
// Stream states
type StreamState uint8
const (
StateDisconnected StreamState = iota
StateConnecting
StateEstablished
StateClosing
StateClosed
)
// Packet priority levels
const (
PriorityLow uint32 = 0
PriorityNormal uint32 = 5
PriorityHigh uint32 = 10
)
// Max packet sizes
const (
MaxPacketSize = 0xFFFF
DefaultMTU = 1450
MaxCombinedSize = 512
MaxFragmentChunk = 0xFFFF
)

271
stream/factory.go Normal file
View File

@ -0,0 +1,271 @@
package stream
import (
"database/sql"
"log"
"sync"
"time"
"git.sharkk.net/EQ2/Protocol/opcodes"
"git.sharkk.net/EQ2/Protocol/packets"
"github.com/panjf2000/gnet/v2"
)
// StreamFactory manages EQStream instances using gnet
type StreamFactory struct {
gnet.BuiltinEventEngine
mu sync.RWMutex
streams map[gnet.Conn]*Stream
// Configuration
crcKey uint32
maxPacketSize uint16
encodeKey int
decodeKey int
opcodeSize uint8
clientVersion int16
// Database for opcodes
db *sql.DB
opcodeTable string
opcodeManager opcodes.Manager
// Callbacks
onNewStream func(*Stream)
onStreamClosed func(*Stream)
onPacket func(*Stream, *packets.AppPacket)
// Stats
totalStreams uint64
activeStreams uint64
}
// FactoryConfig holds factory configuration
type FactoryConfig struct {
CRCKey uint32
MaxPacketSize uint16
EncodeKey int
DecodeKey int
OpcodeSize uint8
ClientVersion int16
Database *sql.DB
OpcodeTable string
}
// NewStreamFactory creates a new factory
func NewStreamFactory(cfg *FactoryConfig) *StreamFactory {
f := &StreamFactory{
streams: make(map[gnet.Conn]*Stream),
crcKey: cfg.CRCKey,
maxPacketSize: cfg.MaxPacketSize,
encodeKey: cfg.EncodeKey,
decodeKey: cfg.DecodeKey,
opcodeSize: cfg.OpcodeSize,
clientVersion: cfg.ClientVersion,
db: cfg.Database,
opcodeTable: cfg.OpcodeTable,
}
if f.maxPacketSize == 0 {
f.maxPacketSize = packets.DefaultMTU
}
if f.opcodeSize == 0 {
f.opcodeSize = packets.DefaultOpcodeSize
}
if f.opcodeTable == "" {
f.opcodeTable = "opcodes"
}
// Initialize opcode manager
if cfg.ClientVersion > 0 && cfg.Database != nil {
f.opcodeManager = opcodes.GetManager(cfg.ClientVersion, cfg.Database, f.opcodeTable)
}
return f
}
// OnBoot called when server starts
func (f *StreamFactory) OnBoot(eng gnet.Engine) gnet.Action {
log.Printf("StreamFactory started")
return gnet.None
}
// OnOpen called when new connection opens
func (f *StreamFactory) OnOpen(c gnet.Conn) ([]byte, gnet.Action) {
// Create stream configuration
streamCfg := &Config{
SessionID: uint32(time.Now().UnixNano()),
CRCKey: f.crcKey,
MaxPacketSize: f.maxPacketSize,
EncodeKey: f.encodeKey,
DecodeKey: f.decodeKey,
OpcodeManager: f.opcodeManager,
OpcodeSize: f.opcodeSize,
AckThreshold: 200 * time.Millisecond,
KeepaliveTime: 10 * time.Second,
TimeoutDuration: 30 * time.Second,
}
// Create new stream
stream := NewStream(c, streamCfg)
// Set packet callback
stream.SetPacketCallback(func(app *packets.AppPacket) {
if f.onPacket != nil {
f.onPacket(stream, app)
}
})
// Store stream
f.mu.Lock()
f.streams[c] = stream
f.activeStreams++
f.totalStreams++
f.mu.Unlock()
// Notify callback
if f.onNewStream != nil {
f.onNewStream(stream)
}
c.SetContext(stream)
log.Printf("New stream from %s", c.RemoteAddr())
return nil, gnet.None
}
// OnClose called when connection closes
func (f *StreamFactory) OnClose(c gnet.Conn, err error) gnet.Action {
f.mu.Lock()
stream, exists := f.streams[c]
if exists {
delete(f.streams, c)
f.activeStreams--
}
f.mu.Unlock()
if stream != nil {
stream.Close()
// Notify callback
if f.onStreamClosed != nil {
f.onStreamClosed(stream)
}
log.Printf("Stream closed: %s", c.RemoteAddr())
}
return gnet.None
}
// OnTraffic handles incoming data
func (f *StreamFactory) OnTraffic(c gnet.Conn) gnet.Action {
stream := c.Context().(*Stream)
if stream == nil {
return gnet.Close
}
// Read all available data
buf, err := c.Next(-1)
if err != nil {
return gnet.Close
}
// Process packet
if err := stream.Process(buf); err != nil {
log.Printf("Stream process error: %v", err)
return gnet.Close
}
return gnet.None
}
// OnTick called periodically to process stream queues
func (f *StreamFactory) OnTick() (time.Duration, gnet.Action) {
f.mu.RLock()
streams := make([]*Stream, 0, len(f.streams))
for _, stream := range f.streams {
streams = append(streams, stream)
}
f.mu.RUnlock()
// Process queues for all streams
for _, stream := range streams {
stream.processQueues()
}
return 100 * time.Millisecond, gnet.None
}
// GetStream returns stream for a connection
func (f *StreamFactory) GetStream(c gnet.Conn) *Stream {
f.mu.RLock()
defer f.mu.RUnlock()
return f.streams[c]
}
// GetAllStreams returns all active streams
func (f *StreamFactory) GetAllStreams() []*Stream {
f.mu.RLock()
defer f.mu.RUnlock()
result := make([]*Stream, 0, len(f.streams))
for _, stream := range f.streams {
result = append(result, stream)
}
return result
}
// SetNewStreamCallback sets callback for new streams
func (f *StreamFactory) SetNewStreamCallback(fn func(*Stream)) {
f.onNewStream = fn
}
// SetStreamClosedCallback sets callback for closed streams
func (f *StreamFactory) SetStreamClosedCallback(fn func(*Stream)) {
f.onStreamClosed = fn
}
// SetPacketCallback sets global packet callback
func (f *StreamFactory) SetPacketCallback(fn func(*Stream, *packets.AppPacket)) {
f.onPacket = fn
}
// UpdateOpcodeManager updates opcodes for a specific version
func (f *StreamFactory) UpdateOpcodeManager(version int16) {
if f.db != nil {
f.opcodeManager = opcodes.GetManager(version, f.db, f.opcodeTable)
log.Printf("Updated opcode manager to version %d", version)
}
}
// Stats returns factory statistics
func (f *StreamFactory) Stats() map[string]uint64 {
f.mu.RLock()
defer f.mu.RUnlock()
return map[string]uint64{
"total_streams": f.totalStreams,
"active_streams": f.activeStreams,
}
}
// Run starts the factory as a UDP server
func Run(addr string, factory *StreamFactory) error {
return gnet.Run(factory, addr,
gnet.WithMulticore(true),
gnet.WithReuseAddr(true),
gnet.WithReusePort(true),
gnet.WithTicker(true),
gnet.WithTCPKeepAlive(0), // Disable for UDP
gnet.WithSocketRecvBuffer(1024*1024),
gnet.WithSocketSendBuffer(1024*1024),
)
}
// RunTLS starts the factory with TLS (future enhancement)
func RunTLS(addr string, factory *StreamFactory, certFile, keyFile string) error {
// TLS configuration would go here if needed
return Run(addr, factory)
}

1170
stream/stream.go Normal file

File diff suppressed because it is too large Load Diff