From 748321fcdbf9db31ab635b0bf95570232371cc30 Mon Sep 17 00:00:00 2001 From: barncastle Date: Mon, 14 Aug 2017 21:25:46 +0100 Subject: [PATCH] Initial Commit --- .gitignore | 253 ++ AIO Sandbox.sln | 76 + Common/Commands/CommandHelpAttribute.cs | 24 + Common/Commands/CommandManager.cs | 54 + Common/Commands/Commands.cs | 219 ++ Common/Common.csproj | 82 + Common/Constants/AreaTriggers.cs | 131 + Common/Constants/Emotes.cs | 331 ++ Common/Constants/Enums.cs | 54 + Common/Constants/Extensions.cs | 33 + Common/Constants/Opcodes.cs | 86 + Common/Constants/Worldports.cs | 251 ++ Common/Cryptography/BigInteger.cs | 2982 +++++++++++++++++ Common/Cryptography/ClientAuth.cs | 157 + Common/Extensions/CharacterExtensions.cs | 119 + Common/Extensions/WorldExtensions.cs | 24 + Common/Interfaces/Handlers/IAuthHandler.cs | 14 + Common/Interfaces/Handlers/ICharHandler.cs | 19 + Common/Interfaces/Handlers/IWorldHandler.cs | 16 + Common/Interfaces/ICharacter.cs | 47 + Common/Interfaces/IOpcodes.cs | 14 + Common/Interfaces/IPacketReader.cs | 32 + Common/Interfaces/IPacketWriter.cs | 33 + Common/Interfaces/ISandbox.cs | 23 + Common/Interfaces/IWorldManager.cs | 15 + Common/Libs/ObjectDB/Db4objects.Db4o.Linq.dll | Bin 0 -> 58880 bytes Common/Libs/ObjectDB/Db4objects.Db4o.dll | Bin 0 -> 775168 bytes Common/Logging/Log.cs | 83 + Common/Network/PacketManager.cs | 27 + Common/Properties/AssemblyInfo.cs | 36 + Common/Structs/Account.cs | 109 + Common/Structs/Location.cs | 73 + Plugins/Alpha_3368/Alpha_3368.csproj | 68 + Plugins/Alpha_3368/Character.cs | 293 ++ Plugins/Alpha_3368/Handlers/AuthHandler.cs | 75 + Plugins/Alpha_3368/Handlers/CharHandler.cs | 213 ++ Plugins/Alpha_3368/Handlers/WorldHandler.cs | 82 + Plugins/Alpha_3368/Opcodes.cs | 80 + Plugins/Alpha_3368/PacketReader.cs | 121 + Plugins/Alpha_3368/PacketWriter.cs | 125 + Plugins/Alpha_3368/Properties/AssemblyInfo.cs | 36 + Plugins/Alpha_3368/Sandbox.cs | 29 + Plugins/Beta_3592/Beta_3592.csproj | 68 + Plugins/Beta_3592/Character.cs | 302 ++ Plugins/Beta_3592/Handlers/AuthHandler.cs | 107 + Plugins/Beta_3592/Handlers/CharHandler.cs | 212 ++ Plugins/Beta_3592/Handlers/WorldHandler.cs | 97 + Plugins/Beta_3592/Opcodes.cs | 82 + Plugins/Beta_3592/PacketReader.cs | 120 + Plugins/Beta_3592/PacketWriter.cs | 119 + Plugins/Beta_3592/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3592/Sandbox.cs | 28 + Plugins/Beta_3694/Beta_3694.csproj | 68 + Plugins/Beta_3694/Character.cs | 301 ++ Plugins/Beta_3694/Handlers/AuthHandler.cs | 112 + Plugins/Beta_3694/Handlers/CharHandler.cs | 217 ++ Plugins/Beta_3694/Handlers/WorldHandler.cs | 89 + Plugins/Beta_3694/Opcodes.cs | 83 + Plugins/Beta_3694/PacketReader.cs | 120 + Plugins/Beta_3694/PacketWriter.cs | 119 + Plugins/Beta_3694/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3694/Sandbox.cs | 32 + Plugins/Beta_3734/Beta_3734.csproj | 68 + Plugins/Beta_3734/Character.cs | 309 ++ Plugins/Beta_3734/Handlers/AuthHandler.cs | 112 + Plugins/Beta_3734/Handlers/CharHandler.cs | 216 ++ Plugins/Beta_3734/Handlers/WorldHandler.cs | 89 + Plugins/Beta_3734/Opcodes.cs | 82 + Plugins/Beta_3734/PacketReader.cs | 120 + Plugins/Beta_3734/PacketWriter.cs | 119 + Plugins/Beta_3734/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3734/Sandbox.cs | 32 + Plugins/Beta_3807/Beta_3807.csproj | 68 + Plugins/Beta_3807/Character.cs | 306 ++ Plugins/Beta_3807/Handlers/AuthHandler.cs | 112 + Plugins/Beta_3807/Handlers/CharHandler.cs | 216 ++ Plugins/Beta_3807/Handlers/WorldHandler.cs | 90 + Plugins/Beta_3807/Opcodes.cs | 82 + Plugins/Beta_3807/PacketReader.cs | 120 + Plugins/Beta_3807/PacketWriter.cs | 119 + Plugins/Beta_3807/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3807/Sandbox.cs | 32 + Plugins/Beta_3892/Beta_3892.csproj | 68 + Plugins/Beta_3892/Character.cs | 313 ++ Plugins/Beta_3892/Handlers/AuthHandler.cs | 120 + Plugins/Beta_3892/Handlers/CharHandler.cs | 216 ++ Plugins/Beta_3892/Handlers/WorldHandler.cs | 89 + Plugins/Beta_3892/Opcodes.cs | 82 + Plugins/Beta_3892/PacketReader.cs | 120 + Plugins/Beta_3892/PacketWriter.cs | 119 + Plugins/Beta_3892/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3892/Sandbox.cs | 32 + Plugins/Beta_3988/Beta_3988.csproj | 68 + Plugins/Beta_3988/Character.cs | 314 ++ Plugins/Beta_3988/Handlers/AuthHandler.cs | 121 + Plugins/Beta_3988/Handlers/CharHandler.cs | 224 ++ Plugins/Beta_3988/Handlers/WorldHandler.cs | 87 + Plugins/Beta_3988/Opcodes.cs | 84 + Plugins/Beta_3988/PacketReader.cs | 139 + Plugins/Beta_3988/PacketWriter.cs | 135 + Plugins/Beta_3988/Properties/AssemblyInfo.cs | 36 + Plugins/Beta_3988/Sandbox.cs | 31 + Plugins/TBC_Alpha_5610/Character.cs | 478 +++ .../TBC_Alpha_5610/Handlers/AuthHandler.cs | 129 + .../TBC_Alpha_5610/Handlers/CharHandler.cs | 225 ++ .../TBC_Alpha_5610/Handlers/WorldHandler.cs | 90 + Plugins/TBC_Alpha_5610/Opcodes.cs | 84 + Plugins/TBC_Alpha_5610/PacketReader.cs | 141 + Plugins/TBC_Alpha_5610/PacketWriter.cs | 137 + .../TBC_Alpha_5610/Properties/AssemblyInfo.cs | 36 + Plugins/TBC_Alpha_5610/Sandbox.cs | 31 + Plugins/TBC_Alpha_5610/TBC_Alpha_5610.csproj | 68 + README.md | 36 + WorldServer/Network/RealmManager.cs | 31 + WorldServer/Network/RealmSocket.cs | 81 + WorldServer/Network/WorldManager.cs | 67 + WorldServer/Network/WorldSocket.cs | 61 + WorldServer/Packets/HandlerDefinitions.cs | 53 + WorldServer/Plugins/PluginHandler.cs | 70 + WorldServer/Properties/AssemblyInfo.cs | 36 + WorldServer/SandboxHost.cs | 36 + WorldServer/WorldServer.cs | 68 + WorldServer/WorldServer.csproj | 83 + WorldServer/app.config | 9 + WorldServer/favicon.ico | Bin 0 -> 32038 bytes 125 files changed, 15735 insertions(+) create mode 100644 .gitignore create mode 100644 AIO Sandbox.sln create mode 100644 Common/Commands/CommandHelpAttribute.cs create mode 100644 Common/Commands/CommandManager.cs create mode 100644 Common/Commands/Commands.cs create mode 100644 Common/Common.csproj create mode 100644 Common/Constants/AreaTriggers.cs create mode 100644 Common/Constants/Emotes.cs create mode 100644 Common/Constants/Enums.cs create mode 100644 Common/Constants/Extensions.cs create mode 100644 Common/Constants/Opcodes.cs create mode 100644 Common/Constants/Worldports.cs create mode 100644 Common/Cryptography/BigInteger.cs create mode 100644 Common/Cryptography/ClientAuth.cs create mode 100644 Common/Extensions/CharacterExtensions.cs create mode 100644 Common/Extensions/WorldExtensions.cs create mode 100644 Common/Interfaces/Handlers/IAuthHandler.cs create mode 100644 Common/Interfaces/Handlers/ICharHandler.cs create mode 100644 Common/Interfaces/Handlers/IWorldHandler.cs create mode 100644 Common/Interfaces/ICharacter.cs create mode 100644 Common/Interfaces/IOpcodes.cs create mode 100644 Common/Interfaces/IPacketReader.cs create mode 100644 Common/Interfaces/IPacketWriter.cs create mode 100644 Common/Interfaces/ISandbox.cs create mode 100644 Common/Interfaces/IWorldManager.cs create mode 100644 Common/Libs/ObjectDB/Db4objects.Db4o.Linq.dll create mode 100644 Common/Libs/ObjectDB/Db4objects.Db4o.dll create mode 100644 Common/Logging/Log.cs create mode 100644 Common/Network/PacketManager.cs create mode 100644 Common/Properties/AssemblyInfo.cs create mode 100644 Common/Structs/Account.cs create mode 100644 Common/Structs/Location.cs create mode 100644 Plugins/Alpha_3368/Alpha_3368.csproj create mode 100644 Plugins/Alpha_3368/Character.cs create mode 100644 Plugins/Alpha_3368/Handlers/AuthHandler.cs create mode 100644 Plugins/Alpha_3368/Handlers/CharHandler.cs create mode 100644 Plugins/Alpha_3368/Handlers/WorldHandler.cs create mode 100644 Plugins/Alpha_3368/Opcodes.cs create mode 100644 Plugins/Alpha_3368/PacketReader.cs create mode 100644 Plugins/Alpha_3368/PacketWriter.cs create mode 100644 Plugins/Alpha_3368/Properties/AssemblyInfo.cs create mode 100644 Plugins/Alpha_3368/Sandbox.cs create mode 100644 Plugins/Beta_3592/Beta_3592.csproj create mode 100644 Plugins/Beta_3592/Character.cs create mode 100644 Plugins/Beta_3592/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3592/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3592/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3592/Opcodes.cs create mode 100644 Plugins/Beta_3592/PacketReader.cs create mode 100644 Plugins/Beta_3592/PacketWriter.cs create mode 100644 Plugins/Beta_3592/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3592/Sandbox.cs create mode 100644 Plugins/Beta_3694/Beta_3694.csproj create mode 100644 Plugins/Beta_3694/Character.cs create mode 100644 Plugins/Beta_3694/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3694/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3694/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3694/Opcodes.cs create mode 100644 Plugins/Beta_3694/PacketReader.cs create mode 100644 Plugins/Beta_3694/PacketWriter.cs create mode 100644 Plugins/Beta_3694/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3694/Sandbox.cs create mode 100644 Plugins/Beta_3734/Beta_3734.csproj create mode 100644 Plugins/Beta_3734/Character.cs create mode 100644 Plugins/Beta_3734/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3734/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3734/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3734/Opcodes.cs create mode 100644 Plugins/Beta_3734/PacketReader.cs create mode 100644 Plugins/Beta_3734/PacketWriter.cs create mode 100644 Plugins/Beta_3734/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3734/Sandbox.cs create mode 100644 Plugins/Beta_3807/Beta_3807.csproj create mode 100644 Plugins/Beta_3807/Character.cs create mode 100644 Plugins/Beta_3807/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3807/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3807/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3807/Opcodes.cs create mode 100644 Plugins/Beta_3807/PacketReader.cs create mode 100644 Plugins/Beta_3807/PacketWriter.cs create mode 100644 Plugins/Beta_3807/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3807/Sandbox.cs create mode 100644 Plugins/Beta_3892/Beta_3892.csproj create mode 100644 Plugins/Beta_3892/Character.cs create mode 100644 Plugins/Beta_3892/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3892/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3892/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3892/Opcodes.cs create mode 100644 Plugins/Beta_3892/PacketReader.cs create mode 100644 Plugins/Beta_3892/PacketWriter.cs create mode 100644 Plugins/Beta_3892/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3892/Sandbox.cs create mode 100644 Plugins/Beta_3988/Beta_3988.csproj create mode 100644 Plugins/Beta_3988/Character.cs create mode 100644 Plugins/Beta_3988/Handlers/AuthHandler.cs create mode 100644 Plugins/Beta_3988/Handlers/CharHandler.cs create mode 100644 Plugins/Beta_3988/Handlers/WorldHandler.cs create mode 100644 Plugins/Beta_3988/Opcodes.cs create mode 100644 Plugins/Beta_3988/PacketReader.cs create mode 100644 Plugins/Beta_3988/PacketWriter.cs create mode 100644 Plugins/Beta_3988/Properties/AssemblyInfo.cs create mode 100644 Plugins/Beta_3988/Sandbox.cs create mode 100644 Plugins/TBC_Alpha_5610/Character.cs create mode 100644 Plugins/TBC_Alpha_5610/Handlers/AuthHandler.cs create mode 100644 Plugins/TBC_Alpha_5610/Handlers/CharHandler.cs create mode 100644 Plugins/TBC_Alpha_5610/Handlers/WorldHandler.cs create mode 100644 Plugins/TBC_Alpha_5610/Opcodes.cs create mode 100644 Plugins/TBC_Alpha_5610/PacketReader.cs create mode 100644 Plugins/TBC_Alpha_5610/PacketWriter.cs create mode 100644 Plugins/TBC_Alpha_5610/Properties/AssemblyInfo.cs create mode 100644 Plugins/TBC_Alpha_5610/Sandbox.cs create mode 100644 Plugins/TBC_Alpha_5610/TBC_Alpha_5610.csproj create mode 100644 README.md create mode 100644 WorldServer/Network/RealmManager.cs create mode 100644 WorldServer/Network/RealmSocket.cs create mode 100644 WorldServer/Network/WorldManager.cs create mode 100644 WorldServer/Network/WorldSocket.cs create mode 100644 WorldServer/Packets/HandlerDefinitions.cs create mode 100644 WorldServer/Plugins/PluginHandler.cs create mode 100644 WorldServer/Properties/AssemblyInfo.cs create mode 100644 WorldServer/SandboxHost.cs create mode 100644 WorldServer/WorldServer.cs create mode 100644 WorldServer/WorldServer.csproj create mode 100644 WorldServer/app.config create mode 100644 WorldServer/favicon.ico diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7dd8cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,253 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +Changes.txt + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +[Tt]ools/ diff --git a/AIO Sandbox.sln b/AIO Sandbox.sln new file mode 100644 index 0000000..5aa1e94 --- /dev/null +++ b/AIO Sandbox.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorldServer", "WorldServer\WorldServer.csproj", "{B736A574-F0AD-43D2-BF24-A54EDC0BCD6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{63268176-7FF9-4B3E-AAAD-CC7C329F924A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alpha_3368", "Plugins\Alpha_3368\Alpha_3368.csproj", "{1087E940-0B75-452B-B245-24C77CE80F13}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3592", "Plugins\Beta_3592\Beta_3592.csproj", "{5B87983C-952D-4965-9E99-8F34FA31793D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3694", "Plugins\Beta_3694\Beta_3694.csproj", "{7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3734", "Plugins\Beta_3734\Beta_3734.csproj", "{625F1463-6F82-4F56-9B1E-C8F5D366C0B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3807", "Plugins\Beta_3807\Beta_3807.csproj", "{0C8366A3-73B1-405F-8742-C356AF0EAC8E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3892", "Plugins\Beta_3892\Beta_3892.csproj", "{276409AF-8D5E-415B-A61E-BE460AE2FDBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta_3988", "Plugins\Beta_3988\Beta_3988.csproj", "{85FA1408-8526-4F9E-9A04-C9C78D2F31F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TBC_Alpha_5610", "Plugins\TBC_Alpha_5610\TBC_Alpha_5610.csproj", "{19DD917C-87C6-407E-BFAA-9ECD37155040}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B736A574-F0AD-43D2-BF24-A54EDC0BCD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B736A574-F0AD-43D2-BF24-A54EDC0BCD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B736A574-F0AD-43D2-BF24-A54EDC0BCD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B736A574-F0AD-43D2-BF24-A54EDC0BCD6B}.Release|Any CPU.Build.0 = Release|Any CPU + {63268176-7FF9-4B3E-AAAD-CC7C329F924A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63268176-7FF9-4B3E-AAAD-CC7C329F924A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63268176-7FF9-4B3E-AAAD-CC7C329F924A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63268176-7FF9-4B3E-AAAD-CC7C329F924A}.Release|Any CPU.Build.0 = Release|Any CPU + {1087E940-0B75-452B-B245-24C77CE80F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1087E940-0B75-452B-B245-24C77CE80F13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1087E940-0B75-452B-B245-24C77CE80F13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1087E940-0B75-452B-B245-24C77CE80F13}.Release|Any CPU.Build.0 = Release|Any CPU + {5B87983C-952D-4965-9E99-8F34FA31793D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B87983C-952D-4965-9E99-8F34FA31793D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B87983C-952D-4965-9E99-8F34FA31793D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B87983C-952D-4965-9E99-8F34FA31793D}.Release|Any CPU.Build.0 = Release|Any CPU + {7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1}.Release|Any CPU.Build.0 = Release|Any CPU + {625F1463-6F82-4F56-9B1E-C8F5D366C0B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {625F1463-6F82-4F56-9B1E-C8F5D366C0B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {625F1463-6F82-4F56-9B1E-C8F5D366C0B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {625F1463-6F82-4F56-9B1E-C8F5D366C0B9}.Release|Any CPU.Build.0 = Release|Any CPU + {0C8366A3-73B1-405F-8742-C356AF0EAC8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C8366A3-73B1-405F-8742-C356AF0EAC8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C8366A3-73B1-405F-8742-C356AF0EAC8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C8366A3-73B1-405F-8742-C356AF0EAC8E}.Release|Any CPU.Build.0 = Release|Any CPU + {276409AF-8D5E-415B-A61E-BE460AE2FDBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {276409AF-8D5E-415B-A61E-BE460AE2FDBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {276409AF-8D5E-415B-A61E-BE460AE2FDBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {276409AF-8D5E-415B-A61E-BE460AE2FDBC}.Release|Any CPU.Build.0 = Release|Any CPU + {85FA1408-8526-4F9E-9A04-C9C78D2F31F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85FA1408-8526-4F9E-9A04-C9C78D2F31F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85FA1408-8526-4F9E-9A04-C9C78D2F31F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85FA1408-8526-4F9E-9A04-C9C78D2F31F0}.Release|Any CPU.Build.0 = Release|Any CPU + {19DD917C-87C6-407E-BFAA-9ECD37155040}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19DD917C-87C6-407E-BFAA-9ECD37155040}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19DD917C-87C6-407E-BFAA-9ECD37155040}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19DD917C-87C6-407E-BFAA-9ECD37155040}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Common/Commands/CommandHelpAttribute.cs b/Common/Commands/CommandHelpAttribute.cs new file mode 100644 index 0000000..017b09b --- /dev/null +++ b/Common/Commands/CommandHelpAttribute.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Commands +{ + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + public class CommandHelpAttribute : Attribute + { + public string HelpText { get; set; } + + public CommandHelpAttribute(string helptext) + { + this.HelpText = helptext; + } + + public override string ToString() + { + return HelpText; + } + } +} diff --git a/Common/Commands/CommandManager.cs b/Common/Commands/CommandManager.cs new file mode 100644 index 0000000..1b24d4d --- /dev/null +++ b/Common/Commands/CommandManager.cs @@ -0,0 +1,54 @@ +using Common.Interfaces; +using Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Common.Commands +{ + public class CommandManager + { + public static Dictionary CommandHandlers = new Dictionary(); + public delegate void HandleCommand(IWorldManager manager, string[] args); + + static CommandManager() + { + DefineCommand("gps", Commands.Gps); + DefineCommand("help", Commands.Help); + DefineCommand("speed", Commands.Speed); + DefineCommand("go", Commands.Go); + DefineCommand("nudge", Commands.Nudge); + DefineCommand("morph", Commands.Morph); + DefineCommand("demorph", Commands.Demorph); + } + + public static void DefineCommand(string command, HandleCommand handler) => CommandHandlers[command.ToLower()] = handler; + + public static bool InvokeHandler(string command, IWorldManager manager) + { + if (string.IsNullOrEmpty(command)) + return false; + + if (command[0] != '.') + return false; + + string[] lines = command.Split(' '); + return InvokeHandler(lines[0], manager, lines.Skip(1).ToArray()); + } + + public static bool InvokeHandler(string command, IWorldManager manager, params string[] args) + { + command = command.TrimStart('.').Trim().ToLower(); //Remove command "." prefix and format + if (CommandHandlers.ContainsKey(command)) + { + CommandHandlers[command].Invoke(manager, args); + return true; + } + + return false; + } + } +} diff --git a/Common/Commands/Commands.cs b/Common/Commands/Commands.cs new file mode 100644 index 0000000..969c959 --- /dev/null +++ b/Common/Commands/Commands.cs @@ -0,0 +1,219 @@ +using Common.Constants; +using Common.Interfaces; +using System; +using System.Linq; +using System.Reflection; + +namespace Common.Commands +{ + public class Commands + { + #region Coordinates + [CommandHelp(".gps")] + public static void Gps(IWorldManager manager, string[] args) + { + var character = manager.Account.ActiveCharacter; + manager.Send(character.BuildMessage(character.Location.ToString())); + } + #endregion + + #region Teleport + + [CommandHelp(".go {x} {y} {z} Optional: {mapid}")] + [CommandHelp(".go {name}")] + [CommandHelp(".go instance {name | id}")] + + public static void Go(IWorldManager manager, string[] args) + { + if (args.Length == 0) + return; + + if (Read(args, 0, out float test)) //Co-ordinate port + GoLocation(manager, args); + else if (args[0].ToLower().Trim() == "instance") //Area Trigger + GoTrigger(manager, args); + else + GoNamedArea(manager, true, args); //Worldport + } + + + private static void GoNamedArea(IWorldManager manager, bool worldport, string[] args) + { + int skip = args[0] == "area" || args[0] == "instance" ? 1 : 0; + string needle = string.Join(" ", args.Skip(skip).ToArray()); //Replace "area" and "instance" + + var locations = worldport ? Worldports.FindLocation(needle) : AreaTriggers.FindTrigger(needle); + switch (locations.Count()) + { + case 0: //No matches + manager.Send(manager.Account.ActiveCharacter.BuildMessage("No matching locations found")); + break; + case 1: //Single match + var loc = locations.First().Value; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + break; + default: //Multiple possible matches + manager.Send(manager.Account.ActiveCharacter.BuildMessage("Multiple matches:")); + + foreach (var l in locations) + manager.Send(manager.Account.ActiveCharacter.BuildMessage(" " + l.Key)); + + break; + } + } + + private static void GoLocation(IWorldManager manager, string[] args) + { + if (args.Length < 3 || args.Length > 4) + return; + + var character = manager.Account.ActiveCharacter; + uint map = character.Location.Map; + bool teleport = true; + + if (teleport &= Read(args, 0, out float x)) + character.BuildMessage($"Invalid X parameter."); + + if (teleport &= Read(args, 1, out float y)) + character.BuildMessage($"Invalid Y parameter."); + + if (teleport &= Read(args, 2, out float z)) + character.BuildMessage($"Invalid Z parameter."); + + if (args.Length > 3 && (teleport &= Read(args, 3, out map))) + character.BuildMessage($"Invalid Map parameter."); + + if (teleport) + character.Teleport(x, y, z, character.Location.O, map, ref manager); + } + + private static void GoTrigger(IWorldManager manager, string[] args) + { + if (args.Length < 2) + return; + + var character = manager.Account.ActiveCharacter; + if (uint.TryParse(args[1], out uint areaid)) //Area Id check + { + if (AreaTriggers.Triggers.ContainsKey(areaid)) + { + var area = AreaTriggers.Triggers[areaid]; + character.Teleport(area.X, area.Y, area.Z, area.O, area.Map, ref manager); + } + else + { + manager.Send(character.BuildMessage($"Area Id {areaid} does not exist")); + } + } + else + { + GoNamedArea(manager, false, args); //Area name check + } + } + #endregion + + #region Nudge + [CommandHelp(".nudge Optional: [1 - 100]")] + public static void Nudge(IWorldManager manager, string[] args) + { + var character = manager.Account.ActiveCharacter; + var loc = character.Location; + + Read(args, 0, out float force); + force = Math.Min(Math.Max((int)force, 1), 100); //Min 1 Max 100 + + float X = (float)(loc.X + Math.Cos(loc.O) * force); + float Y = (float)(loc.Y + Math.Sin(loc.O) * force); + character.Teleport(X, Y, loc.Z, loc.O, loc.Map, ref manager); + } + #endregion + + #region Speed + [CommandHelp(".speed [0.1 - 10] Optional: {run | swim | all} ")] + public static void Speed(IWorldManager manager, string[] args) + { + if (args.Length < 1) + return; + + Read(args, 0, out float speed); + speed = Math.Min(Math.Max(speed, 0.1f), 10f); //Min 0.1 Max 10.0 + + string type = (args.Length > 1 ? args[1] : "all").ToLower().Trim(); + + var character = manager.Account.ActiveCharacter; + switch (type) + { + case "swim": + manager.Send(character.BuildForceSpeed(speed, true)); + break; + case "run": + manager.Send(character.BuildForceSpeed(speed)); + break; + default: + manager.Send(character.BuildForceSpeed(speed, true)); + manager.Send(character.BuildForceSpeed(speed)); + break; + } + + manager.Send(character.BuildMessage($"{type.ToUpperFirst()} speed changed to {speed * 100}% of normal")); + } + #endregion + + #region Morph + [CommandHelp(".morph id")] + public static void Morph(IWorldManager manager, string[] args) + { + if (args.Length < 1) + return; + + if(Read(args, 0, out uint Id)) + { + var character = manager.Account.ActiveCharacter; + character.DisplayId = Id; + + manager.Send(character.BuildUpdate()); + } + } + + [CommandHelp(".demorph")] + public static void Demorph(IWorldManager manager, string[] args) + { + var character = manager.Account.ActiveCharacter; + character.Demorph(); + manager.Send(character.BuildUpdate()); + } + #endregion + + public static void Help(IWorldManager manager, string[] args) + { + var character = manager.Account.ActiveCharacter; + var attrs = typeof(Commands).GetMethods() + .Where(x => x.IsDefined(typeof(CommandHelpAttribute), false)) + .SelectMany(x => x.GetCustomAttributes(typeof(CommandHelpAttribute), false) as CommandHelpAttribute[]) + .OrderBy(x => x.HelpText); + + if (attrs.Any()) + { + manager.Send(character.BuildMessage("Commands: ")); + foreach (var attr in attrs) + manager.Send(character.BuildMessage(" " + attr.HelpText)); + } + } + + private static bool Read(string[] args, uint index, out T result) + { + if (index < args.Length) + { + var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)); + if (converter.IsValid(args[index])) + { + result = (T)converter.ConvertFromString(args[index]); + return true; + } + } + + result = default(T); + return false; + } + } +} diff --git a/Common/Common.csproj b/Common/Common.csproj new file mode 100644 index 0000000..f67a60f --- /dev/null +++ b/Common/Common.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Library + Properties + Common + Common + v4.6.1 + 512 + + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Common/Constants/AreaTriggers.cs b/Common/Constants/AreaTriggers.cs new file mode 100644 index 0000000..ddd6862 --- /dev/null +++ b/Common/Constants/AreaTriggers.cs @@ -0,0 +1,131 @@ +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Constants +{ + public static class AreaTriggers + { + public readonly static IDictionary Triggers = new Dictionary() + { + {45, new Location(1688.99f, 1053.48f, 18.6775f, 0.00117f, 189, "Scarlet Monastery - Graveyard (Entrance)")}, + {78, new Location(-16.4f, -383.07f, 61.78f, 1.86f, 36, "DeadMines")}, + {101, new Location(54.23f, 0.28f, -18.34f, 6.26f, 34, "Stormwind Stockades")}, + {104, new Location(-8764.83f, 846.075f, 87.4842f, 3.77934f, 0, "Stormwind Stockades - Outside")}, + {107, new Location(-0.91f, 40.57f, -24.23f, 0f, 35, "Stormwind Vault")}, + {109, new Location(-8653.45f, 606.19f, 91.16f, 0f, 0, "Stormwind Vault Instance")}, + {119, new Location(-11208.3f, 1672.52f, 24.66f, 4.55217f, 0, "Deadmines - Outside Start")}, + {121, new Location(-11339.4f, 1571.16f, 100.56f, 0f, 0, "Deadmines - Outside End")}, + {145, new Location(-229.135f, 2109.18f, 76.8898f, 1.267f, 33, "Shadowfang Keep")}, + {194, new Location(-232.796f, 1568.28f, 76.8909f, 4.398f, 0, "Shadowfang keep - Entrance")}, + {226, new Location(-740.059f, -2214.23f, 16.1374f, 5.68f, 1, "Wailing Caverns - Outside")}, + {228, new Location(-163.49f, 132.9f, -73.66f, 5.83f, 43, "Wailing Caverns")}, + {242, new Location(-4464.92f, -1666.24f, 90f, 0f, 1, "Razorfen Kraul Instance Start")}, + {244, new Location(1943f, 1544.63f, 82f, 1.38f, 47, "Razorfen Kraul")}, + {257, new Location(-151.89f, 106.96f, -39.87f, 4.53f, 48, "Blackfathom Deeps")}, + {259, new Location(4247.74f, 745.879f, -24.5299f, 4.5828f, 1, "Blackfathom Deeps Instance Start")}, + {286, new Location(-226.8f, 49.09f, -46.03f, 1.39f, 70, "Uldaman")}, + {288, new Location(-6066.73f, -2955.63f, 209.776f, 3.20443f, 0, "Uldaman Instance Start")}, + {322, new Location(-5163.33f, 927.623f, 257.188f, 0f, 0, "Gnomeregan Instance Start")}, + {324, new Location(-332.22f, -2.28f, -150.86f, 2.77f, 90, "Gnomeregan")}, + {442, new Location(2592.55f, 1107.5f, 51.29f, 4.74f, 129, "Razorfen Downs")}, + {444, new Location(-4658.12f, -2526.35f, 81.492f, 1.25978f, 1, "Razorfen Downs Instance Start")}, + {446, new Location(-319.24f, 99.9f, -131.85f, 3.19f, 109, "Altar of Atal'Hakkar")}, + {448, new Location(-10175.1f, -3995.15f, -112.9f, 2.95938f, 0, "Altar Of Atal'Hakkar Instance Start")}, + {503, new Location(-8764.83f, 846.075f, 87.4842f, 3.77934f, 0, "Stockades Instance")}, + {523, new Location(-736.51f, 2.71f, -249.99f, 3.14f, 90, "Gnomeregan Train Depot")}, + {525, new Location(-4858.27f, 756.435f, 244.923f, 0f, 0, "Gnomeregan Train Depot Instance")}, + {527, new Location(8786.36f, 967.445f, 30.197f, 3.39632f, 1, "Teddrassil - Ruth Theran")}, + {542, new Location(9945.13f, 2616.89f, 1316.46f, 4.61446f, 1, "Teddrassil - Darnassus")}, + {602, new Location(2913.92f, -802.404f, 160.333f, 3.50405f, 0, "Scarlet Monastery - Graveyard (Exit)")}, + {604, new Location(2906.14f, -813.772f, 160.333f, 1.95739f, 0, "Scarlet Monastery - Cathedral (Exit)")}, + {606, new Location(2884.45f, -822.01f, 160.333f, 1.95268f, 0, "Scarlet Monastery - Armory (Exit)")}, + {608, new Location(2870.9f, -820.164f, 160.333f, 0.387856f, 0, "Scarlet Monastery - Library (Exit)")}, + {610, new Location(855.683f, 1321.5f, 18.6709f, 0.001747f, 189, "Scarlet Monastery - Cathedral")}, + {612, new Location(1610.83f, -323.433f, 18.6738f, 6.28022f, 189, "Scarlet Monastery - Armory")}, + {614, new Location(255.346f, -209.09f, 18.6773f, 6.26656f, 189, "Scarlet Monastery - Library")}, + {702, new Location(-9015.97f, 875.318f, 148.617f, 0f, 0, "Stormwind - Wizard Sanctum Room")}, + {704, new Location(-9019.16f, 887.596f, 29.6206f, 0f, 0, "Stormwind - Wizard Sanctum Tower Portal")}, + {882, new Location(-6620.48f, -3765.19f, 266.226f, 3.13531f, 0, "Uldaman Instance End")}, + {902, new Location(-214.02f, 383.607f, -38.7687f, 0.5f, 70, "Uldaman Exit")}, + {922, new Location(-6796.49f, -2890.77f, 8.88063f, 3.30496f, 1, "Zul'Farrak Instance Start")}, + {924, new Location(1213.52f, 841.59f, 8.93f, 6.09f, 209, "Zul'Farrak Entrance")}, + {943, new Location(-5187.47f, -2804.32f, -8.375f, 5.76f, 1, "Leap of Faith - End of fall")}, + {1064, new Location(-4747.17f, -3753.27f, 49.8122f, 0.713271f, 1, "Onyxia's Lair - Dustwallow Instance")}, + {1466, new Location(458.32f, 26.52f, -70.67f, 4.95f, 230, "Blackrock Mountain - Searing Gorge Instance?")}, + {1468, new Location(78.5083f, -225.044f, 49.839f, 5.1f, 229, "Blackrock Spire - Searing Gorge Instance (Inside)")}, + {1470, new Location(-7524.19f, -1230.13f, 285.743f, 2.09544f, 0, "Blackrock Spire - Searing Gorge Instance")}, + {1472, new Location(-7179.63f, -923.667f, 166.416f, 1.84097f, 0, "Blackrock Dephts - Searing Gorge Instance")}, + {2068, new Location(-7524.19f, -1230.13f, 285.743f, 2.09544f, 0, "Blackrock Spire - Fall out")}, + {2166, new Location(-4838.95f, -1318.46f, 501.868f, 1.42372f, 0, "Deeprun Tram - Ironforge Instance (Inside)")}, + {2171, new Location(-8364.57f, 535.981f, 91.7969f, 2.24619f, 0, "Deeprun Tram - Stormwind Instance (Inside)")}, + {2173, new Location(68.3006f, 2490.91f, -4.29647f, 3.12192f, 369, "Deeprun Tram - Stormwind Entrance")}, + {2175, new Location(69.2542f, 10.257f, -4.29664f, 3.09832f, 369, "Deeprun Tram - Ironforge Entrance")}, + {2214, new Location(3593.15f, -3646.56f, 138.5f, 5.33f, 329, "Stratholme - Eastern Plaguelands Instance")}, + {2216, new Location(3395.09f, -3380.25f, 142.702f, 0.1f, 329, "Stratholme - Eastern Plaguelands Instance")}, + {2217, new Location(3395.09f, -3380.25f, 142.702f, 0.1f, 329, "Stratholme - Eastern Plaguelands Instance")}, + {2221, new Location(3235.46f, -4050.6f, 108.45f, 1.93522f, 0, "Stratholme - Eastern Plaguelands Instance (Inside)")}, + {2226, new Location(1813.49f, -4418.58f, -18.57f, 1.78f, 1, "Ragefire Chasm - Ogrimmar Instance (Inside)")}, + {2230, new Location(3.81f, -14.82f, -17.84f, 4.39f, 389, "Ragefire Chasm - Ogrimmar Instance")}, + {2527, new Location(221.322f, 74.4933f, 25.7195f, 0.484836f, 450, "Hall of Legends - Ogrimmar")}, + {2530, new Location(1637.32f, -4242.7f, 56.1827f, 4.1927f, 1, "Hall of Legends - Ogrimmar (Inside)")}, + {2532, new Location(-0.299116f, 4.39156f, -0.255884f, 1.54805f, 449, "Stormwind - Champions Hall")}, + {2534, new Location(-8762.45f, 403.062f, 103.902f, 5.34463f, 0, "Stormwind (Inside) - Champions Hall")}, + {2567, new Location(196.37f, 127.05f, 134.91f, 6.09f, 289, "Scholomance")}, + {2568, new Location(1275.05f, -2552.03f, 90.3994f, 3.6631f, 0, "Scholomance Instance")}, + {2606, new Location(534.868f, -1087.68f, 106.119f, 3.35758f, 0, "Alterac Valley - Horde Exit")}, + {2608, new Location(98.432f, -182.274f, 127.52f, 5.02654f, 0, "Alterac Valley - Alliance Exit")}, + {2848, new Location(29.1607f, -71.3372f, -8.18032f, 4.58f, 249, "Onyxia's Lair")}, + {2886, new Location(1096f, -467f, -104.6f, 3.64f, 409, "The Molten Bridge")}, + {2890, new Location(1115.35f, -457.35f, -102.7f, 0.5f, 230, "Molten Core Entrance")}, + {3126, new Location(-1186.98f, 2875.95f, 85.7258f, 1.78443f, 1, "Maraudon")}, + {3131, new Location(-1471.07f, 2618.57f, 76.1944f, 0f, 1, "Maraudon")}, + {3133, new Location(1019.69f, -458.31f, -43.43f, 0.31f, 349, "Maraudon")}, + {3134, new Location(752.91f, -616.53f, -33.11f, 1.37f, 349, "Maraudon")}, + {3183, new Location(44.4499f, -154.822f, -2.71201f, 0f, 429, "Dire Maul 1")}, + {3184, new Location(-201.11f, -328.66f, -2.72f, 5.22f, 429, "Dire Maul 2")}, + {3185, new Location(9.31119f, -837.085f, -32.5305f, 0f, 429, "Dire Maul 3")}, + {3186, new Location(-62.9658f, 159.867f, -3.46206f, 3.14788f, 429, "Dire Maul 4")}, + {3187, new Location(31.5609f, 159.45f, -3.4777f, 0.01f, 429, "Dire Maul 5")}, + {3189, new Location(255.249f, -16.0561f, -2.58737f, 4.7f, 429, "Dire Maul 6")}, + {3190, new Location(-3831.79f, 1250.23f, 160.223f, 0f, 1, "Dire Maul")}, + {3191, new Location(-3747.96f, 1249.18f, 160.217f, 3.15827f, 1, "Dire Maul")}, + {3193, new Location(-3520.65f, 1077.72f, 161.138f, 1.5009f, 1, "Dire Maul")}, + {3194, new Location(-3737.48f, 934.975f, 160.973f, 3.13864f, 1, "Dire Maul")}, + {3195, new Location(-3980.58f, 776.193f, 161.006f, 0f, 1, "Dire Maul")}, + {3196, new Location(-4030.21f, 127.966f, 26.8109f, 0f, 1, "Dire Maul")}, + {3197, new Location(-3577.67f, 841.859f, 134.594f, 0f, 1, "Dire Maul")}, + {3528, new Location(1096f, -467f, -104.6f, 3.64f, 409, "The Molten Core Window")}, + {3529, new Location(1096f, -467f, -104.6f, 3.64f, 409, "The Molten Core Window (Lava)")}, + {3726, new Location(-7666.23f, -1102.79f, 399.68f, 0.601256f, 469, "Blackwing Lair")}, + {3728, new Location(-7524.19f, -1230.13f, 285.743f, 2.09544f, 0, "Blackrock Spire Unknown")}, + {3928, new Location(-11916.1f, -1230.53f, 92.5334f, 4.71867f, 309, "Zul'Gurub")}, + {3930, new Location(-11916.3f, -1208.37f, 92.2868f, 1.61792f, 0, "Zul'Gurub Exit")}, + {3948, new Location(-1198f, -2533f, 22f, 0f, 0, "Arathi Basin Alliance Out")}, + {3949, new Location(-817f, -3509f, 73f, 0f, 0, "Arathi Basin Horde Out")}, + {4006, new Location(-8418.5f, 1505.94f, 31.8232f, 0f, 1, "Ruins Of Ahn'Qiraj (Inside)")}, + {4008, new Location(-8429.74f, 1512.14f, 31.9074f, 2.58f, 509, "Ruins Of Ahn'Qiraj")}, + {4010, new Location(-8231.33f, 2010.6f, 129.861f, 0f, 531, "Ahn'Qiraj Temple")}, + {4012, new Location(-8242.67f, 1992.06f, 129.072f, 0f, 1, "Ahn'Qiraj Temple (Inside)")}, + {4055, new Location(3005.87f, -3435.01f, 293.882f, 0f, 533, "Naxxramas")}, + {4156, new Location(3498.28f, -5349.9f, 144.968f, 1.31324f, 533, "Naxxramas (Entrance)")}, + }; + + public static IEnumerable> FindTrigger(string needle) + { + Func FormatString = (s) => s.Replace(" ", "").Replace("'", "").ToLower().Trim(); + + needle = FormatString(needle); + + var exact = Triggers.FirstOrDefault(x => FormatString(x.Value.Description) == needle && x.Value.Map > 1); + if (exact.Key != 0) + return new[] { new KeyValuePair($"{exact.Value.Description} : {exact.Key}", exact.Value) }; + + return Triggers.Where(x => FormatString(x.Value.Description).Contains(needle) && x.Value.Map > 1) + .Select(x => new KeyValuePair($"{x.Value.Description} : {x.Key}", x.Value)); + } + } +} diff --git a/Common/Constants/Emotes.cs b/Common/Constants/Emotes.cs new file mode 100644 index 0000000..04c352c --- /dev/null +++ b/Common/Constants/Emotes.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Constants +{ + public enum TextEmotes + { + EMOTE_AGREE = 1, + EMOTE_AMAZE = 2, + EMOTE_ANGRY = 3, + EMOTE_APOLOGIZE = 4, + EMOTE_APPLAUD = 5, + EMOTE_BARK = 205, + EMOTE_BASHFUL = 6, + EMOTE_BECKON = 7, + EMOTE_BEG = 8, + EMOTE_BITE = 9, + EMOTE_BLEED = 10, + EMOTE_BLINK = 11, + EMOTE_BLUSH = 12, + EMOTE_BOGGLE = 107, + EMOTE_BONK = 13, + EMOTE_BORED = 14, + EMOTE_BOUNCE = 15, + EMOTE_BOW = 17, + EMOTE_BRB = 16, + EMOTE_BURP = 18, + EMOTE_BYE = 19, + EMOTE_CACKLE = 20, + EMOTE_CALM = 108, + EMOTE_CHEER = 21, + EMOTE_CHICKEN = 22, + EMOTE_CHUCKLE = 23, + EMOTE_CLAP = 24, + EMOTE_COLD = 109, + EMOTE_COMFORT = 110, + EMOTE_COMMEND = 243, + EMOTE_CONFUSED = 25, + EMOTE_CONGRATULATE = 26, + EMOTE_COUGH = 27, + EMOTE_COWER = 28, + EMOTE_CRACK = 29, + EMOTE_CRINGE = 30, + EMOTE_CRY = 31, + EMOTE_CUDDLE = 111, + EMOTE_CURIOUS = 32, + EMOTE_CURTSEY = 33, + EMOTE_DANCE = 34, + EMOTE_DRINK = 35, + EMOTE_DROOL = 36, + EMOTE_DUCK = 112, + EMOTE_EAT = 37, + EMOTE_EYE = 38, + EMOTE_FART = 39, + EMOTE_FIDGET = 40, + EMOTE_FLEX = 41, + EMOTE_FLOP = 224, + EMOTE_FROWN = 42, + EMOTE_GASP = 43, + EMOTE_GAZE = 44, + EMOTE_GIGGLE = 45, + EMOTE_GLARE = 46, + EMOTE_GLOAT = 47, + EMOTE_GREET = 48, + EMOTE_GRIN = 49, + EMOTE_GROAN = 50, + EMOTE_GROVEL = 51, + EMOTE_GROWL = 204, + EMOTE_GUFFAW = 52, + EMOTE_HAIL = 53, + EMOTE_HAPPY = 54, + EMOTE_HELLO = 55, + EMOTE_HUG = 56, + EMOTE_HUNGRY = 57, + EMOTE_INSULT = 113, + EMOTE_INTRODUCE = 114, + EMOTE_JK = 115, + EMOTE_KISS = 58, + EMOTE_KNEEL = 59, + EMOTE_LAUGH = 60, + EMOTE_LAYDOWN = 61, + EMOTE_LICK = 116, + EMOTE_LISTEN = 117, + EMOTE_LOST = 118, + EMOTE_LOVE = 225, + EMOTE_MASSAGE = 62, + EMOTE_MOAN = 63, + EMOTE_MOCK = 119, + EMOTE_MOO = 226, + EMOTE_MOON = 64, + EMOTE_MOURN = 65, + EMOTE_NO = 66, + EMOTE_NOD = 67, + EMOTE_NOSEPICK = 68, + EMOTE_PANIC = 69, + EMOTE_PEER = 70, + EMOTE_PITY = 203, + EMOTE_PLEAD = 71, + EMOTE_POINT = 72, + EMOTE_POKE = 73, + EMOTE_PONDER = 120, + EMOTE_POUNCE = 121, + EMOTE_PRAISE = 122, + EMOTE_PRAY = 74, + EMOTE_PURR = 123, + EMOTE_PUZZLE = 124, + EMOTE_RAISE = 125, + EMOTE_RASP = 183, + EMOTE_READY = 126, + EMOTE_ROAR = 75, + EMOTE_ROFL = 76, + EMOTE_RUDE = 77, + EMOTE_SALUTE = 78, + EMOTE_SCARED = 223, + EMOTE_SCRATCH = 79, + EMOTE_SEXY = 80, + EMOTE_SHAKE = 81, + EMOTE_SHIMMY = 127, + EMOTE_SHIVER = 128, + EMOTE_SHOO = 129, + EMOTE_SHOUT = 82, + EMOTE_SHRUG = 83, + EMOTE_SHY = 84, + EMOTE_SIGH = 85, + EMOTE_SIT = 86, + EMOTE_SLAP = 130, + EMOTE_SLEEP = 87, + EMOTE_SMILE = 163, + EMOTE_SMIRK = 131, + EMOTE_SNARL = 88, + EMOTE_SNICKER = 140, + EMOTE_SNIFF = 132, + EMOTE_SNUB = 133, + EMOTE_SOOTHE = 134, + EMOTE_SPIT = 89, + EMOTE_STAND = 141, + EMOTE_STARE = 90, + EMOTE_STINK = 135, + EMOTE_SURPRISED = 91, + EMOTE_SURRENDER = 92, + EMOTE_TALK = 93, + EMOTE_TALKEX = 94, + EMOTE_TALKQ = 95, + EMOTE_TAP = 96, + EMOTE_TAUNT = 136, + EMOTE_TEASE = 137, + EMOTE_THANK = 97, + EMOTE_THIRSTY = 138, + EMOTE_THREATEN = 98, + EMOTE_TICKLE = 142, + EMOTE_TIRED = 99, + EMOTE_TRAIN = 264, + EMOTE_VETO = 139, + EMOTE_VICTORY = 100, + EMOTE_VIOLIN = 143, + EMOTE_WAVE = 101, + EMOTE_WELCOME = 102, + EMOTE_WHINE = 103, + EMOTE_WHISTLE = 104, + EMOTE_WORK = 105, + EMOTE_YAWN = 106, + } + + public static class Emotes + { + private static readonly Dictionary emoteLookup = new Dictionary() + { + { TextEmotes.EMOTE_AGREE, 0 }, + { TextEmotes.EMOTE_AMAZE, 0 }, + { TextEmotes.EMOTE_ANGRY, 14 }, + { TextEmotes.EMOTE_APOLOGIZE, 0 }, + { TextEmotes.EMOTE_APPLAUD, 21 }, + { TextEmotes.EMOTE_BARK, 0 }, + { TextEmotes.EMOTE_BASHFUL, 24 }, + { TextEmotes.EMOTE_BECKON, 0 }, + { TextEmotes.EMOTE_BEG, 20 }, + { TextEmotes.EMOTE_BITE, 0 }, + { TextEmotes.EMOTE_BLEED, 0 }, + { TextEmotes.EMOTE_BLINK, 0 }, + { TextEmotes.EMOTE_BLUSH, 24 }, + { TextEmotes.EMOTE_BOGGLE, 6 }, + { TextEmotes.EMOTE_BONK, 0 }, + { TextEmotes.EMOTE_BORED, 0 }, + { TextEmotes.EMOTE_BOUNCE, 0 }, + { TextEmotes.EMOTE_BOW, 2 }, + { TextEmotes.EMOTE_BRB, 0 }, + { TextEmotes.EMOTE_BURP, 0 }, + { TextEmotes.EMOTE_BYE, 3 }, + { TextEmotes.EMOTE_CACKLE, 11 }, + { TextEmotes.EMOTE_CALM, 0 }, + { TextEmotes.EMOTE_CHEER, 4 }, + { TextEmotes.EMOTE_CHICKEN, 19 }, + { TextEmotes.EMOTE_CHUCKLE, 11 }, + { TextEmotes.EMOTE_CLAP, 21 }, + { TextEmotes.EMOTE_COLD, 0 }, + { TextEmotes.EMOTE_COMFORT, 0 }, + { TextEmotes.EMOTE_COMMEND, 21 }, + { TextEmotes.EMOTE_CONFUSED, 6 }, + { TextEmotes.EMOTE_CONGRATULATE, 21 }, + { TextEmotes.EMOTE_COUGH, 0 }, + { TextEmotes.EMOTE_COWER, 0 }, + { TextEmotes.EMOTE_CRACK, 0 }, + { TextEmotes.EMOTE_CRINGE, 0 }, + { TextEmotes.EMOTE_CRY, 18 }, + { TextEmotes.EMOTE_CUDDLE, 0 }, + { TextEmotes.EMOTE_CURIOUS, 6 }, + { TextEmotes.EMOTE_CURTSEY, 2 }, + { TextEmotes.EMOTE_DANCE, 10 }, + { TextEmotes.EMOTE_DRINK, 7 }, + { TextEmotes.EMOTE_DROOL, 0 }, + { TextEmotes.EMOTE_DUCK, 0 }, + { TextEmotes.EMOTE_EAT, 7 }, + { TextEmotes.EMOTE_EYE, 0 }, + { TextEmotes.EMOTE_FART, 0 }, + { TextEmotes.EMOTE_FIDGET, 0 }, + { TextEmotes.EMOTE_FLEX, 23 }, + { TextEmotes.EMOTE_FLOP, 0 }, + { TextEmotes.EMOTE_FROWN, 0 }, + { TextEmotes.EMOTE_GASP, 5 }, + { TextEmotes.EMOTE_GAZE, 0 }, + { TextEmotes.EMOTE_GIGGLE, 11 }, + { TextEmotes.EMOTE_GLARE, 0 }, + { TextEmotes.EMOTE_GLOAT, 11 }, + { TextEmotes.EMOTE_GREET, 3 }, + { TextEmotes.EMOTE_GRIN, 0 }, + { TextEmotes.EMOTE_GROAN, 0 }, + { TextEmotes.EMOTE_GROVEL, 20 }, + { TextEmotes.EMOTE_GROWL, 15 }, + { TextEmotes.EMOTE_GUFFAW, 11 }, + { TextEmotes.EMOTE_HAIL, 3 }, + { TextEmotes.EMOTE_HAPPY, 0 }, + { TextEmotes.EMOTE_HELLO, 3 }, + { TextEmotes.EMOTE_HUG, 0 }, + { TextEmotes.EMOTE_HUNGRY, 0 }, + { TextEmotes.EMOTE_INSULT, 14 }, + { TextEmotes.EMOTE_INTRODUCE, 0 }, + { TextEmotes.EMOTE_JK, 0 }, + { TextEmotes.EMOTE_KISS, 17 }, + { TextEmotes.EMOTE_KNEEL, 68 }, + { TextEmotes.EMOTE_LAUGH, 11 }, + { TextEmotes.EMOTE_LAYDOWN, 12 }, + { TextEmotes.EMOTE_LICK, 0 }, + { TextEmotes.EMOTE_LISTEN, 0 }, + { TextEmotes.EMOTE_LOST, 6 }, + { TextEmotes.EMOTE_LOVE, 0 }, + { TextEmotes.EMOTE_MASSAGE, 0 }, + { TextEmotes.EMOTE_MOAN, 0 }, + { TextEmotes.EMOTE_MOCK, 0 }, + { TextEmotes.EMOTE_MOO, 0 }, + { TextEmotes.EMOTE_MOON, 0 }, + { TextEmotes.EMOTE_MOURN, 18 }, + { TextEmotes.EMOTE_NO, 274 }, + { TextEmotes.EMOTE_NOD, 273 }, + { TextEmotes.EMOTE_NOSEPICK, 0 }, + { TextEmotes.EMOTE_PANIC, 0 }, + { TextEmotes.EMOTE_PEER, 0 }, + { TextEmotes.EMOTE_PITY, 0 }, + { TextEmotes.EMOTE_PLEAD, 20 }, + { TextEmotes.EMOTE_POINT, 25 }, + { TextEmotes.EMOTE_POKE, 0 }, + { TextEmotes.EMOTE_PONDER, 6 }, + { TextEmotes.EMOTE_POUNCE, 0 }, + { TextEmotes.EMOTE_PRAISE, 0 }, + { TextEmotes.EMOTE_PRAY, 16 }, + { TextEmotes.EMOTE_PURR, 0 }, + { TextEmotes.EMOTE_PUZZLE, 6 }, + { TextEmotes.EMOTE_RAISE, 0 }, + { TextEmotes.EMOTE_RASP, 14 }, + { TextEmotes.EMOTE_READY, 0 }, + { TextEmotes.EMOTE_ROAR, 15 }, + { TextEmotes.EMOTE_ROFL, 11 }, + { TextEmotes.EMOTE_RUDE, 14 }, + { TextEmotes.EMOTE_SALUTE, 66 }, + { TextEmotes.EMOTE_SCARED, 0 }, + { TextEmotes.EMOTE_SCRATCH, 0 }, + { TextEmotes.EMOTE_SEXY, 0 }, + { TextEmotes.EMOTE_SHAKE, 0 }, + { TextEmotes.EMOTE_SHIMMY, 0 }, + { TextEmotes.EMOTE_SHIVER, 0 }, + { TextEmotes.EMOTE_SHOO, 0 }, + { TextEmotes.EMOTE_SHOUT, 22 }, + { TextEmotes.EMOTE_SHRUG, 6 }, + { TextEmotes.EMOTE_SHY, 24 }, + { TextEmotes.EMOTE_SIGH, 0 }, + { TextEmotes.EMOTE_SIT, 13 }, + { TextEmotes.EMOTE_SLAP, 0 }, + { TextEmotes.EMOTE_SLEEP, 12 }, + { TextEmotes.EMOTE_SMILE, 0 }, + { TextEmotes.EMOTE_SMIRK, 0 }, + { TextEmotes.EMOTE_SNARL, 0 }, + { TextEmotes.EMOTE_SNICKER, 0 }, + { TextEmotes.EMOTE_SNIFF, 0 }, + { TextEmotes.EMOTE_SNUB, 0 }, + { TextEmotes.EMOTE_SOOTHE, 0 }, + { TextEmotes.EMOTE_SPIT, 0 }, + { TextEmotes.EMOTE_STAND, 26 }, + { TextEmotes.EMOTE_STARE, 0 }, + { TextEmotes.EMOTE_STINK, 0 }, + { TextEmotes.EMOTE_SURPRISED, 0 }, + { TextEmotes.EMOTE_SURRENDER, 20 }, + { TextEmotes.EMOTE_TALK, 1 }, + { TextEmotes.EMOTE_TALKEX, 5 }, + { TextEmotes.EMOTE_TALKQ, 6 }, + { TextEmotes.EMOTE_TAP, 0 }, + { TextEmotes.EMOTE_TAUNT, 19 }, + { TextEmotes.EMOTE_TEASE, 0 }, + { TextEmotes.EMOTE_THANK, 0 }, + { TextEmotes.EMOTE_THIRSTY, 0 }, + { TextEmotes.EMOTE_THREATEN, 0 }, + { TextEmotes.EMOTE_TICKLE, 0 }, + { TextEmotes.EMOTE_TIRED, 0 }, + { TextEmotes.EMOTE_TRAIN, 275 }, + { TextEmotes.EMOTE_VETO, 0 }, + { TextEmotes.EMOTE_VICTORY, 4 }, + { TextEmotes.EMOTE_VIOLIN, 18 }, + { TextEmotes.EMOTE_WAVE, 3 }, + { TextEmotes.EMOTE_WELCOME, 3 }, + { TextEmotes.EMOTE_WHINE, 0 }, + { TextEmotes.EMOTE_WHISTLE, 0 }, + { TextEmotes.EMOTE_WORK, 0 }, + { TextEmotes.EMOTE_YAWN, 0 }, + }; + + public static uint Get(TextEmotes emote) => emoteLookup.ContainsKey(emote) ? emoteLookup[emote] : 0; + } + + +} diff --git a/Common/Constants/Enums.cs b/Common/Constants/Enums.cs new file mode 100644 index 0000000..c2c06e1 --- /dev/null +++ b/Common/Constants/Enums.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Common.Constants +{ + public enum Classes : byte + { + WARRIOR = 1, + PALADIN = 2, + HUNTER = 3, + ROGUE = 4, + PRIEST = 5, + SHAMAN = 7, + MAGE = 8, + WARLOCK = 9, + DRUID = 11 + } + + public enum PowerTypes : uint + { + MANA = 0, + RAGE = 1, + FOCUS = 2, + ENERGY = 3, + HAPPINESS = 4, + POWER_HEALTH = 0xFFFFFFFE + } + + public enum StandState : uint + { + STANDING = 0x0, + SITTING = 0x1, + SITTINGCHAIR = 0x2, + SLEEPING = 0x3, + SITTINGCHAIRLOW = 0x4, + FIRSTCHAIRSIT = 0x4, + SITTINGCHAIRMEDIUM = 0x5, + SITTINGCHAIRHIGH = 0x6, + LASTCHAIRSIT = 0x6, + DEAD = 0x7, + KNEEL = 0x8, + }; + + public enum RealmlistOpcodes : byte + { + LOGON_CHALLENGE = 0, + LOGON_PROOF = 1, + RECONNECT_CHALLENGE = 2, + RECONNECT_PROOF = 3, + REALMLIST_REQUEST = 16, + } +} diff --git a/Common/Constants/Extensions.cs b/Common/Constants/Extensions.cs new file mode 100644 index 0000000..e591b4a --- /dev/null +++ b/Common/Constants/Extensions.cs @@ -0,0 +1,33 @@ +using Common.Interfaces; +using Common.Logging; +using System; +using System.Net.Sockets; + +namespace Common.Constants +{ + public static class Extensions + { + public static void SendData(this Socket socket, IPacketWriter packet, string packetname = "") + { + byte[] buffer = packet.ReadDataToSend(); + try + { + socket.Send(buffer, 0, buffer.Length, SocketFlags.None); + if(!string.IsNullOrEmpty(packetname)) + Log.Message(LogType.DUMP, "SENT {0}.", packetname); + } + catch (Exception e) + { + Log.Message(LogType.ERROR, "{0}", e.Message); + } + } + + public static string ToUpperFirst(this string s) + { + if (string.IsNullOrEmpty(s)) + return string.Empty; + + return char.ToUpper(s[0]) + s.ToLower().Substring(1); + } + } +} diff --git a/Common/Constants/Opcodes.cs b/Common/Constants/Opcodes.cs new file mode 100644 index 0000000..a84dca9 --- /dev/null +++ b/Common/Constants/Opcodes.cs @@ -0,0 +1,86 @@ +public enum Opcodes : ushort +{ + SMSG_AUTH_CHALLENGE, + SMSG_AUTH_RESPONSE, + CMSG_AUTH_SESSION, + CMSG_CHAR_CREATE, + SMSG_CHAR_CREATE, + CMSG_CHAR_DELETE, + SMSG_CHAR_DELETE, + CMSG_CHAR_ENUM, + SMSG_CHAR_ENUM, + CMSG_PING, + SMSG_PONG, + CMSG_PLAYER_LOGIN, + SMSG_UPDATE_OBJECT, + CMSG_QUERY_TIME, + SMSG_LOGIN_SETTIMESPEED, + + //NameCache, + CMSG_NAME_QUERY, + SMSG_NAME_QUERY_RESPONSE, + + //Logout, + CMSG_LOGOUT_REQUEST, + SMSG_LOGOUT_COMPLETE, + + //Teleport, + CMSG_WORLD_TELEPORT, + SMSG_NEW_WORLD, + SMSG_TRANSFER_PENDING, + + //Chat + CMSG_MESSAGECHAT, + SMSG_MESSAGECHAT, + CMSG_TEXT_EMOTE, + SMSG_TEXT_EMOTE, + SMSG_EMOTE, + + //Movement, + MSG_MOVE_START_FORWARD, + MSG_MOVE_START_BACKWARD, + MSG_MOVE_STOP, + MSG_MOVE_START_STRAFE_LEFT, + MSG_MOVE_START_STRAFE_RIGHT, + MSG_MOVE_STOP_STRAFE, + MSG_MOVE_JUMP, + MSG_MOVE_START_TURN_LEFT, + MSG_MOVE_START_TURN_RIGHT, + MSG_MOVE_STOP_TURN, + MSG_MOVE_START_PITCH_UP, + MSG_MOVE_START_PITCH_DOWN, + MSG_MOVE_STOP_PITCH, + MSG_MOVE_SET_RUN_MODE, + MSG_MOVE_SET_WALK_MODE, + MOVE_COLLIDE_REDIRECT, + MOVE_COLLIDE_STUCK, + MSG_MOVE_START_SWIM, + MSG_MOVE_STOP_SWIM, + MSG_MOVE_SET_FACING, + MSG_MOVE_SET_PITCH, + MSG_MOVE_ROOT, + MSG_MOVE_UNROOT, + MSG_MOVE_HEARTBEAT, + + //Force speed change + SMSG_FORCE_SPEED_CHANGE, + SMSG_FORCE_SWIM_SPEED_CHANGE, + + //Worldporting + SMSG_MOVE_WORLDPORT_ACK, + MSG_MOVE_WORLDPORT_ACK, + MSG_MOVE_TELEPORT_ACK, + CMSG_AREATRIGGER, + CMSG_ZONEUPDATE, + + //Standstate + CMSG_STANDSTATECHANGE, + + //Misc + SMSG_LOGIN_VERIFY_WORLD, + SMSG_TUTORIAL_FLAGS, + SMSG_ACCOUNT_DATA_MD5, + SMSG_UI_CONFIG_MD5, + CMSG_UPDATE_ACCOUNT_DATA, + SMSG_UPDATE_ACCOUNT_DATA, +} diff --git a/Common/Constants/Worldports.cs b/Common/Constants/Worldports.cs new file mode 100644 index 0000000..92139fa --- /dev/null +++ b/Common/Constants/Worldports.cs @@ -0,0 +1,251 @@ +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Common.Constants +{ + public static class Worldports + { + public static readonly IDictionary Locations = new Dictionary() + { + { "Aerie Peak", new Location(260.366f,-2125.21f,119.565f,3.18494f,0) }, + { "Algaz Station", new Location(-4817.04f,-2656.26f,327.12f,4.68835f,0) }, + { "Alterac Mountains", new Location(370.763f,-491.355f,175.361f,5.37858f,0) }, + { "Alterac Valley", new Location(-215.738f,-309.394f,6.66761f,3.07325f,30) }, + { "Alterac Valley Alliance", new Location(883.187f,-489.375f,96.7618f,3.06932f,30) }, + { "Alterac Valley Horde", new Location(-818.155f,-623.043f,54.0884f,2.15276f,30) }, + { "Ambermill", new Location(-129.094f,835.621f,63.598f,4.83351f,0) }, + { "Angor Fortress", new Location(-6398.46f,-3166.67f,299.812f,0.769213f,0) }, + { "Arathi Basin", new Location(1017.16f,1040.59f,-44.9017f,0.325149f,529) }, + { "Arathi Basin Alliance", new Location(1308.68f,1306.03f,-9.0107f,3.91285f,529) }, + { "Arathi Basin Horde", new Location(686.053f,683.165f,-12.9149f,0.816022f,529) }, + { "Arathi Highlands", new Location(-1508.51f,-2732.06f,32.4986f,3.35708f,0) }, + { "Ashenvale", new Location(1928.34f,-2165.95f,93.7896f,0.205731f,1) }, + { "Astranaar", new Location(2676.19f,-422.905f,107.123f,0.648691f,1) }, + { "Auberdine", new Location(6501.4f,481.607f,6.27062f,1.70033f,1) }, + { "Azshara", new Location(3341.36f,-4603.79f,92.5027f,5.28142f,1) }, + { "Azshara Crater", new Location(128.205f,135.136f,236.11f,4.59132f,37) }, + { "Badlands", new Location(-6779.2f,-3423.64f,241.667f,0.647481f,0) }, + { "Barrens", new Location(48.9976f,-2715.55f,91.6677f,0.158612f,1) }, + { "Beggar's Haunt", new Location(-10361.1f,-1529.87f,91.4594f,5.96075f,0) }, + { "Blackfathom Deeps", new Location(4249.99f,740.102f,-25.671f,1.34062f,1) }, + { "Blackrock Depths", new Location(-7179.34f,-921.212f,165.821f,5.09599f,0) }, + { "Blackrock Mountain", new Location(-7494.94f,-1123.49f,265.547f,3.3092f,0) }, + { "Blackrock Spire", new Location(-7527.05f,-1226.77f,285.732f,5.29626f,0) }, + { "Blackrock Stronghold", new Location(-7733.43f,-1510.24f,132.792f,1.01584f,0) }, + { "Blackwing Lair", new Location(164.789f,-475.305f,116.842f,0.022714f,229) }, + { "Blasted Lands", new Location(-11182.5f,-3016.67f,7.42235f,4.0004f,0) }, + { "Bloodhoof Village", new Location(-2240.91f,-399.174f,-9.42446f,2.53353f,1) }, + { "Bloodvenom Post", new Location(5128.91f,-343.506f,355.035f,3.39176f,1) }, + { "Booty Bay", new Location(-14297.2f,530.993f,8.77916f,3.98863f,0) }, + { "Brackenwall Village", new Location(-3130.67f,-2908.43f,34.0976f,1.42798f,1) }, + { "Brewnall Village", new Location(-5385.04f,310.278f,394.151f,5.19649f,0) }, + { "Brill", new Location(2259.25f,290.43f,34.1137f,0.987414f,0) }, + { "Burning Steppes", new Location(-8118.54f,-1633.83f,132.996f,0.089067f,0) }, + { "Caer Darrow", new Location(1248.8f,-2604.13f,90.961f,0.255412f,0) }, + { "Camp Aparaje", new Location(-44.6129f,-505.122f,-46.1274f,1.84172f,1) }, + { "Camp Mojache", new Location(-4396.7f,224.841f,25.4136f,4.93684f,1) }, + { "Camp Narache", new Location(-2919.35f,-264.535f,53.6197f,0.409027f,1) }, + { "Camp Taurajo", new Location(-2363.11f,-1913.78f,95.7829f,0.165556f,1) }, + { "Caverns Of Time", new Location(-8204.88f,-4495.25f,9.0091f,4.72574f,1) }, + { "Cenarion Hold", new Location(-6818.09f,733.814f,41.5661f,2.3082f,1) }, + { "Coldridge Valley", new Location(-6231.77f,332.993f,383.171f,0.480178f,0) }, + { "Corin's Crossing", new Location(2012.28f,-4470.7f,73.6229f,5.15472f,0) }, + { "Crypt", new Location(-11052.9f,-1568.93f,27.233f,4.64509f,0) }, + { "Dalaran", new Location(335.479f,204.771f,42.1124f,3.42294f,0) }, + { "Darkshire", new Location(-10573f,-1182.51f,28.0148f,0.309022f,0) }, + { "Darkshore", new Location(5756.25f,298.505f,20.6049f,5.96504f,1) }, + { "Darnassus", new Location(9949.56f,2284.21f,1341.4f,1.59587f,1) }, + { "Deadwind Pass", new Location(-10438.8f,-1932.75f,104.617f,4.77402f,0) }, + { "Deathknell", new Location(1843.5f,1590f,93.2971f,3.08757f,0) }, + { "Desolace", new Location(-606.395f,2211.75f,92.9818f,0.809746f,1) }, + { "Dire Maul East", new Location(-3980.8f,789.005f,161.007f,4.71945f,1) }, + { "Dire Maul North", new Location(-3521.29f,1085.2f,161.097f,4.7281f,1) }, + { "Dire Maul West", new Location(-3828.01f,1250.22f,160.226f,3.20835f,1) }, + { "Dolanaar", new Location(9848.37f,966.953f,1306.38f,3.77457f,1) }, + { "Dreadmaul Hold", new Location(-10895f,-2933.24f,12.8408f,6.26628f,0) }, + { "Dreadmaul Rock", new Location(-7734.77f,-2609.01f,165.137f,4.22183f,0) }, + { "Dun Algaz", new Location(-4086.41f,-2604.38f,44.7943f,1.58996f,0) }, + { "Dun Garok", new Location(-1256.99f,-1189.47f,38.9804f,3.82979f,0) }, + { "Dun Modr", new Location(-2600.48f,-2350.81f,82.9572f,0.914501f,0) }, + { "Dun Morogh", new Location(-5451.55f,-656.992f,392.675f,0.66789f,0) }, + { "Durnholde Keep", new Location(-483.455f,-1426.23f,89.1916f,1.93697f,0) }, + { "Durotar", new Location(1007.78f,-4446.22f,11.2022f,0.20797f,1) }, + { "Durotar Zeppelin", new Location(1340.98f,-4638.58f,53.5445f,5.0328f,1) }, + { "Duskwood", new Location(-10898.3f,-364.784f,39.2681f,3.04614f,0) }, + { "Dustwallow Marsh", new Location(-4043.65f,-2991.32f,36.3984f,3.37443f,1) }, + { "Eastern Plaguelands", new Location(2300.97f,-4613.36f,73.6231f,0.367722f,0) }, + { "Eastwall Gate", new Location(3194.88f,-4038.96f,107.991f,6.27156f,0) }, + { "Echo Isles", new Location(-1041.59f,-5346.67f,0.218679f,4.0102f,1) }, + { "Elwynn Forest", new Location(-9617.06f,-288.949f,57.3053f,4.72687f,0) }, + { "Emerald Sanctuary", new Location(3986.71f,-1293.58f,250.144f,5.74591f,1) }, + { "Everlook", new Location(6725.69f,-4619.44f,720.909f,4.66802f,1) }, + { "Feathermoon Stronghold", new Location(-4317.47f,3287.35f,18.2864f,3.12825f,1) }, + { "Felwood", new Location(4102.25f,-1006.79f,272.717f,0.790048f,1) }, + { "Fenris Isle", new Location(998.173f,736.541f,59.2738f,6.16398f,0) }, + { "Feralas", new Location(-4841.19f,1309.44f,81.3937f,1.48501f,1) }, + { "Flame Crest", new Location(-7501.51f,-2183.08f,165.926f,6.07144f,0) }, + { "Freewind Post", new Location(-5431.78f,-2449.38f,89.2848f,2.32854f,1) }, + { "Gadgetzan", new Location(-7177.15f,-3785.34f,8.36981f,6.10237f,1) }, + { "Gates Of Ahn' Qiraj", new Location(-8216.06f,1536.36f,1.30797f,3.0826f,1) }, + { "Ghost Walker Post", new Location(-1224.46f,1728.53f,90.0592f,0.831707f,1) }, + { "GM Island", new Location(16226.2f,16257f,13.2022f,1.65007f,1) }, + { "Gnomeregan", new Location(-5163.54f,925.423f,257.181f,1.57423f,0) }, + { "Goldshire", new Location(-9448.55f,68.236f,56.3225f,2.1115f,0) }, + { "Grim Batol", new Location(-4074.39f,-3459.53f,281.388f,0.859539f,0) }, + { "Grom'gol Base Camp", new Location(-12388.9f,172.578f,2.83358f,1.91753f,0) }, + { "Gurubashi Arena", new Location(-13277.4f,127.372f,26.1418f,1.11878f,0) }, + { "Hammerfall", new Location(-941.007f,-3526.66f,70.935f,3.48668f,0) }, + { "Hearthglen", new Location(2793.09f,-1621.4f,129.333f,1.98722f,0) }, + { "Hillsbrad Foothills", new Location(-436.657f,-581.254f,53.5944f,1.25917f,0) }, + { "Ironforge", new Location(-4918.88f,-940.406f,501.564f,5.42347f,0) }, + { "Jintha' Alor", new Location(-233.765f,-4121.89f,117.635f,3.39306f,0) }, + { "Kargath", new Location(-6692.48f,-2175.31f,244.145f,0.427571f,0) }, + { "Kharanos", new Location(-5597.31f,-483.398f,396.981f,3.17566f,0) }, + { "Lakeshire", new Location(-9266.59f,-2188.77f,64.0892f,2.10205f,0) }, + { "Light's Hope Chapel", new Location(2279.65f,-5310.01f,87.0759f,5.07618f,0) }, + { "Loch Modan", new Location(-5202.94f,-2855.18f,336.822f,0.37651f,0) }, + { "Maraudon", new Location(-1419.13f,2908.14f,137.464f,1.57366f,1) }, + { "Maraudon Orange", new Location(-1464.14f,2615.21f,76.7172f,3.21357f,1) }, + { "Maraudon Purple", new Location(-1188.37f,2879.61f,85.7888f,5.07366f,1) }, + { "Marshal's Refuge", new Location(-6152.25f,-1087.6f,-201.435f,0.707637f,1) }, + { "Menethil Harbor", new Location(-3769.32f,-744.26f,8.01027f,1.95752f,0) }, + { "Mirage Raceway", new Location(-6221.35f,-3927.64f,-58.7495f,0.757735f,1) }, + { "Molten Core", new Location(1126.64f,-459.94f,-102.535f,3.46095f,230) }, + { "Moonbrook", new Location(-10986.7f,1542.75f,44.7858f,2.62438f,0) }, + { "Moonglade", new Location(7654.3f,-2232.87f,462.107f,5.96786f,1) }, + { "Morgan's Vigil", new Location(-8372.77f,-2754.46f,186.622f,3.43486f,0) }, + { "Mor'shan Base Camp", new Location(1035.62f,-2106f,122.946f,1.60767f,1) }, + { "Mudsprocket", new Location(-4573.79f,-3173.15f,34.0877f,3.1231f,1) }, + { "Mulgore", new Location(-2192.62f,-736.317f,-13.3274f,0.487569f,1) }, + { "Naxxramas", new Location(3120.16f,-3724.93f,137.66f,5.83567f,0) }, + { "Nethergarde Keep", new Location(-10999.8f,-3380.08f,62.2525f,4.63501f,0) }, + { "Nighthaven", new Location(7966.85f,-2491.04f,487.734f,3.20562f,1) }, + { "Nijel's Point", new Location(176.426f,1309.76f,190.18f,0.556817f,1) }, + { "Northshire Valley", new Location(-8921.09f,-119.135f,82.195f,5.82878f,0) }, + { "Onyxia's Lair", new Location(-4708.27f,-3727.64f,54.5589f,3.72786f,1) }, + { "Orb Of Command", new Location(-7663.74f,-1217.4f,287.789f,5.33945f,0) }, + { "Orgrimmar", new Location(1629.36f,-4373.39f,31.2564f,3.54839f,1) }, + { "Plaguewood", new Location(3065.36f,-3704f,120.931f,1.21752f,0) }, + { "Pyrewood Village", new Location(-388.146f,1543.67f,18.1592f,3.10171f,0) }, + { "Quel' Danil Lodge", new Location(226.318f,-2777.59f,123.356f,0.59469f,0) }, + { "Ragefire Chasm", new Location(1811.78f,-4410.5f,-18.4704f,5.20165f,1) }, + { "Ratchet", new Location(-956.664f,-3754.71f,5.33239f,0.996637f,1) }, + { "Raven Hill", new Location(-10742.2f,330.574f,38.2503f,0.551712f,0) }, + { "Razor Hill", new Location(326.81f,-4706.65f,15.3665f,4.16414f,1) }, + { "Razorfen Downs", new Location(-4657.3f,-2519.35f,81.0529f,4.54808f,1) }, + { "Razorfen Kraul", new Location(-4470.28f,-1677.77f,81.3925f,1.16302f,1) }, + { "Rebel Camp", new Location(-11322.4f,-202.492f,75.6362f,0.432339f,0) }, + { "Redridge Mountains", new Location(-9551.81f,-2204.73f,93.473f,5.47141f,0) }, + { "Refuge Pointe", new Location(-1246.61f,-2529.32f,20.6098f,0.741709f,0) }, + { "Revantusk Village", new Location(-557.226f,-4581.27f,9.5884f,1.01724f,0) }, + { "Ruins Of Alterac", new Location(629.684f,-348.068f,151.105f,2.85588f,0) }, + { "Ruins Of Andorhal", new Location(1400.61f,-1493.87f,54.7844f,4.08661f,0) }, + { "Scarlet Monastery", new Location(2872.6f,-764.398f,160.332f,5.05735f,0) }, + { "Scholomance", new Location(1269.64f,-2556.21f,93.6088f,0.620623f,0) }, + { "Searing Gorge", new Location(-7012.47f,-1065.13f,241.786f,5.63162f,0) }, + { "Sen'jin Village", new Location(-813.097f,-4880.08f,18.995f,4.42647f,1) }, + { "Sentinel Hill", new Location(-10624.5f,1096.66f,33.7641f,1.31041f,0) }, + { "Seradane", new Location(799.721f,-3995.68f,122.007f,3.77399f,0) }, + { "Shadow Fang Keep", new Location(-234.675f,1561.63f,76.8921f,1.24031f,0) }, + { "Shadowglen", new Location(10334f,833.902f,1326.11f,3.62142f,1) }, + { "Shadowprey Village", new Location(-1664.79f,3091.67f,30.5552f,6.07818f,1) }, + { "Shady Rest Inn", new Location(-3707.79f,-2530.37f,68.2635f,3.31945f,1) }, + { "Silithus", new Location(-7426.87f,1005.31f,1.13359f,2.96086f,1) }, + { "Silverpine Forest", new Location(878.74f,1359.33f,50.355f,5.89929f,0) }, + { "Skulk Rock", new Location(369.856f,-3802.84f,170.093f,3.58942f,0) }, + { "Southshore", new Location(-853.221f,-533.529f,9.98556f,0.242866f,0) }, + { "Splintertree Post", new Location(2270.94f,-2538.19f,93.9198f,0.060429f,1) }, + { "Steamwheedle Port", new Location(-6908.08f,-4801.39f,8.15214f,5.07916f,1) }, + { "Stonard", new Location(-10446.9f,-3261.91f,20.1795f,5.02142f,0) }, + { "Stonetalon Mountains", new Location(1570.92f,1031.52f,137.959f,3.33006f,1) }, + { "Stonetalon Peak", new Location(2678.38f,1497.46f,233.869f,6.26038f,1) }, + { "Stonewatch", new Location(-9323.5f,-3030.84f,132.559f,2.94713f,0) }, + { "Stonewrought Dam", new Location(-4750.07f,-3328.02f,310.257f,4.61609f,0) }, + { "Stormwind", new Location(-8833.38f,628.628f,94.0066f,1.06535f,0) }, + { "Strahnbrad", new Location(659.762f,-959.316f,164.404f,0.433716f,0) }, + { "Stranglethorn Vale", new Location(-12644.3f,-377.411f,10.1021f,6.09978f,0) }, + { "Stratholme", new Location(3352.92f,-3379.03f,144.782f,6.25978f,0) }, + { "Stromgarde Keep", new Location(-1551.2f,-1808.1f,67.5219f,3.119f,0) }, + { "Sun Rock Retreat", new Location(966.147f,926.499f,104.649f,1.27231f,1) }, + { "Swamp Of Sorrows", new Location(-10345.4f,-2773.42f,21.99f,5.08426f,0) }, + { "Talonbranch Glade", new Location(6209.51f,-1927.01f,569.393f,3.82137f,1) }, + { "Talrendis Point", new Location(2735.06f,-3867.44f,98.6548f,3.56139f,1) }, + { "Tanaris", new Location(-7931.2f,-3414.28f,80.7365f,0.66522f,1) }, + { "Tarren Mill", new Location(-34.1467f,-923.366f,54.5576f,0.15019f,0) }, + { "Teldrassil", new Location(10111.3f,1557.73f,1324.33f,4.04239f,1) }, + { "Temple Of Atal' Hakkar", new Location(-10450.3f,-3825.44f,18.0679f,6.03616f,0) }, + { "Terrordale", new Location(2957.87f,-2794.79f,110.464f,1.19003f,0) }, + { "Thalanaar", new Location(-4525.63f,-791.364f,-42.3639f,1.09938f,1) }, + { "The Altar Of Zul", new Location(-271.689f,-3438.52f,187.18f,3.93027f,0) }, + { "The Black Morass", new Location(-8734.3f,-4230.11f,-209.5f,2.16212f,1) }, + { "The Bulwark", new Location(1711.99f,-719.761f,54.3351f,4.66387f,0) }, + { "The Cauldron", new Location(-6939.52f,-1263.21f,179.709f,0.200595f,0) }, + { "The Crossroads", new Location(-452.84f,-2650.76f,95.5209f,0.241081f,1) }, + { "The Dark Portal", new Location(-11840.1f,-3196.63f,-29.6059f,3.3391f,0) }, + { "The Deadmines", new Location(-11208.7f,1673.52f,24.6361f,1.51067f,0) }, + { "The Forgotten Coast", new Location(-4347.46f,2415.11f,8.00515f,1.52603f,1) }, + { "The Fungal Vale", new Location(2448.89f,-3708.71f,177.867f,5.66288f,0) }, + { "The Grinding Quarry", new Location(-7390.69f,-941.553f,169.43f,3.90454f,0) }, + { "The Harborage", new Location(-10126f,-2834.73f,22.2157f,0.674244f,0) }, + { "The Hinterlands", new Location(119.387f,-3190.37f,117.331f,2.34064f,0) }, + { "The Loch", new Location(-5289.82f,-3482.56f,297.605f,6.2238f,0) }, + { "The Marris Stead", new Location(1869.13f,-3213.89f,124.624f,1.9126f,0) }, + { "The Molten Span", new Location(-7538.51f,-1063.45f,180.981f,0.03409f,0) }, + { "The Ruins Of Ahn' Qiraj", new Location(-8409.82f,1499.06f,27.7179f,2.51868f,1) }, + { "The Scarab Wall", new Location(-8098.67f,1525.15f,2.77194f,3.01977f,1) }, + { "The Sepulcher", new Location(504.534f,1539.08f,129.502f,1.35812f,0) }, + { "The Stockades", new Location(-8787.39f,828.377f,97.6489f,0.626312f,0) }, + { "The Sunken Temple", new Location(-10177.9f,-3994.9f,-111.239f,6.01885f,0) }, + { "The Tainted Scar", new Location(-11892.7f,-2647.08f,-4.68415f,3.69096f,0) }, + { "The Temple Of Ahn' Qiraj", new Location(-8240.09f,1991.32f,129.072f,0.941603f,1) }, + { "The Vice", new Location(-10879.6f,-2206.99f,122.514f,3.74515f,0) }, + { "Thelsamar", new Location(-5352.54f,-2948.53f,323.78f,5.34258f,0) }, + { "Theramore", new Location(-3641.3f,-4358.93f,8.35467f,3.81559f,1) }, + { "Thorium Point", new Location(-6506.47f,-1149.95f,307.708f,4.18256f,0) }, + { "Thousand Needles", new Location(-4969.02f,-1726.89f,-62.1269f,3.7933f,1) }, + { "Thunder Bluff", new Location(-1277.37f,124.804f,131.287f,5.22274f,1) }, + { "Timbermaw Hold", new Location(6808.73f,-2091.08f,624.962f,5.93802f,1) }, + { "Tirisfal Glades", new Location(2036.02f,161.331f,33.8674f,0.143896f,0) }, + { "Tirisfal Glades Zeppelin", new Location(2063.35f,273.607f,94.1076f,5.30632f,0) }, + { "Tower Of Ilgalar", new Location(-9284.76f,-3346.89f,109.759f,1.52871f,0) }, + { "Twilight Grove", new Location(-10384.3f,-421.588f,63.6179f,3.23856f,0) }, + { "Tyr's Hand", new Location(1684.77f,-5320.44f,73.6126f,4.52641f,0) }, + { "Uldaman", new Location(-6071.37f,-2955.16f,209.782f,0.015708f,0) }, + { "Un' Goro Crater", new Location(-7943.22f,-2119.09f,-218.343f,6.0727f,1) }, + { "Undercity", new Location(1584.07f,241.987f,-52.1534f,0.049647f,0) }, + { "Valley Of Trials", new Location(-601.294f,-4296.76f,37.8115f,1.65401f,1) }, + { "Valormok", new Location(3608.59f,-4414.43f,113.047f,1.62303f,1) }, + { "Valor's Rest", new Location(-6382.67f,-291.916f,-3.07818f,4.47432f,1) }, + { "Wailing Caverns", new Location(-731.607f,-2218.39f,17.0281f,2.78486f,1) }, + { "Warsong Gulch", new Location(1235.54f,1427.1f,309.715f,0.557629f,489) }, + { "Warsong Gulch Alliance", new Location(1525.95f,1481.66f,352.001f,3.20756f,489) }, + { "Warsong Gulch Horde", new Location(930.851f,1431.57f,345.537f,0.015704f,489) }, + { "Western Plaguelands", new Location(1728.65f,-1602.25f,63.429f,1.6558f,0) }, + { "Westfall", new Location(-10235.2f,1222.47f,43.6252f,6.2427f,0) }, + { "Wetlands", new Location(-3242.81f,-2469.04f,15.9226f,6.03924f,0) }, + { "Winterspring", new Location(6759.18f,-4419.63f,763.214f,4.43476f,1) }, + { "Zoram'gar Outpost", new Location(3376.86f,1013.05f,3.34387f,3.81699f,1) }, + { "Zul' Farrak", new Location(-6801.19f,-2893.02f,9.00388f,0.158639f,1) }, + { "Zul' Gurub", new Location(-11916.7f,-1215.72f,92.289f,4.72454f,0) } + }; + + public static IEnumerable> FindLocation(string needle) + { + Func FormatString = (s) => s.Replace(" ", "").Replace("'", "").ToLower().Trim(); + + needle = FormatString(needle); + + var exact = Locations.FirstOrDefault(x => FormatString(x.Key) == needle); + if (exact.Key != null) + return new[] { exact }; + + return Locations.Where(x => FormatString(x.Key).Contains(needle)); + } + } + + +} diff --git a/Common/Cryptography/BigInteger.cs b/Common/Cryptography/BigInteger.cs new file mode 100644 index 0000000..3c0a0e9 --- /dev/null +++ b/Common/Cryptography/BigInteger.cs @@ -0,0 +1,2982 @@ +//************************************************************************************ +// BigInteger Class Version 1.03 +// +// Copyright (c) 2002 Chew Keong TAN +// All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, provided that the above +// copyright notice(s) and this permission notice appear in all copies of +// the Software and that both the above copyright notice(s) and this +// permission notice appear in supporting documentation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// +// Disclaimer +// ---------- +// Although reasonable care has been taken to ensure the correctness of this +// implementation, this code should never be used in any application without +// proper verification and testing. I disclaim all liability and responsibility +// to any person or entity with respect to any loss or damage caused, or alleged +// to be caused, directly or indirectly, by the use of this BigInteger class. +// +// Comments, bugs and suggestions to +// (http://www.codeproject.com/csharp/biginteger.asp) +// +// +// Overloaded Operators +, -, *, /, %, >>, <<, ==, !=, >, <, >=, <=, &, |, ^, ++, --, ~ +// +// Features +// -------- +// 1) Arithmetic operations involving large signed integers (2's complement). +// 2) Primality test using Fermat little theorm, Rabin Miller's method, +// Solovay Strassen's method and Lucas strong pseudoprime. +// 3) Modulo exponential with Barrett's reduction. +// 4) Inverse modulo. +// 5) Pseudo prime generation. +// 6) Co-prime generation. +// +// +// Known Problem +// ------------- +// This pseudoprime passes my implementation of +// primality test but failed in JDK's isProbablePrime test. +// +// byte[] pseudoPrime1 = { (byte)0x00, +// (byte)0x85, (byte)0x84, (byte)0x64, (byte)0xFD, (byte)0x70, (byte)0x6A, +// (byte)0x9F, (byte)0xF0, (byte)0x94, (byte)0x0C, (byte)0x3E, (byte)0x2C, +// (byte)0x74, (byte)0x34, (byte)0x05, (byte)0xC9, (byte)0x55, (byte)0xB3, +// (byte)0x85, (byte)0x32, (byte)0x98, (byte)0x71, (byte)0xF9, (byte)0x41, +// (byte)0x21, (byte)0x5F, (byte)0x02, (byte)0x9E, (byte)0xEA, (byte)0x56, +// (byte)0x8D, (byte)0x8C, (byte)0x44, (byte)0xCC, (byte)0xEE, (byte)0xEE, +// (byte)0x3D, (byte)0x2C, (byte)0x9D, (byte)0x2C, (byte)0x12, (byte)0x41, +// (byte)0x1E, (byte)0xF1, (byte)0xC5, (byte)0x32, (byte)0xC3, (byte)0xAA, +// (byte)0x31, (byte)0x4A, (byte)0x52, (byte)0xD8, (byte)0xE8, (byte)0xAF, +// (byte)0x42, (byte)0xF4, (byte)0x72, (byte)0xA1, (byte)0x2A, (byte)0x0D, +// (byte)0x97, (byte)0xB1, (byte)0x31, (byte)0xB3, +// }; +// +// +// Change Log +// ---------- +// 1) September 23, 2002 (Version 1.03) +// - Fixed operator- to give correct data length. +// - Added Lucas sequence generation. +// - Added Strong Lucas Primality test. +// - Added integer square root method. +// - Added setBit/unsetBit methods. +// - New isProbablePrime() method which do not require the +// confident parameter. +// +// 2) August 29, 2002 (Version 1.02) +// - Fixed bug in the exponentiation of negative numbers. +// - Faster modular exponentiation using Barrett reduction. +// - Added getBytes() method. +// - Fixed bug in ToHexString method. +// - Added overloading of ^ operator. +// - Faster computation of Jacobi symbol. +// +// 3) August 19, 2002 (Version 1.01) +// - Big integer is stored and manipulated as unsigned integers (4 bytes) instead of +// individual bytes this gives significant performance improvement. +// - Updated Fermat's Little Theorem test to use a^(p-1) mod p = 1 +// - Added isProbablePrime method. +// - Updated documentation. +// +// 4) August 9, 2002 (Version 1.0) +// - Initial Release. +// +// +// References +// [1] D. E. Knuth, "Seminumerical Algorithms", The Art of Computer Programming Vol. 2, +// 3rd Edition, Addison-Wesley, 1998. +// +// [2] K. H. Rosen, "Elementary Number Theory and Its Applications", 3rd Ed, +// Addison-Wesley, 1993. +// +// [3] B. Schneier, "Applied Cryptography", 2nd Ed, John Wiley & Sons, 1996. +// +// [4] A. Menezes, P. van Oorschot, and S. Vanstone, "Handbook of Applied Cryptography", +// CRC Press, 1996, www.cacr.math.uwaterloo.ca/hac +// +// [5] A. Bosselaers, R. Govaerts, and J. Vandewalle, "Comparison of Three Modular +// Reduction Functions," Proc. CRYPTO'93, pp.175-186. +// +// [6] R. Baillie and S. S. Wagstaff Jr, "Lucas Pseudoprimes", Mathematics of Computation, +// Vol. 35, No. 152, Oct 1980, pp. 1391-1417. +// +// [7] H. C. Williams, "Édouard Lucas and Primality Testing", Canadian Mathematical +// Society Series of Monographs and Advance Texts, vol. 22, John Wiley & Sons, New York, +// NY, 1998. +// +// [8] P. Ribenboim, "The new book of prime number records", 3rd edition, Springer-Verlag, +// New York, NY, 1995. +// +// [9] M. Joye and J.-J. Quisquater, "Efficient computation of full Lucas sequences", +// Electronics Letters, 32(6), 1996, pp 537-538. +// +//************************************************************************************ + +using System; + + +public class BigInteger +{ + // maximum length of the BigInteger in uint (4 bytes) + // change this to suit the required level of precision. + + private const int maxLength = 70; + + // primes smaller than 2000 to test the generated prime number + + public static readonly int[] primesBelow2000 = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, + 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, + 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, + 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, + 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, + 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, + 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, + 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, + 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, + 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, + 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, + 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, + 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, + 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999 }; + + + private uint[] data = null; // stores bytes from the Big Integer + public int dataLength; // number of actual chars used + + + //*********************************************************************** + // Constructor (Default value for BigInteger is 0 + //*********************************************************************** + + public BigInteger() + { + data = new uint[maxLength]; + dataLength = 1; + } + + + //*********************************************************************** + // Constructor (Default value provided by long) + //*********************************************************************** + + public BigInteger(long value) + { + data = new uint[maxLength]; + long tempVal = value; + + // copy bytes from long to BigInteger without any assumption of + // the length of the long datatype + + dataLength = 0; + while (value != 0 && dataLength < maxLength) + { + data[dataLength] = (uint)(value & 0xFFFFFFFF); + value >>= 32; + dataLength++; + } + + if (tempVal > 0) // overflow check for +ve value + { + if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + } + else if (tempVal < 0) // underflow check for -ve value + { + if (value != -1 || (data[dataLength - 1] & 0x80000000) == 0) + throw (new ArithmeticException("Negative underflow in constructor.")); + } + + if (dataLength == 0) + dataLength = 1; + } + + + //*********************************************************************** + // Constructor (Default value provided by ulong) + //*********************************************************************** + + public BigInteger(ulong value) + { + data = new uint[maxLength]; + + // copy bytes from ulong to BigInteger without any assumption of + // the length of the ulong datatype + + dataLength = 0; + while (value != 0 && dataLength < maxLength) + { + data[dataLength] = (uint)(value & 0xFFFFFFFF); + value >>= 32; + dataLength++; + } + + if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + + if (dataLength == 0) + dataLength = 1; + } + + + + //*********************************************************************** + // Constructor (Default value provided by BigInteger) + //*********************************************************************** + + public BigInteger(BigInteger bi) + { + data = new uint[maxLength]; + + dataLength = bi.dataLength; + + for (int i = 0; i < dataLength; i++) + data[i] = bi.data[i]; + } + + + //*********************************************************************** + // Constructor (Default value provided by a string of digits of the + // specified base) + // + // Example (base 10) + // ----------------- + // To initialize "a" with the default value of 1234 in base 10 + // BigInteger a = new BigInteger("1234", 10) + // + // To initialize "a" with the default value of -1234 + // BigInteger a = new BigInteger("-1234", 10) + // + // Example (base 16) + // ----------------- + // To initialize "a" with the default value of 0x1D4F in base 16 + // BigInteger a = new BigInteger("1D4F", 16) + // + // To initialize "a" with the default value of -0x1D4F + // BigInteger a = new BigInteger("-1D4F", 16) + // + // Note that string values are specified in the + // format. + // + //*********************************************************************** + + public BigInteger(string value, int radix) + { + BigInteger multiplier = new BigInteger(1); + BigInteger result = new BigInteger(); + value = (value.ToUpper()).Trim(); + int limit = 0; + + if (value[0] == '-') + limit = 1; + + for (int i = value.Length - 1; i >= limit; i--) + { + int posVal = (int)value[i]; + + if (posVal >= '0' && posVal <= '9') + posVal -= '0'; + else if (posVal >= 'A' && posVal <= 'Z') + posVal = (posVal - 'A') + 10; + else + posVal = 9999999; // arbitrary large + + + if (posVal >= radix) + throw (new ArithmeticException("Invalid string in constructor.")); + else + { + if (value[0] == '-') + posVal = -posVal; + + result = result + (multiplier * posVal); + + if ((i - 1) >= limit) + multiplier = multiplier * radix; + } + } + + if (value[0] == '-') // negative values + { + if ((result.data[maxLength - 1] & 0x80000000) == 0) + throw (new ArithmeticException("Negative underflow in constructor.")); + } + else // positive values + { + if ((result.data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + } + + data = new uint[maxLength]; + for (int i = 0; i < result.dataLength; i++) + data[i] = result.data[i]; + + dataLength = result.dataLength; + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of bytes) + // + // The lowest index of the input byte array (i.e [0]) should contain the + // most significant byte of the number, and the highest index should + // contain the least significant byte. + // + // E.g. + // To initialize "a" with the default value of 0x1D4F in base 16 + // byte[] temp = { 0x1D, 0x4F }; + // BigInteger a = new BigInteger(temp) + // + // Note that this method of initialization does not allow the + // sign to be specified. + // + //*********************************************************************** + + public BigInteger(byte[] inData) + { + dataLength = inData.Length >> 2; + + int leftOver = inData.Length & 0x3; + if (leftOver != 0) // length not multiples of 4 + dataLength++; + + + if (dataLength > maxLength) + throw (new ArithmeticException("Byte overflow in constructor.")); + + data = new uint[maxLength]; + + for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) + { + data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + + (inData[i - 1] << 8) + inData[i]); + } + + if (leftOver == 1) + data[dataLength - 1] = (uint)inData[0]; + else if (leftOver == 2) + data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); + else if (leftOver == 3) + data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); + + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of bytes of the + // specified length.) + //*********************************************************************** + + public BigInteger(byte[] inData, int inLen) + { + dataLength = inLen >> 2; + + int leftOver = inLen & 0x3; + if (leftOver != 0) // length not multiples of 4 + dataLength++; + + if (dataLength > maxLength || inLen > inData.Length) + throw (new ArithmeticException("Byte overflow in constructor.")); + + + data = new uint[maxLength]; + + for (int i = inLen - 1, j = 0; i >= 3; i -= 4, j++) + { + data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + + (inData[i - 1] << 8) + inData[i]); + } + + if (leftOver == 1) + data[dataLength - 1] = (uint)inData[0]; + else if (leftOver == 2) + data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); + else if (leftOver == 3) + data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); + + + if (dataLength == 0) + dataLength = 1; + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of unsigned integers) + //********************************************************************* + + public BigInteger(uint[] inData) + { + dataLength = inData.Length; + + if (dataLength > maxLength) + throw (new ArithmeticException("Byte overflow in constructor.")); + + data = new uint[maxLength]; + + for (int i = dataLength - 1, j = 0; i >= 0; i--, j++) + data[j] = inData[i]; + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Overloading of the typecast operator. + // For BigInteger bi = 10; + //*********************************************************************** + + public static implicit operator BigInteger(long value) + { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(ulong value) + { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(int value) + { + return (new BigInteger((long)value)); + } + + public static implicit operator BigInteger(uint value) + { + return (new BigInteger((ulong)value)); + } + + + //*********************************************************************** + // Overloading of addition operator + //*********************************************************************** + + public static BigInteger operator +(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + long carry = 0; + for (int i = 0; i < result.dataLength; i++) + { + long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; + carry = sum >> 32; + result.data[i] = (uint)(sum & 0xFFFFFFFF); + } + + if (carry != 0 && result.dataLength < maxLength) + { + result.data[result.dataLength] = (uint)(carry); + result.dataLength++; + } + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + + // overflow check + int lastPos = maxLength - 1; + if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException()); + } + + return result; + } + + + //*********************************************************************** + // Overloading of the unary ++ operator + //*********************************************************************** + + public static BigInteger operator ++(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + long val, carry = 1; + int index = 0; + + while (carry != 0 && index < maxLength) + { + val = (long)(result.data[index]); + val++; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + + index++; + } + + if (index > result.dataLength) + result.dataLength = index; + else + { + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + } + + // overflow check + int lastPos = maxLength - 1; + + // overflow if initial value was +ve but ++ caused a sign + // change to negative. + + if ((bi1.data[lastPos] & 0x80000000) == 0 && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException("Overflow in ++.")); + } + return result; + } + + + //*********************************************************************** + // Overloading of subtraction operator + //*********************************************************************** + + public static BigInteger operator -(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + long carryIn = 0; + for (int i = 0; i < result.dataLength; i++) + { + long diff; + + diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; + result.data[i] = (uint)(diff & 0xFFFFFFFF); + + if (diff < 0) + carryIn = 1; + else + carryIn = 0; + } + + // roll over to negative + if (carryIn != 0) + { + for (int i = result.dataLength; i < maxLength; i++) + result.data[i] = 0xFFFFFFFF; + result.dataLength = maxLength; + } + + // fixed in v1.03 to give correct datalength for a - (-b) + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check + + int lastPos = maxLength - 1; + if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException()); + } + + return result; + } + + + //*********************************************************************** + // Overloading of the unary -- operator + //*********************************************************************** + + public static BigInteger operator --(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + long val; + bool carryIn = true; + int index = 0; + + while (carryIn && index < maxLength) + { + val = (long)(result.data[index]); + val--; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + + if (val >= 0) + carryIn = false; + + index++; + } + + if (index > result.dataLength) + result.dataLength = index; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check + int lastPos = maxLength - 1; + + // overflow if initial value was -ve but -- caused a sign + // change to positive. + + if ((bi1.data[lastPos] & 0x80000000) != 0 && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException("Underflow in --.")); + } + + return result; + } + + + //*********************************************************************** + // Overloading of multiplication operator + //*********************************************************************** + + public static BigInteger operator *(BigInteger bi1, BigInteger bi2) + { + int lastPos = maxLength - 1; + bool bi1Neg = false, bi2Neg = false; + + // take the absolute value of the inputs + try + { + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1Neg = true; bi1 = -bi1; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + { + bi2Neg = true; bi2 = -bi2; + } + } + catch (Exception) { } + + BigInteger result = new BigInteger(); + + // multiply the absolute values + try + { + for (int i = 0; i < bi1.dataLength; i++) + { + if (bi1.data[i] == 0) continue; + + ulong mcarry = 0; + for (int j = 0, k = i; j < bi2.dataLength; j++, k++) + { + // k = i + j + ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + + (ulong)result.data[k] + mcarry; + + result.data[k] = (uint)(val & 0xFFFFFFFF); + mcarry = (val >> 32); + } + + if (mcarry != 0) + result.data[i + bi2.dataLength] = (uint)mcarry; + } + } + catch (Exception) + { + throw (new ArithmeticException("Multiplication overflow.")); + } + + + result.dataLength = bi1.dataLength + bi2.dataLength; + if (result.dataLength > maxLength) + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check (result is -ve) + if ((result.data[lastPos] & 0x80000000) != 0) + { + if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign + { + // handle the special case where multiplication produces + // a max negative number in 2's complement. + + if (result.dataLength == 1) + return result; + else + { + bool isMaxNeg = true; + for (int i = 0; i < result.dataLength - 1 && isMaxNeg; i++) + { + if (result.data[i] != 0) + isMaxNeg = false; + } + + if (isMaxNeg) + return result; + } + } + + throw (new ArithmeticException("Multiplication overflow.")); + } + + // if input has different signs, then result is -ve + if (bi1Neg != bi2Neg) + return -result; + + return result; + } + + + + //*********************************************************************** + // Overloading of unary << operators + //*********************************************************************** + + public static BigInteger operator <<(BigInteger bi1, int shiftVal) + { + BigInteger result = new BigInteger(bi1); + result.dataLength = shiftLeft(result.data, shiftVal); + + return result; + } + + + // least significant bits at lower part of buffer + + private static int shiftLeft(uint[] buffer, int shiftVal) + { + int shiftAmount = 32; + int bufLen = buffer.Length; + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + for (int count = shiftVal; count > 0;) + { + if (count < shiftAmount) + shiftAmount = count; + + //Console.WriteLine("shiftAmount = {0}", shiftAmount); + + ulong carry = 0; + for (int i = 0; i < bufLen; i++) + { + ulong val = ((ulong)buffer[i]) << shiftAmount; + val |= carry; + + buffer[i] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + } + + if (carry != 0) + { + if (bufLen + 1 <= buffer.Length) + { + buffer[bufLen] = (uint)carry; + bufLen++; + } + } + count -= shiftAmount; + } + return bufLen; + } + + + //*********************************************************************** + // Overloading of unary >> operators + //*********************************************************************** + + public static BigInteger operator >>(BigInteger bi1, int shiftVal) + { + BigInteger result = new BigInteger(bi1); + result.dataLength = shiftRight(result.data, shiftVal); + + + if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative + { + for (int i = maxLength - 1; i >= result.dataLength; i--) + result.data[i] = 0xFFFFFFFF; + + uint mask = 0x80000000; + for (int i = 0; i < 32; i++) + { + if ((result.data[result.dataLength - 1] & mask) != 0) + break; + + result.data[result.dataLength - 1] |= mask; + mask >>= 1; + } + result.dataLength = maxLength; + } + + return result; + } + + + private static int shiftRight(uint[] buffer, int shiftVal) + { + int shiftAmount = 32; + int invShift = 0; + int bufLen = buffer.Length; + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + //Console.WriteLine("bufLen = " + bufLen + " buffer.Length = " + buffer.Length); + + for (int count = shiftVal; count > 0;) + { + if (count < shiftAmount) + { + shiftAmount = count; + invShift = 32 - shiftAmount; + } + + //Console.WriteLine("shiftAmount = {0}", shiftAmount); + + ulong carry = 0; + for (int i = bufLen - 1; i >= 0; i--) + { + ulong val = ((ulong)buffer[i]) >> shiftAmount; + val |= carry; + + carry = ((ulong)buffer[i]) << invShift; + buffer[i] = (uint)(val); + } + + count -= shiftAmount; + } + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + return bufLen; + } + + + //*********************************************************************** + // Overloading of the NOT operator (1's complement) + //*********************************************************************** + + public static BigInteger operator ~(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + for (int i = 0; i < maxLength; i++) + result.data[i] = (uint)(~(bi1.data[i])); + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of the NEGATE operator (2's complement) + //*********************************************************************** + + public static BigInteger operator -(BigInteger bi1) + { + // handle neg of zero separately since it'll cause an overflow + // if we proceed. + + if (bi1.dataLength == 1 && bi1.data[0] == 0) + return (new BigInteger()); + + BigInteger result = new BigInteger(bi1); + + // 1's complement + for (int i = 0; i < maxLength; i++) + result.data[i] = (uint)(~(bi1.data[i])); + + // add one to result of 1's complement + long val, carry = 1; + int index = 0; + + while (carry != 0 && index < maxLength) + { + val = (long)(result.data[index]); + val++; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + + index++; + } + + if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) + throw (new ArithmeticException("Overflow in negation.\n")); + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + return result; + } + + + //*********************************************************************** + // Overloading of equality operator + //*********************************************************************** + + public static bool operator ==(BigInteger bi1, BigInteger bi2) + { + return bi1.Equals(bi2); + } + + + public static bool operator !=(BigInteger bi1, BigInteger bi2) + { + return !(bi1.Equals(bi2)); + } + + + public override bool Equals(object o) + { + BigInteger bi = (BigInteger)o; + + if (this.dataLength != bi.dataLength) + return false; + + for (int i = 0; i < this.dataLength; i++) + { + if (this.data[i] != bi.data[i]) + return false; + } + return true; + } + + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + + + //*********************************************************************** + // Overloading of inequality operator + //*********************************************************************** + + public static bool operator >(BigInteger bi1, BigInteger bi2) + { + int pos = maxLength - 1; + + // bi1 is negative, bi2 is positive + if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) + return false; + + // bi1 is positive, bi2 is negative + else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) + return true; + + // same sign + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; + + if (pos >= 0) + { + if (bi1.data[pos] > bi2.data[pos]) + return true; + return false; + } + return false; + } + + + public static bool operator <(BigInteger bi1, BigInteger bi2) + { + int pos = maxLength - 1; + + // bi1 is negative, bi2 is positive + if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) + return true; + + // bi1 is positive, bi2 is negative + else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) + return false; + + // same sign + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; + + if (pos >= 0) + { + if (bi1.data[pos] < bi2.data[pos]) + return true; + return false; + } + return false; + } + + + public static bool operator >=(BigInteger bi1, BigInteger bi2) + { + return (bi1 == bi2 || bi1 > bi2); + } + + + public static bool operator <=(BigInteger bi1, BigInteger bi2) + { + return (bi1 == bi2 || bi1 < bi2); + } + + + //*********************************************************************** + // Private function that supports the division of two numbers with + // a divisor that has more than 1 digit. + // + // Algorithm taken from [1] + //*********************************************************************** + + private static void multiByteDivide(BigInteger bi1, BigInteger bi2, + BigInteger outQuotient, BigInteger outRemainder) + { + uint[] result = new uint[maxLength]; + + int remainderLen = bi1.dataLength + 1; + uint[] remainder = new uint[remainderLen]; + + uint mask = 0x80000000; + uint val = bi2.data[bi2.dataLength - 1]; + int shift = 0, resultPos = 0; + + while (mask != 0 && (val & mask) == 0) + { + shift++; mask >>= 1; + } + + //Console.WriteLine("shift = {0}", shift); + //Console.WriteLine("Before bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); + + for (int i = 0; i < bi1.dataLength; i++) + remainder[i] = bi1.data[i]; + shiftLeft(remainder, shift); + bi2 = bi2 << shift; + + /* + Console.WriteLine("bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); + Console.WriteLine("dividend = " + bi1 + "\ndivisor = " + bi2); + for(int q = remainderLen - 1; q >= 0; q--) + Console.Write("{0:x2}", remainder[q]); + Console.WriteLine(); + */ + + int j = remainderLen - bi2.dataLength; + int pos = remainderLen - 1; + + ulong firstDivisorByte = bi2.data[bi2.dataLength - 1]; + ulong secondDivisorByte = bi2.data[bi2.dataLength - 2]; + + int divisorLen = bi2.dataLength + 1; + uint[] dividendPart = new uint[divisorLen]; + + while (j > 0) + { + ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; + //Console.WriteLine("dividend = {0}", dividend); + + ulong q_hat = dividend / firstDivisorByte; + ulong r_hat = dividend % firstDivisorByte; + + //Console.WriteLine("q_hat = {0:X}, r_hat = {1:X}", q_hat, r_hat); + + bool done = false; + while (!done) + { + done = true; + + if (q_hat == 0x100000000 || + (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) + { + q_hat--; + r_hat += firstDivisorByte; + + if (r_hat < 0x100000000) + done = false; + } + } + + for (int h = 0; h < divisorLen; h++) + dividendPart[h] = remainder[pos - h]; + + BigInteger kk = new BigInteger(dividendPart); + BigInteger ss = bi2 * (long)q_hat; + + //Console.WriteLine("ss before = " + ss); + while (ss > kk) + { + q_hat--; + ss -= bi2; + //Console.WriteLine(ss); + } + BigInteger yy = kk - ss; + + //Console.WriteLine("ss = " + ss); + //Console.WriteLine("kk = " + kk); + //Console.WriteLine("yy = " + yy); + + for (int h = 0; h < divisorLen; h++) + remainder[pos - h] = yy.data[bi2.dataLength - h]; + + /* + Console.WriteLine("dividend = "); + for(int q = remainderLen - 1; q >= 0; q--) + Console.Write("{0:x2}", remainder[q]); + Console.WriteLine("\n************ q_hat = {0:X}\n", q_hat); + */ + + result[resultPos++] = (uint)q_hat; + + pos--; + j--; + } + + outQuotient.dataLength = resultPos; + int y = 0; + for (int x = outQuotient.dataLength - 1; x >= 0; x--, y++) + outQuotient.data[y] = result[x]; + for (; y < maxLength; y++) + outQuotient.data[y] = 0; + + while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) + outQuotient.dataLength--; + + if (outQuotient.dataLength == 0) + outQuotient.dataLength = 1; + + outRemainder.dataLength = shiftRight(remainder, shift); + + for (y = 0; y < outRemainder.dataLength; y++) + outRemainder.data[y] = remainder[y]; + for (; y < maxLength; y++) + outRemainder.data[y] = 0; + } + + + //*********************************************************************** + // Private function that supports the division of two numbers with + // a divisor that has only 1 digit. + //*********************************************************************** + + private static void singleByteDivide(BigInteger bi1, BigInteger bi2, + BigInteger outQuotient, BigInteger outRemainder) + { + uint[] result = new uint[maxLength]; + int resultPos = 0; + + // copy dividend to reminder + for (int i = 0; i < maxLength; i++) + outRemainder.data[i] = bi1.data[i]; + outRemainder.dataLength = bi1.dataLength; + + while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) + outRemainder.dataLength--; + + ulong divisor = (ulong)bi2.data[0]; + int pos = outRemainder.dataLength - 1; + ulong dividend = (ulong)outRemainder.data[pos]; + + //Console.WriteLine("divisor = " + divisor + " dividend = " + dividend); + //Console.WriteLine("divisor = " + bi2 + "\ndividend = " + bi1); + + if (dividend >= divisor) + { + ulong quotient = dividend / divisor; + result[resultPos++] = (uint)quotient; + + outRemainder.data[pos] = (uint)(dividend % divisor); + } + pos--; + + while (pos >= 0) + { + //Console.WriteLine(pos); + + dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; + ulong quotient = dividend / divisor; + result[resultPos++] = (uint)quotient; + + outRemainder.data[pos + 1] = 0; + outRemainder.data[pos--] = (uint)(dividend % divisor); + //Console.WriteLine(">>>> " + bi1); + } + + outQuotient.dataLength = resultPos; + int j = 0; + for (int i = outQuotient.dataLength - 1; i >= 0; i--, j++) + outQuotient.data[j] = result[i]; + for (; j < maxLength; j++) + outQuotient.data[j] = 0; + + while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) + outQuotient.dataLength--; + + if (outQuotient.dataLength == 0) + outQuotient.dataLength = 1; + + while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) + outRemainder.dataLength--; + } + + + //*********************************************************************** + // Overloading of division operator + //*********************************************************************** + + public static BigInteger operator /(BigInteger bi1, BigInteger bi2) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + + int lastPos = maxLength - 1; + bool divisorNeg = false, dividendNeg = false; + + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1 = -bi1; + dividendNeg = true; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + { + bi2 = -bi2; + divisorNeg = true; + } + + if (bi1 < bi2) + { + return quotient; + } + + else + { + if (bi2.dataLength == 1) + singleByteDivide(bi1, bi2, quotient, remainder); + else + multiByteDivide(bi1, bi2, quotient, remainder); + + if (dividendNeg != divisorNeg) + return -quotient; + + return quotient; + } + } + + + //*********************************************************************** + // Overloading of modulus operator + //*********************************************************************** + + public static BigInteger operator %(BigInteger bi1, BigInteger bi2) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(bi1); + + int lastPos = maxLength - 1; + bool dividendNeg = false; + + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1 = -bi1; + dividendNeg = true; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + bi2 = -bi2; + + if (bi1 < bi2) + { + return remainder; + } + + else + { + if (bi2.dataLength == 1) + singleByteDivide(bi1, bi2, quotient, remainder); + else + multiByteDivide(bi1, bi2, quotient, remainder); + + if (dividendNeg) + return -remainder; + + return remainder; + } + } + + + //*********************************************************************** + // Overloading of bitwise AND operator + //*********************************************************************** + + public static BigInteger operator &(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] & bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of bitwise OR operator + //*********************************************************************** + + public static BigInteger operator |(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] | bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of bitwise XOR operator + //*********************************************************************** + + public static BigInteger operator ^(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] ^ bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Returns max(this, bi) + //*********************************************************************** + + public BigInteger max(BigInteger bi) + { + if (this > bi) + return (new BigInteger(this)); + else + return (new BigInteger(bi)); + } + + + //*********************************************************************** + // Returns min(this, bi) + //*********************************************************************** + + public BigInteger min(BigInteger bi) + { + if (this < bi) + return (new BigInteger(this)); + else + return (new BigInteger(bi)); + + } + + + //*********************************************************************** + // Returns the absolute value + //*********************************************************************** + + public BigInteger abs() + { + if ((this.data[maxLength - 1] & 0x80000000) != 0) + return (-this); + else + return (new BigInteger(this)); + } + + + //*********************************************************************** + // Returns a string representing the BigInteger in base 10. + //*********************************************************************** + + public override string ToString() + { + return ToString(10); + } + + + //*********************************************************************** + // Returns a string representing the BigInteger in sign-and-magnitude + // format in the specified radix. + // + // Example + // ------- + // If the value of BigInteger is -255 in base 10, then + // ToString(16) returns "-FF" + // + //*********************************************************************** + + public string ToString(int radix) + { + if (radix < 2 || radix > 36) + throw (new ArgumentException("Radix must be >= 2 and <= 36")); + + string charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + string result = ""; + + BigInteger a = this; + + bool negative = false; + if ((a.data[maxLength - 1] & 0x80000000) != 0) + { + negative = true; + try + { + a = -a; + } + catch (Exception) { } + } + + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + BigInteger biRadix = new BigInteger(radix); + + if (a.dataLength == 1 && a.data[0] == 0) + result = "0"; + else + { + while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) + { + singleByteDivide(a, biRadix, quotient, remainder); + + if (remainder.data[0] < 10) + result = remainder.data[0] + result; + else + result = charSet[(int)remainder.data[0] - 10] + result; + + a = quotient; + } + if (negative) + result = "-" + result; + } + + return result; + } + + + //*********************************************************************** + // Returns a hex string showing the contains of the BigInteger + // + // Examples + // ------- + // 1) If the value of BigInteger is 255 in base 10, then + // ToHexString() returns "FF" + // + // 2) If the value of BigInteger is -255 in base 10, then + // ToHexString() returns ".....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01", + // which is the 2's complement representation of -255. + // + //*********************************************************************** + + public string ToHexString() + { + string result = data[dataLength - 1].ToString("X"); + + for (int i = dataLength - 2; i >= 0; i--) + { + result += data[i].ToString("X8"); + } + + return result; + } + + + + //*********************************************************************** + // Modulo Exponentiation + //*********************************************************************** + + public BigInteger ModPow(BigInteger exp, BigInteger n) + { + if ((exp.data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive exponents only.")); + + BigInteger resultNum = 1; + BigInteger tempNum; + bool thisNegative = false; + + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative this + { + tempNum = -this % n; + thisNegative = true; + } + else + tempNum = this % n; // ensures (tempNum * tempNum) < b^(2k) + + if ((n.data[maxLength - 1] & 0x80000000) != 0) // negative n + n = -n; + + // calculate constant = b^(2k) / m + BigInteger constant = new BigInteger(); + + int i = n.dataLength << 1; + constant.data[i] = 0x00000001; + constant.dataLength = i + 1; + + constant = constant / n; + int totalBits = exp.bitCount(); + int count = 0; + + // perform squaring and multiply exponentiation + for (int pos = 0; pos < exp.dataLength; pos++) + { + uint mask = 0x01; + //Console.WriteLine("pos = " + pos); + + for (int index = 0; index < 32; index++) + { + if ((exp.data[pos] & mask) != 0) + resultNum = BarrettReduction(resultNum * tempNum, n, constant); + + mask <<= 1; + + tempNum = BarrettReduction(tempNum * tempNum, n, constant); + + + if (tempNum.dataLength == 1 && tempNum.data[0] == 1) + { + if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp + return -resultNum; + return resultNum; + } + count++; + if (count == totalBits) + break; + } + } + + if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp + return -resultNum; + + return resultNum; + } + + + + //*********************************************************************** + // Fast calculation of modular reduction using Barrett's reduction. + // Requires x < b^(2k), where b is the base. In this case, base is + // 2^32 (uint). + // + // Reference [4] + //*********************************************************************** + + private BigInteger BarrettReduction(BigInteger x, BigInteger n, BigInteger constant) + { + int k = n.dataLength, + kPlusOne = k + 1, + kMinusOne = k - 1; + + BigInteger q1 = new BigInteger(); + + // q1 = x / b^(k-1) + for (int i = kMinusOne, j = 0; i < x.dataLength; i++, j++) + q1.data[j] = x.data[i]; + q1.dataLength = x.dataLength - kMinusOne; + if (q1.dataLength <= 0) + q1.dataLength = 1; + + + BigInteger q2 = q1 * constant; + BigInteger q3 = new BigInteger(); + + // q3 = q2 / b^(k+1) + for (int i = kPlusOne, j = 0; i < q2.dataLength; i++, j++) + q3.data[j] = q2.data[i]; + q3.dataLength = q2.dataLength - kPlusOne; + if (q3.dataLength <= 0) + q3.dataLength = 1; + + + // r1 = x mod b^(k+1) + // i.e. keep the lowest (k+1) words + BigInteger r1 = new BigInteger(); + int lengthToCopy = (x.dataLength > kPlusOne) ? kPlusOne : x.dataLength; + for (int i = 0; i < lengthToCopy; i++) + r1.data[i] = x.data[i]; + r1.dataLength = lengthToCopy; + + + // r2 = (q3 * n) mod b^(k+1) + // partial multiplication of q3 and n + + BigInteger r2 = new BigInteger(); + for (int i = 0; i < q3.dataLength; i++) + { + if (q3.data[i] == 0) continue; + + ulong mcarry = 0; + int t = i; + for (int j = 0; j < n.dataLength && t < kPlusOne; j++, t++) + { + // t = i + j + ulong val = ((ulong)q3.data[i] * (ulong)n.data[j]) + + (ulong)r2.data[t] + mcarry; + + r2.data[t] = (uint)(val & 0xFFFFFFFF); + mcarry = (val >> 32); + } + + if (t < kPlusOne) + r2.data[t] = (uint)mcarry; + } + r2.dataLength = kPlusOne; + while (r2.dataLength > 1 && r2.data[r2.dataLength - 1] == 0) + r2.dataLength--; + + r1 -= r2; + if ((r1.data[maxLength - 1] & 0x80000000) != 0) // negative + { + BigInteger val = new BigInteger(); + val.data[kPlusOne] = 0x00000001; + val.dataLength = kPlusOne + 1; + r1 += val; + } + + while (r1 >= n) + r1 -= n; + + return r1; + } + + + //*********************************************************************** + // Returns gcd(this, bi) + //*********************************************************************** + + public BigInteger gcd(BigInteger bi) + { + BigInteger x; + BigInteger y; + + if ((data[maxLength - 1] & 0x80000000) != 0) // negative + x = -this; + else + x = this; + + if ((bi.data[maxLength - 1] & 0x80000000) != 0) // negative + y = -bi; + else + y = bi; + + BigInteger g = y; + + while (x.dataLength > 1 || (x.dataLength == 1 && x.data[0] != 0)) + { + g = x; + x = y % x; + y = g; + } + + return g; + } + + + //*********************************************************************** + // Populates "this" with the specified amount of random bits + //*********************************************************************** + + public void genRandomBits(int bits, Random rand) + { + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + if (dwords > maxLength) + throw (new ArithmeticException("Number of required bits > maxLength.")); + + for (int i = 0; i < dwords; i++) + data[i] = (uint)(rand.NextDouble() * 0x100000000); + + for (int i = dwords; i < maxLength; i++) + data[i] = 0; + + if (remBits != 0) + { + uint mask = (uint)(0x01 << (remBits - 1)); + data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + data[dwords - 1] &= mask; + } + else + data[dwords - 1] |= 0x80000000; + + dataLength = dwords; + + if (dataLength == 0) + dataLength = 1; + } + + + //*********************************************************************** + // Returns the position of the most significant bit in the BigInteger. + // + // Eg. The result is 0, if the value of BigInteger is 0...0000 0000 + // The result is 1, if the value of BigInteger is 0...0000 0001 + // The result is 2, if the value of BigInteger is 0...0000 0010 + // The result is 2, if the value of BigInteger is 0...0000 0011 + // + //*********************************************************************** + + public int bitCount() + { + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + uint value = data[dataLength - 1]; + uint mask = 0x80000000; + int bits = 32; + + while (bits > 0 && (value & mask) == 0) + { + bits--; + mask >>= 1; + } + bits += ((dataLength - 1) << 5); + + return bits; + } + + + //*********************************************************************** + // Probabilistic prime test based on Fermat's little theorem + // + // for any a < p (p does not divide a) if + // a^(p-1) mod p != 1 then p is not prime. + // + // Otherwise, p is probably prime (pseudoprime to the chosen base). + // + // Returns + // ------- + // True if "this" is a pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + // Note - this method is fast but fails for Carmichael numbers except + // when the randomly chosen base is a factor of the number. + // + //*********************************************************************** + + public bool FermatLittleTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + int bits = thisVal.bitCount(); + BigInteger a = new BigInteger(); + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + Random rand = new Random(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = (int)(rand.NextDouble() * bits); + + a.genRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + // calculate a^(p-1) mod p + BigInteger expResult = a.ModPow(p_sub1, thisVal); + + int resultLen = expResult.dataLength; + + // is NOT prime is a^(p-1) mod p != 1 + + if (resultLen > 1 || (resultLen == 1 && expResult.data[0] != 1)) + { + //Console.WriteLine("a = " + a.ToString()); + return false; + } + } + + return true; + } + + + //*********************************************************************** + // Probabilistic prime test based on Rabin-Miller's + // + // for any p > 0 with p - 1 = 2^s * t + // + // p is probably prime (strong pseudoprime) if for any a < p, + // 1) a^t mod p = 1 or + // 2) a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + // + // Otherwise, p is composite. + // + // Returns + // ------- + // True if "this" is a strong pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + //*********************************************************************** + + public bool RabinMillerTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + // calculate values of s and t + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + int s = 0; + + for (int index = 0; index < p_sub1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_sub1.data[index] & mask) != 0) + { + index = p_sub1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_sub1 >> s; + + int bits = thisVal.bitCount(); + BigInteger a = new BigInteger(); + Random rand = new Random(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = (int)(rand.NextDouble() * bits); + + a.genRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + BigInteger b = a.ModPow(t, thisVal); + + /* + Console.WriteLine("a = " + a.ToString(10)); + Console.WriteLine("b = " + b.ToString(10)); + Console.WriteLine("t = " + t.ToString(10)); + Console.WriteLine("s = " + s); + */ + + bool result = false; + + if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 + result = true; + + for (int j = 0; result == false && j < s; j++) + { + if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + { + result = true; + break; + } + + b = (b * b) % thisVal; + } + + if (result == false) + return false; + } + return true; + } + + + //*********************************************************************** + // Probabilistic prime test based on Solovay-Strassen (Euler Criterion) + // + // p is probably prime if for any a < p (a is not multiple of p), + // a^((p-1)/2) mod p = J(a, p) + // + // where J is the Jacobi symbol. + // + // Otherwise, p is composite. + // + // Returns + // ------- + // True if "this" is a Euler pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + //*********************************************************************** + + public bool SolovayStrassenTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + int bits = thisVal.bitCount(); + BigInteger a = new BigInteger(); + BigInteger p_sub1 = thisVal - 1; + BigInteger p_sub1_shift = p_sub1 >> 1; + + Random rand = new Random(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = (int)(rand.NextDouble() * bits); + + a.genRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + // calculate a^((p-1)/2) mod p + + BigInteger expResult = a.ModPow(p_sub1_shift, thisVal); + if (expResult == p_sub1) + expResult = -1; + + // calculate Jacobi symbol + BigInteger jacob = Jacobi(a, thisVal); + + //Console.WriteLine("a = " + a.ToString(10) + " b = " + thisVal.ToString(10)); + //Console.WriteLine("expResult = " + expResult.ToString(10) + " Jacob = " + jacob.ToString(10)); + + // if they are different then it is not prime + if (expResult != jacob) + return false; + } + + return true; + } + + + //*********************************************************************** + // Implementation of the Lucas Strong Pseudo Prime test. + // + // Let n be an odd number with gcd(n,D) = 1, and n - J(D, n) = 2^s * d + // with d odd and s >= 0. + // + // If Ud mod n = 0 or V2^r*d mod n = 0 for some 0 <= r < s, then n + // is a strong Lucas pseudoprime with parameters (P, Q). We select + // P and Q based on Selfridge. + // + // Returns True if number is a strong Lucus pseudo prime. + // Otherwise, returns False indicating that number is composite. + //*********************************************************************** + + public bool LucasStrongTest() + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + return LucasStrongTestHelper(thisVal); + } + + + private bool LucasStrongTestHelper(BigInteger thisVal) + { + // Do the test (selects D based on Selfridge) + // Let D be the first element of the sequence + // 5, -7, 9, -11, 13, ... for which J(D,n) = -1 + // Let P = 1, Q = (1-D) / 4 + + long D = 5, sign = -1, dCount = 0; + bool done = false; + + while (!done) + { + int Jresult = BigInteger.Jacobi(D, thisVal); + + if (Jresult == -1) + done = true; // J(D, this) = 1 + else + { + if (Jresult == 0 && Math.Abs(D) < thisVal) // divisor found + return false; + + if (dCount == 20) + { + // check for square + BigInteger root = thisVal.sqrt(); + if (root * root == thisVal) + return false; + } + + //Console.WriteLine(D); + D = (Math.Abs(D) + 2) * sign; + sign = -sign; + } + dCount++; + } + + long Q = (1 - D) >> 2; + + /* + Console.WriteLine("D = " + D); + Console.WriteLine("Q = " + Q); + Console.WriteLine("(n,D) = " + thisVal.gcd(D)); + Console.WriteLine("(n,Q) = " + thisVal.gcd(Q)); + Console.WriteLine("J(D|n) = " + BigInteger.Jacobi(D, thisVal)); + */ + + BigInteger p_add1 = thisVal + 1; + int s = 0; + + for (int index = 0; index < p_add1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_add1.data[index] & mask) != 0) + { + index = p_add1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_add1 >> s; + + // calculate constant = b^(2k) / m + // for Barrett Reduction + BigInteger constant = new BigInteger(); + + int nLen = thisVal.dataLength << 1; + constant.data[nLen] = 0x00000001; + constant.dataLength = nLen + 1; + + constant = constant / thisVal; + + BigInteger[] lucas = LucasSequenceHelper(1, Q, t, thisVal, constant, 0); + bool isPrime = false; + + if ((lucas[0].dataLength == 1 && lucas[0].data[0] == 0) || + (lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) + { + // u(t) = 0 or V(t) = 0 + isPrime = true; + } + + for (int i = 1; i < s; i++) + { + if (!isPrime) + { + // doubling of index + lucas[1] = thisVal.BarrettReduction(lucas[1] * lucas[1], thisVal, constant); + lucas[1] = (lucas[1] - (lucas[2] << 1)) % thisVal; + + //lucas[1] = ((lucas[1] * lucas[1]) - (lucas[2] << 1)) % thisVal; + + if ((lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) + isPrime = true; + } + + lucas[2] = thisVal.BarrettReduction(lucas[2] * lucas[2], thisVal, constant); //Q^k + } + + + if (isPrime) // additional checks for composite numbers + { + // If n is prime and gcd(n, Q) == 1, then + // Q^((n+1)/2) = Q * Q^((n-1)/2) is congruent to (Q * J(Q, n)) mod n + + BigInteger g = thisVal.gcd(Q); + if (g.dataLength == 1 && g.data[0] == 1) // gcd(this, Q) == 1 + { + if ((lucas[2].data[maxLength - 1] & 0x80000000) != 0) + lucas[2] += thisVal; + + BigInteger temp = (Q * BigInteger.Jacobi(Q, thisVal)) % thisVal; + if ((temp.data[maxLength - 1] & 0x80000000) != 0) + temp += thisVal; + + if (lucas[2] != temp) + isPrime = false; + } + } + + return isPrime; + } + + + //*********************************************************************** + // Determines whether a number is probably prime, using the Rabin-Miller's + // test. Before applying the test, the number is tested for divisibility + // by primes < 2000 + // + // Returns true if number is probably prime. + //*********************************************************************** + + public bool isProbablePrime(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + + // test for divisibility by primes < 2000 + for (int p = 0; p < primesBelow2000.Length; p++) + { + BigInteger divisor = primesBelow2000[p]; + + if (divisor >= thisVal) + break; + + BigInteger resultNum = thisVal % divisor; + if (resultNum.IntValue() == 0) + { + /* + Console.WriteLine("Not prime! Divisible by {0}\n", + primesBelow2000[p]); + */ + return false; + } + } + + if (thisVal.RabinMillerTest(confidence)) + return true; + else + { + //Console.WriteLine("Not prime! Failed primality test\n"); + return false; + } + } + + + //*********************************************************************** + // Determines whether this BigInteger is probably prime using a + // combination of base 2 strong pseudoprime test and Lucas strong + // pseudoprime test. + // + // The sequence of the primality test is as follows, + // + // 1) Trial divisions are carried out using prime numbers below 2000. + // if any of the primes divides this BigInteger, then it is not prime. + // + // 2) Perform base 2 strong pseudoprime test. If this BigInteger is a + // base 2 strong pseudoprime, proceed on to the next step. + // + // 3) Perform strong Lucas pseudoprime test. + // + // Returns True if this BigInteger is both a base 2 strong pseudoprime + // and a strong Lucas pseudoprime. + // + // For a detailed discussion of this primality test, see [6]. + // + //*********************************************************************** + + public bool isProbablePrime() + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + // test for divisibility by primes < 2000 + for (int p = 0; p < primesBelow2000.Length; p++) + { + BigInteger divisor = primesBelow2000[p]; + + if (divisor >= thisVal) + break; + + BigInteger resultNum = thisVal % divisor; + if (resultNum.IntValue() == 0) + { + //Console.WriteLine("Not prime! Divisible by {0}\n", + // primesBelow2000[p]); + + return false; + } + } + + // Perform BASE 2 Rabin-Miller Test + + // calculate values of s and t + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + int s = 0; + + for (int index = 0; index < p_sub1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_sub1.data[index] & mask) != 0) + { + index = p_sub1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_sub1 >> s; + + int bits = thisVal.bitCount(); + BigInteger a = 2; + + // b = a^t mod p + BigInteger b = a.ModPow(t, thisVal); + bool result = false; + + if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 + result = true; + + for (int j = 0; result == false && j < s; j++) + { + if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + { + result = true; + break; + } + + b = (b * b) % thisVal; + } + + // if number is strong pseudoprime to base 2, then do a strong lucas test + if (result) + result = LucasStrongTestHelper(thisVal); + + return result; + } + + + + //*********************************************************************** + // Returns the lowest 4 bytes of the BigInteger as an int. + //*********************************************************************** + + public int IntValue() + { + return (int)data[0]; + } + + + //*********************************************************************** + // Returns the lowest 8 bytes of the BigInteger as a long. + //*********************************************************************** + + public long LongValue() + { + long val = 0; + + val = (long)data[0]; + try + { // exception if maxLength = 1 + val |= (long)data[1] << 32; + } + catch (Exception) + { + if ((data[0] & 0x80000000) != 0) // negative + val = (int)data[0]; + } + + return val; + } + + + //*********************************************************************** + // Computes the Jacobi Symbol for a and b. + // Algorithm adapted from [3] and [4] with some optimizations + //*********************************************************************** + + public static int Jacobi(BigInteger a, BigInteger b) + { + // Jacobi defined only for odd integers + if ((b.data[0] & 0x1) == 0) + throw (new ArgumentException("Jacobi defined only for odd integers.")); + + if (a >= b) a %= b; + if (a.dataLength == 1 && a.data[0] == 0) return 0; // a == 0 + if (a.dataLength == 1 && a.data[0] == 1) return 1; // a == 1 + + if (a < 0) + { + if ((((b - 1).data[0]) & 0x2) == 0) //if( (((b-1) >> 1).data[0] & 0x1) == 0) + return Jacobi(-a, b); + else + return -Jacobi(-a, b); + } + + int e = 0; + for (int index = 0; index < a.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((a.data[index] & mask) != 0) + { + index = a.dataLength; // to break the outer loop + break; + } + mask <<= 1; + e++; + } + } + + BigInteger a1 = a >> e; + + int s = 1; + if ((e & 0x1) != 0 && ((b.data[0] & 0x7) == 3 || (b.data[0] & 0x7) == 5)) + s = -1; + + if ((b.data[0] & 0x3) == 3 && (a1.data[0] & 0x3) == 3) + s = -s; + + if (a1.dataLength == 1 && a1.data[0] == 1) + return s; + else + return (s * Jacobi(b % a1, a1)); + } + + + + //*********************************************************************** + // Generates a positive BigInteger that is probably prime. + //*********************************************************************** + + public static BigInteger genPseudoPrime(int bits, int confidence, Random rand) + { + BigInteger result = new BigInteger(); + bool done = false; + + while (!done) + { + result.genRandomBits(bits, rand); + result.data[0] |= 0x01; // make it odd + + // prime test + done = result.isProbablePrime(confidence); + } + return result; + } + + + //*********************************************************************** + // Generates a random number with the specified number of bits such + // that gcd(number, this) = 1 + //*********************************************************************** + + public BigInteger genCoPrime(int bits, Random rand) + { + bool done = false; + BigInteger result = new BigInteger(); + + while (!done) + { + result.genRandomBits(bits, rand); + //Console.WriteLine(result.ToString(16)); + + // gcd test + BigInteger g = result.gcd(this); + if (g.dataLength == 1 && g.data[0] == 1) + done = true; + } + + return result; + } + + + //*********************************************************************** + // Returns the modulo inverse of this. Throws ArithmeticException if + // the inverse does not exist. (i.e. gcd(this, modulus) != 1) + //*********************************************************************** + + public BigInteger modInverse(BigInteger modulus) + { + BigInteger[] p = { 0, 1 }; + BigInteger[] q = new BigInteger[2]; // quotients + BigInteger[] r = { 0, 0 }; // remainders + + int step = 0; + + BigInteger a = modulus; + BigInteger b = this; + + while (b.dataLength > 1 || (b.dataLength == 1 && b.data[0] != 0)) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + + if (step > 1) + { + BigInteger pval = (p[0] - (p[1] * q[0])) % modulus; + p[0] = p[1]; + p[1] = pval; + } + + if (b.dataLength == 1) + singleByteDivide(a, b, quotient, remainder); + else + multiByteDivide(a, b, quotient, remainder); + + /* + Console.WriteLine(quotient.dataLength); + Console.WriteLine("{0} = {1}({2}) + {3} p = {4}", a.ToString(10), + b.ToString(10), quotient.ToString(10), remainder.ToString(10), + p[1].ToString(10)); + */ + + q[0] = q[1]; + r[0] = r[1]; + q[1] = quotient; r[1] = remainder; + + a = b; + b = remainder; + + step++; + } + + if (r[0].dataLength > 1 || (r[0].dataLength == 1 && r[0].data[0] != 1)) + throw (new ArithmeticException("No inverse!")); + + BigInteger result = ((p[0] - (p[1] * q[0])) % modulus); + + if ((result.data[maxLength - 1] & 0x80000000) != 0) + result += modulus; // get the least positive modulus + + return result; + } + + + //*********************************************************************** + // Returns the value of the BigInteger as a byte array. The lowest + // index contains the MSB. + //*********************************************************************** + + public byte[] GetBytes() + { + return GetBytes(0); + } + + public byte[] GetBytes(int numBytes) + { + int numBits = bitCount(); + + + int m = numBits >> 3; + if ((numBits & 0x7) != 0) + m++; + + if (numBytes < m) numBytes = m; + + byte[] result = new byte[numBytes]; + + //Console.WriteLine(result.Length); + + int pos = 0; + uint tempVal, val = data[dataLength - 1]; + + if ((tempVal = (val >> 24 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val >> 16 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val >> 8 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + + for (int i = dataLength - 2; i >= 0; i--, pos += 4) + { + val = data[i]; + result[pos + 3] = (byte)(val & 0xFF); + val >>= 8; + result[pos + 2] = (byte)(val & 0xFF); + val >>= 8; + result[pos + 1] = (byte)(val & 0xFF); + val >>= 8; + result[pos] = (byte)(val & 0xFF); + } + + return result; + } + + + //*********************************************************************** + // Sets the value of the specified bit to 1 + // The Least Significant Bit position is 0. + //*********************************************************************** + + public void setBit(uint bitNum) + { + uint bytePos = bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + this.data[bytePos] |= mask; + + if (bytePos >= this.dataLength) + this.dataLength = (int)bytePos + 1; + } + + + //*********************************************************************** + // Sets the value of the specified bit to 0 + // The Least Significant Bit position is 0. + //*********************************************************************** + + public void unsetBit(uint bitNum) + { + uint bytePos = bitNum >> 5; + + if (bytePos < this.dataLength) + { + byte bitPos = (byte)(bitNum & 0x1F); + + uint mask = (uint)1 << bitPos; + uint mask2 = 0xFFFFFFFF ^ mask; + + this.data[bytePos] &= mask2; + + if (this.dataLength > 1 && this.data[this.dataLength - 1] == 0) + this.dataLength--; + } + } + + + //*********************************************************************** + // Returns a value that is equivalent to the integer square root + // of the BigInteger. + // + // The integer square root of "this" is defined as the largest integer n + // such that (n * n) <= this + // + //*********************************************************************** + + public BigInteger sqrt() + { + uint numBits = (uint)this.bitCount(); + + if ((numBits & 0x1) != 0) // odd number of bits + numBits = (numBits >> 1) + 1; + else + numBits = (numBits >> 1); + + uint bytePos = numBits >> 5; + byte bitPos = (byte)(numBits & 0x1F); + + uint mask; + + BigInteger result = new BigInteger(); + if (bitPos == 0) + mask = 0x80000000; + else + { + mask = (uint)1 << bitPos; + bytePos++; + } + result.dataLength = (int)bytePos; + + for (int i = (int)bytePos - 1; i >= 0; i--) + { + while (mask != 0) + { + // guess + result.data[i] ^= mask; + + // undo the guess if its square is larger than this + if ((result * result) > this) + result.data[i] ^= mask; + + mask >>= 1; + } + mask = 0x80000000; + } + return result; + } + + + //*********************************************************************** + // Returns the k_th number in the Lucas Sequence reduced modulo n. + // + // Uses index doubling to speed up the process. For example, to calculate V(k), + // we maintain two numbers in the sequence V(n) and V(n+1). + // + // To obtain V(2n), we use the identity + // V(2n) = (V(n) * V(n)) - (2 * Q^n) + // To obtain V(2n+1), we first write it as + // V(2n+1) = V((n+1) + n) + // and use the identity + // V(m+n) = V(m) * V(n) - Q * V(m-n) + // Hence, + // V((n+1) + n) = V(n+1) * V(n) - Q^n * V((n+1) - n) + // = V(n+1) * V(n) - Q^n * V(1) + // = V(n+1) * V(n) - Q^n * P + // + // We use k in its binary expansion and perform index doubling for each + // bit position. For each bit position that is set, we perform an + // index doubling followed by an index addition. This means that for V(n), + // we need to update it to V(2n+1). For V(n+1), we need to update it to + // V((2n+1)+1) = V(2*(n+1)) + // + // This function returns + // [0] = U(k) + // [1] = V(k) + // [2] = Q^n + // + // Where U(0) = 0 % n, U(1) = 1 % n + // V(0) = 2 % n, V(1) = P % n + //*********************************************************************** + + public static BigInteger[] LucasSequence(BigInteger P, BigInteger Q, + BigInteger k, BigInteger n) + { + if (k.dataLength == 1 && k.data[0] == 0) + { + BigInteger[] result = new BigInteger[3]; + + result[0] = 0; result[1] = 2 % n; result[2] = 1 % n; + return result; + } + + // calculate constant = b^(2k) / m + // for Barrett Reduction + BigInteger constant = new BigInteger(); + + int nLen = n.dataLength << 1; + constant.data[nLen] = 0x00000001; + constant.dataLength = nLen + 1; + + constant = constant / n; + + // calculate values of s and t + int s = 0; + + for (int index = 0; index < k.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((k.data[index] & mask) != 0) + { + index = k.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = k >> s; + + //Console.WriteLine("s = " + s + " t = " + t); + return LucasSequenceHelper(P, Q, t, n, constant, s); + } + + + //*********************************************************************** + // Performs the calculation of the kth term in the Lucas Sequence. + // For details of the algorithm, see reference [9]. + // + // k must be odd. i.e LSB == 1 + //*********************************************************************** + + private static BigInteger[] LucasSequenceHelper(BigInteger P, BigInteger Q, + BigInteger k, BigInteger n, + BigInteger constant, int s) + { + BigInteger[] result = new BigInteger[3]; + + if ((k.data[0] & 0x00000001) == 0) + throw (new ArgumentException("Argument k must be odd.")); + + int numbits = k.bitCount(); + uint mask = (uint)0x1 << ((numbits & 0x1F) - 1); + + // v = v0, v1 = v1, u1 = u1, Q_k = Q^0 + + BigInteger v = 2 % n, Q_k = 1 % n, + v1 = P % n, u1 = Q_k; + bool flag = true; + + for (int i = k.dataLength - 1; i >= 0; i--) // iterate on the binary expansion of k + { + //Console.WriteLine("round"); + while (mask != 0) + { + if (i == 0 && mask == 0x00000001) // last bit + break; + + if ((k.data[i] & mask) != 0) // bit is set + { + // index doubling with addition + + u1 = (u1 * v1) % n; + + v = ((v * v1) - (P * Q_k)) % n; + v1 = n.BarrettReduction(v1 * v1, n, constant); + v1 = (v1 - ((Q_k * Q) << 1)) % n; + + if (flag) + flag = false; + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + + Q_k = (Q_k * Q) % n; + } + else + { + // index doubling + u1 = ((u1 * v) - Q_k) % n; + + v1 = ((v * v1) - (P * Q_k)) % n; + v = n.BarrettReduction(v * v, n, constant); + v = (v - (Q_k << 1)) % n; + + if (flag) + { + Q_k = Q % n; + flag = false; + } + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + } + + mask >>= 1; + } + mask = 0x80000000; + } + + // at this point u1 = u(n+1) and v = v(n) + // since the last bit always 1, we need to transform u1 to u(2n+1) and v to v(2n+1) + + u1 = ((u1 * v) - Q_k) % n; + v = ((v * v1) - (P * Q_k)) % n; + if (flag) + flag = false; + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + + Q_k = (Q_k * Q) % n; + + + for (int i = 0; i < s; i++) + { + // index doubling + u1 = (u1 * v) % n; + v = ((v * v) - (Q_k << 1)) % n; + + if (flag) + { + Q_k = Q % n; + flag = false; + } + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + } + + result[0] = u1; + result[1] = v; + result[2] = Q_k; + + return result; + } + +} diff --git a/Common/Cryptography/ClientAuth.cs b/Common/Cryptography/ClientAuth.cs new file mode 100644 index 0000000..ae1cb98 --- /dev/null +++ b/Common/Cryptography/ClientAuth.cs @@ -0,0 +1,157 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Cryptography +{ + public static class ClientAuth + { + public static string Password { get; set; } + public static bool Encode { get; set; } = false; + public static byte[] SS_Hash { get; private set; } + public static byte[] Key { get; private set; } = new byte[4]; + + + public static readonly byte[] Reconnect_Challenge = + { + 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 + }; + + + #region Private Vars + static readonly byte[] N = + { + 0x89, 0x4B, 0x64, 0x5E, 0x89, 0xE1, 0x53, 0x5B, + 0xBD, 0xAD, 0x5B, 0x8B, 0x29, 0x06, 0x50, 0x53, + 0x08, 0x01, 0xB1, 0x8E, 0xBF, 0xBF, 0x5E, 0x8F, + 0xAB, 0x3C, 0x82, 0x87, 0x2A, 0x3E, 0x9B, 0xB7 + }; + + static readonly byte[] Salt = + { + 0xAD, 0xD0, 0x3A, 0x31, 0xD2, 0x71, 0x14, 0x46, + 0x75, 0xF2, 0x70, 0x7E, 0x50, 0x26, 0xB6, 0xD2, + 0xF1, 0x86, 0x59, 0x99, 0x76, 0x02, 0x50, 0xAA, + 0xB9, 0x45, 0xE0, 0x9E, 0xDD, 0x2A, 0xA3, 0x45, + }; + + static readonly byte[] RN = N.Reverse().ToArray(); + + static BigInteger B; + static BigInteger V; + static byte[] RB; + static BigInteger K; + static BigInteger G; + static byte[] BUsername; + #endregion + + public static void Clear() + { + Key = new byte[4]; + Encode = false; + } + + public static byte[] LogonChallenge(IPacketReader packet) + { + packet.ReadBytes(32); //Skip to username + BUsername = packet.ReadBytes(packet.ReadByte()); //Read username + string username = Encoding.ASCII.GetString(BUsername); + + byte[] x; + using (SHA1 sha = new SHA1CryptoServiceProvider()) + { + byte[] user = Encoding.ASCII.GetBytes(username.ToUpper() + ":" + Password.ToUpper()); + byte[] res = Salt.Concat(sha.ComputeHash(user, 0, user.Length)).ToArray(); + x = sha.ComputeHash(res, 0, res.Length).Reverse().ToArray(); + } + + byte[] b = new byte[20]; + new Random().NextBytes(b); + RB = b.Reverse().ToArray(); + + G = new BigInteger(new byte[] { 7 }); + V = G.ModPow(new BigInteger(x), new BigInteger(RN)); + + K = new BigInteger(new byte[] { 3 }); + BigInteger temp = (K * V) + G.ModPow(new BigInteger(RB), new BigInteger(RN)); + B = temp % new BigInteger(RN); + + IEnumerable result = new byte[3]; //Opcode, 0, Success + result = result.Concat(B.GetBytes(32).Reverse()); + result = result.Concat(new byte[] { 1, 7, 32 }); //1, G, 32 + result = result.Concat(N); + result = result.Concat(Salt); + result = result.Concat(new byte[16]); //unknown, Security Flag + return result.ToArray(); + } + + public static byte[] LogonProof(IPacketReader packet) + { + byte[] A = packet.ReadBytes(32); + byte[] kM1 = packet.ReadBytes(20); + byte[] rA = A.Reverse().ToArray(); + byte[] AB = A.Concat(B.GetBytes(32).Reverse()).ToArray(); + + SHA1 sha = new SHA1CryptoServiceProvider(); + byte[] rU = sha.ComputeHash(AB).Reverse().ToArray(); + + //SS_Hash + BigInteger s = V.ModPow(new BigInteger(rU), new BigInteger(RN)); + s *= new BigInteger(rA); + s = s.ModPow(new BigInteger(RB), new BigInteger(RN)); + + byte[] S1 = new byte[16]; + byte[] S2 = new byte[16]; + byte[] S = s.GetBytes(32); + byte[] rS = S.Reverse().ToArray(); + for (int t = 0; t < 16; t++) + { + S1[t] = rS[t * 2]; + S2[t] = rS[(t * 2) + 1]; + } + + byte[] hashS1 = sha.ComputeHash(S1); + byte[] hashS2 = sha.ComputeHash(S2); + SS_Hash = new byte[hashS1.Length + hashS2.Length]; + for (int t = 0; t < hashS1.Length; t++) + { + SS_Hash[t * 2] = hashS1[t]; + SS_Hash[(t * 2) + 1] = hashS2[t]; + } + + //calc M1 + byte[] M1; + byte[] NHash = sha.ComputeHash(N); + byte[] GHash = sha.ComputeHash(G.GetBytes()); + byte[] NG_Hash = new byte[20]; + for (int t = 0; t < 20; t++) + NG_Hash[t] = (byte)(NHash[t] ^ GHash[t]); + + IEnumerable tmp = NG_Hash.Concat(sha.ComputeHash(BUsername)); + tmp = tmp.Concat(Salt); + tmp = tmp.Concat(A); + tmp = tmp.Concat(B.GetBytes(32).Reverse()); + tmp = tmp.Concat(SS_Hash); + M1 = sha.ComputeHash(tmp.ToArray()); + + //calc M2 + byte[] M2; + tmp = A.Concat(M1); + tmp = tmp.Concat(SS_Hash); + M2 = sha.ComputeHash(tmp.ToArray()); + + sha.Dispose(); + + IEnumerable result = new byte[] { 1, 0 }; + result = result.Concat(M2); + result = result.Concat(new byte[4]); + return result.ToArray(); + } + } +} diff --git a/Common/Extensions/CharacterExtensions.cs b/Common/Extensions/CharacterExtensions.cs new file mode 100644 index 0000000..36aedee --- /dev/null +++ b/Common/Extensions/CharacterExtensions.cs @@ -0,0 +1,119 @@ +using Common.Constants; +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Extensions +{ + public static class CharacterExtensions + { + public static uint GetDisplayId(this ICharacter character) + { + bool male = (character.Gender == 0); + + switch (character.Race) + { + case 1: + return (uint)(male ? 0x31 : 0x32); + case 2: + return (uint)(male ? 0x33 : 0x34); + case 3: + return (uint)(male ? 0x35 : 0x36); + case 4: + return (uint)(male ? 0x37 : 0x38); + case 5: + return (uint)(male ? 0x39 : 0x3A); + case 6: + return (uint)(male ? 0x3B : 0x3C); + case 7: + return (uint)(male ? 0x61B : 0x61C); + case 8: + return (uint)(male ? 0x5C6 : 0x5C7); + case 10: + return (uint)(male ? 0x3C74 : 0x3C73); + case 11: + return (uint)(male ? 0x3EFD : 0x3EFE); + default: + return (uint)(male ? 0x31 : 0x32); //Default to human + } + } + + public static void SetPowerType(this ICharacter character, bool hunterFocus = false) + { + switch ((Classes)character.Class) + { + case Classes.WARRIOR: + character.PowerType = (byte)PowerTypes.RAGE; + break; + case Classes.ROGUE: + character.PowerType = (byte)PowerTypes.ENERGY; + break; + default: + character.PowerType = (byte)PowerTypes.MANA; + break; + } + + if (hunterFocus && (Classes)character.Class == Classes.HUNTER) + character.PowerType = (byte)PowerTypes.FOCUS; + } + + public static void SetField(this ICharacter character, int field, object value, ref SortedDictionary fieldData, ref byte[] maskArray) + { + switch (Type.GetTypeCode(value.GetType())) + { + case TypeCode.Byte: + fieldData.Add(field, new byte[] { (byte)value }); + break; + case TypeCode.Single: + fieldData.Add(field, BitConverter.GetBytes((float)value)); + break; + case TypeCode.UInt16: + fieldData.Add(field, BitConverter.GetBytes((ushort)value)); + break; + case TypeCode.UInt32: + fieldData.Add(field, BitConverter.GetBytes((uint)value)); + break; + case TypeCode.UInt64: + fieldData.Add(field, BitConverter.GetBytes((ulong)value)); + break; + case TypeCode.Int16: + fieldData.Add(field, BitConverter.GetBytes((short)value)); + break; + case TypeCode.Int32: + fieldData.Add(field, BitConverter.GetBytes((int)value)); + break; + case TypeCode.Int64: + fieldData.Add(field, BitConverter.GetBytes((long)value)); + break; + default: + throw new NotSupportedException(); + } + + for (int i = 0; i < (fieldData[field].Length / 4); i++) + maskArray[field / 8] |= (byte)(1 << ((field + i) % 8)); + } + + public static IPacketWriter BuildMessage(this ICharacter character, IPacketWriter message, string text) + { + message.WriteUInt8(9); //System Message + message.WriteUInt32(0); //Language: General + message.WriteUInt64(0); + message.WriteString(text); + message.WriteUInt8(0); + return message; + } + + public static IPacketWriter BuildForceSpeed(this ICharacter character, IPacketWriter writer, float modifier) + { + float maxmod = 8f; + modifier *= (10f / maxmod); + modifier = Math.Min(Math.Max(modifier, 1f), maxmod); //Min 1 Max 8 + + writer.WriteFloat(modifier * 7f); + return writer; + } + } +} diff --git a/Common/Extensions/WorldExtensions.cs b/Common/Extensions/WorldExtensions.cs new file mode 100644 index 0000000..1ba805e --- /dev/null +++ b/Common/Extensions/WorldExtensions.cs @@ -0,0 +1,24 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Extensions +{ + public static class WorldExtensions + { + public static int GetTime(this IWorldHandler worldhandler) + { + DateTime now = DateTime.Now; + int year = (now.Year - 2000) << 24; + int month = (now.Month - 1) << 20; + int day = (now.Day - 1) << 14; + int dow = (int)now.DayOfWeek << 11; + int hour = now.Hour << 6; + + return now.Minute + hour + dow + day + month + year; + } + } +} diff --git a/Common/Interfaces/Handlers/IAuthHandler.cs b/Common/Interfaces/Handlers/IAuthHandler.cs new file mode 100644 index 0000000..ba45278 --- /dev/null +++ b/Common/Interfaces/Handlers/IAuthHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.Net.Sockets; + +namespace Common.Interfaces.Handlers +{ + public interface IAuthHandler + { + IPacketWriter HandleAuthChallenge(); + IPacketWriter HandleRedirect(); + void HandleRealmList(Socket socket); + void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager); + void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager); + } +} diff --git a/Common/Interfaces/Handlers/ICharHandler.cs b/Common/Interfaces/Handlers/ICharHandler.cs new file mode 100644 index 0000000..f0c2bdf --- /dev/null +++ b/Common/Interfaces/Handlers/ICharHandler.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Common.Interfaces.Handlers +{ + public interface ICharHandler + { + void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager); + void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager); + void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager); + void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager); + void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager); + void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager); + void HandleStandState(ref IPacketReader packet, ref IWorldManager manager); + void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager); + } +} diff --git a/Common/Interfaces/Handlers/IWorldHandler.cs b/Common/Interfaces/Handlers/IWorldHandler.cs new file mode 100644 index 0000000..ba0fdd6 --- /dev/null +++ b/Common/Interfaces/Handlers/IWorldHandler.cs @@ -0,0 +1,16 @@ +using System; + +namespace Common.Interfaces.Handlers +{ + public interface IWorldHandler + { + void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager); + void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager); + void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager); + void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager); + void HandlePing(ref IPacketReader packet, ref IWorldManager manager); + void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager); + void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager); + void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager); + } +} diff --git a/Common/Interfaces/ICharacter.cs b/Common/Interfaces/ICharacter.cs new file mode 100644 index 0000000..6f8d348 --- /dev/null +++ b/Common/Interfaces/ICharacter.cs @@ -0,0 +1,47 @@ +using Common.Constants; +using Common.Structs; +using System; + +namespace Common.Interfaces +{ + public interface ICharacter + { + int Build { get; set; } + + ulong Guid { get; set; } + string Name { get; set; } + byte Race { get; set; } + byte Class { get; set; } + byte Gender { get; set; } + byte Skin { get; set; } + byte Face { get; set; } + byte HairStyle { get; set; } + byte HairColor { get; set; } + byte FacialHair { get; set; } + uint Level { get; set; } + uint Zone { get; set; } + Location Location { get; set; } + bool IsOnline { get; set; } + uint Health { get; set; } + uint Mana { get; set; } + uint Rage { get; set; } + uint Focus { get; set; } + uint Energy { get; set; } + uint Strength { get; set; } + uint Agility { get; set; } + uint Stamina { get; set; } + uint Intellect { get; set; } + uint Spirit { get; set; } + byte PowerType { get; set; } + StandState StandState { get; set; } + bool IsTeleporting { get; set; } + uint DisplayId { get; set; } + uint MountDisplayId { get; set; } + + void Demorph(); + void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager); + IPacketWriter BuildForceSpeed(float modifier, bool swim = false); + IPacketWriter BuildMessage(string text); + IPacketWriter BuildUpdate(); + } +} diff --git a/Common/Interfaces/IOpcodes.cs b/Common/Interfaces/IOpcodes.cs new file mode 100644 index 0000000..c0b0e2e --- /dev/null +++ b/Common/Interfaces/IOpcodes.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Common.Interfaces +{ + public interface IOpcodes + { + Opcodes this[uint id] { get; } + uint this[Opcodes opcode] { get; } + bool OpcodeExists(uint opcode); + } +} diff --git a/Common/Interfaces/IPacketReader.cs b/Common/Interfaces/IPacketReader.cs new file mode 100644 index 0000000..801a55d --- /dev/null +++ b/Common/Interfaces/IPacketReader.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Common.Interfaces +{ + public interface IPacketReader + { + uint Opcode { get; set; } + uint Size { get; set; } + long Position { get; set; } + + byte ReadByte(); + sbyte ReadInt8(); + short ReadInt16(); + int ReadInt32(); + long ReadInt64(); + byte ReadUInt8(); + ushort ReadUInt16(); + uint ReadUInt32(); + ulong ReadUInt64(); + float ReadFloat(); + double ReadDouble(); + string ReadString(byte terminator = 0); + string ReadString(); + byte[] ReadBytes(int count); + byte[] ReadToEnd(); + string ReadStringFromBytes(int count); + void SkipBytes(int count); + } +} diff --git a/Common/Interfaces/IPacketWriter.cs b/Common/Interfaces/IPacketWriter.cs new file mode 100644 index 0000000..4309544 --- /dev/null +++ b/Common/Interfaces/IPacketWriter.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Common.Interfaces +{ + public interface IPacketWriter + { + string Name { get; set; } + uint Opcode { get; set; } + uint Size { get; set; } + + + void WritePacketHeader(uint opcode); + byte[] ReadDataToSend(); + + + void WriteInt8(sbyte data); + void WriteInt16(short data); + void WriteInt32(int data); + void WriteInt64(long data); + void WriteUInt8(byte data); + void WriteUInt16(ushort data); + void WriteUInt32(uint data); + void WriteUInt64(ulong data); + void WriteFloat(float data); + void WriteDouble(double data); + void WriteString(string data); + void WriteBytes(byte[] data); + } +} diff --git a/Common/Interfaces/ISandbox.cs b/Common/Interfaces/ISandbox.cs new file mode 100644 index 0000000..29e3608 --- /dev/null +++ b/Common/Interfaces/ISandbox.cs @@ -0,0 +1,23 @@ +using Common.Interfaces.Handlers; +using Common.Structs; +using System; + +namespace Common.Interfaces +{ + public interface ISandbox + { + string RealmName { get; set; } + int Build { get; set; } + int RealmPort { get; set; } + int RedirectPort { get; set; } + int WorldPort { get; set; } + + IOpcodes Opcodes { get; set; } + IAuthHandler AuthHandler { get; set; } + ICharHandler CharHandler { get; set; } + IWorldHandler WorldHandler { get; set; } + + IPacketReader ReadPacket(byte[] data, bool parse = true); + IPacketWriter WritePacket(); + } +} diff --git a/Common/Interfaces/IWorldManager.cs b/Common/Interfaces/IWorldManager.cs new file mode 100644 index 0000000..9fb2cfd --- /dev/null +++ b/Common/Interfaces/IWorldManager.cs @@ -0,0 +1,15 @@ +using Common.Structs; +using System; +using System.Net.Sockets; + +namespace Common.Interfaces +{ + public interface IWorldManager + { + Account Account { get; set; } + Socket Socket { get; set; } + + void Recieve(); + void Send(IPacketWriter packet); + } +} diff --git a/Common/Libs/ObjectDB/Db4objects.Db4o.Linq.dll b/Common/Libs/ObjectDB/Db4objects.Db4o.Linq.dll new file mode 100644 index 0000000000000000000000000000000000000000..4130a62357385dce12edab2bd0fd6735d65e8a41 GIT binary patch literal 58880 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 z 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 literal 0 HcmV?d00001 diff --git a/Common/Libs/ObjectDB/Db4objects.Db4o.dll b/Common/Libs/ObjectDB/Db4objects.Db4o.dll new file mode 100644 index 0000000000000000000000000000000000000000..3d1dbe58c90a8a829b117c24be6562a8a425354f GIT binary patch literal 775168 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

VlvI#I(lZkpdR9sm#@BczstjuFh&IJ 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 z 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


A@6M1BjORp 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 OpcodeHandlers = new Dictionary(); + public delegate void HandlePacket(ref IPacketReader packet, ref IWorldManager manager); + + public static void DefineOpcodeHandler(Opcodes opcode, HandlePacket handler) + { + OpcodeHandlers[opcode] = handler; + } + + public static bool InvokeHandler(IPacketReader reader, IWorldManager manager, Opcodes opcode) + { + if (OpcodeHandlers.ContainsKey(opcode)) + { + OpcodeHandlers[opcode].Invoke(ref reader, ref manager); + return true; + } + else + return false; + } + } +} diff --git a/Common/Properties/AssemblyInfo.cs b/Common/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..055a0e5 --- /dev/null +++ b/Common/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Common")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("274967c1-2bf6-4f7d-86d3-f0eb443b541d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/Structs/Account.cs b/Common/Structs/Account.cs new file mode 100644 index 0000000..10fafc8 --- /dev/null +++ b/Common/Structs/Account.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using Common.Interfaces; +using Common.Cryptography; +using System.Runtime.Serialization; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace Common.Structs +{ + [Serializable] + public class Account + { + public string Name { get; set; } + public List Characters { get; set; } = new List(); + public ICharacter ActiveCharacter => Characters.Find(x => x.IsOnline); + + private bool _saving = false; + + public Account() { } + + public Account(string name) + { + Name = name; + } + + public void Save() + { + if (_saving) return; + + _saving = true; + Directory.CreateDirectory("Accounts"); + using (var fs = new FileStream(Path.Combine("Accounts", Name.ToUpper() + ".dat"), FileMode.Create, FileAccess.Write)) + using (var bw = new BinaryWriter(fs)) + { + foreach (var c in Characters) + { + bw.Write(c.Build); + bw.Write(c.Class); + bw.Write(c.DisplayId); + bw.Write(c.Face); + bw.Write(c.FacialHair); + bw.Write(c.Gender); + bw.Write(c.Guid); + bw.Write(c.HairColor); + bw.Write(c.HairStyle); + bw.Write(c.Location.X); + bw.Write(c.Location.Y); + bw.Write(c.Location.Z); + bw.Write(c.Location.O); + bw.Write(c.Location.Map); + bw.Write(c.Name); + bw.Write(c.PowerType); + bw.Write(c.Race); + bw.Write(c.Skin); + bw.Write(c.Zone); + } + } + _saving = false; + } + + public void Load() where T : new() + { + Characters = new List(); + + if (!File.Exists(Path.Combine("Accounts", Name + ".dat"))) + return; + + try + { + using (var fs = new FileStream(Path.Combine("Accounts", Name.ToUpper() + ".dat"), FileMode.Open, FileAccess.Read)) + using (var br = new BinaryReader(fs)) + { + while (br.BaseStream.Position < br.BaseStream.Length) + { + ICharacter c = (ICharacter)Activator.CreateInstance(); + c.Build = br.ReadInt32(); + c.Class = br.ReadByte(); + c.DisplayId = br.ReadUInt32(); + c.Face = br.ReadByte(); + c.FacialHair = br.ReadByte(); + c.Gender = br.ReadByte(); + c.Guid = br.ReadUInt64(); + c.HairColor = br.ReadByte(); + c.HairStyle = br.ReadByte(); + + c.Location = new Location() + { + X = br.ReadSingle(), + Y = br.ReadSingle(), + Z = br.ReadSingle(), + O = br.ReadSingle(), + Map = br.ReadUInt32(), + }; + + c.Name = br.ReadString(); + c.PowerType = br.ReadByte(); + c.Race = br.ReadByte(); + c.Skin = br.ReadByte(); + c.Zone = br.ReadUInt32(); + + Characters.Add(c); + } + } + } + catch { } + } + } +} diff --git a/Common/Structs/Location.cs b/Common/Structs/Location.cs new file mode 100644 index 0000000..8325107 --- /dev/null +++ b/Common/Structs/Location.cs @@ -0,0 +1,73 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Structs +{ + [Serializable] + public class Location + { + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + public float O { get; set; } + public uint Map { get; set; } + public string Description { get; set; } + + public Location() { } + + public Location(float x, float y, float z, float o, uint map, string description) + { + this.X = x; + this.Y = y; + this.Z = z; + this.O = o; + this.Map = map; + this.Description = description; + } + + public Location(float x, float y, float z, float o, uint map) + { + this.X = x; + this.Y = y; + this.Z = z; + this.O = o; + this.Map = map; + } + + + public void Update(float x, float y, float z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + public void Update(float x, float y, float z, float o) + { + this.X = x; + this.Y = y; + this.Z = z; + this.O = o; + } + + public void Update(IPacketReader packet, bool orientation = false) + { + this.X = packet.ReadFloat(); + this.Y = packet.ReadFloat(); + this.Z = packet.ReadFloat(); + + if (orientation) + this.O = packet.ReadFloat(); + } + + + public override string ToString() + { + return $"X: {X}, Y: {Y}, Z: {Z}, O: {O}, Map: {Map}"; + } + } +} diff --git a/Plugins/Alpha_3368/Alpha_3368.csproj b/Plugins/Alpha_3368/Alpha_3368.csproj new file mode 100644 index 0000000..9ef83f2 --- /dev/null +++ b/Plugins/Alpha_3368/Alpha_3368.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {1087E940-0B75-452B-B245-24C77CE80F13} + Library + Properties + Alpha_3368 + Alpha_3368 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7ff9-4b3e-aaad-cc7c329f924a} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Alpha_3368/Character.cs b/Plugins/Alpha_3368/Character.cs new file mode 100644 index 0000000..10bd6b0 --- /dev/null +++ b/Plugins/Alpha_3368/Character.cs @@ -0,0 +1,293 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Alpha_3368 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); //ObjectGuid + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt64(0); //TransportGuid + writer.WriteFloat(0); //TransportX + writer.WriteFloat(0); //TransportY + writer.WriteFloat(0); //TransportZ + writer.WriteFloat(0); //TransportW (TransportO) + + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + + writer.WriteFloat(0); //Pitch + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32(0); //FallTime + + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, 0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, this.Mana); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_POWER3, this.Focus); + SetField(Fields.UNIT_FIELD_POWER4, this.Energy); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Mana); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_MAXPOWER3, this.Focus); + SetField(Fields.UNIT_FIELD_MAXPOWER4, this.Energy); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.UNIT_FIELD_BYTES_1, (uint)StandState); + SetField(Fields.UNIT_FIELD_STAT0, this.Strength); + SetField(Fields.UNIT_FIELD_STAT1, this.Agility); + SetField(Fields.UNIT_FIELD_STAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_STAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_BASESTAT0, this.Strength); + SetField(Fields.UNIT_FIELD_BASESTAT1, this.Agility); + SetField(Fields.UNIT_FIELD_BASESTAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_BASESTAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_BASESTAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.PLAYER_FIELD_NUM_INV_SLOTS, 14); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, 0 }, 0)); + SetField(Fields.PLAYER_BASE_MANA, this.Mana); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks, 20 + writer.WriteBytes(maskArray); + + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MOVE_WORLDPORT_ACK], "SMSG_MOVE_WORLDPORT_ACK"); + movementStatus.WriteUInt64(0); //Transport ID + movementStatus.WriteFloat(0); //Transport + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(x); //Player + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); //? + movementStatus.WriteUInt32(0x08000000); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt8((byte)map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_COMBO_TARGET = 18, + UNIT_FIELD_CHANNEL_OBJECT = 20, + UNIT_FIELD_HEALTH = 22, + UNIT_FIELD_POWER1 = 23, + UNIT_FIELD_POWER2 = 24, + UNIT_FIELD_POWER3 = 25, + UNIT_FIELD_POWER4 = 26, + UNIT_FIELD_MAXHEALTH = 27, + UNIT_FIELD_MAXPOWER1 = 28, + UNIT_FIELD_MAXPOWER2 = 29, + UNIT_FIELD_MAXPOWER3 = 30, + UNIT_FIELD_MAXPOWER4 = 31, + UNIT_FIELD_LEVEL = 32, + UNIT_FIELD_FACTIONTEMPLATE = 33, + UNIT_FIELD_BYTES_0 = 34, + UNIT_FIELD_STAT0 = 35, + UNIT_FIELD_STAT1 = 36, + UNIT_FIELD_STAT2 = 37, + UNIT_FIELD_STAT3 = 38, + UNIT_FIELD_STAT4 = 39, + UNIT_FIELD_BASESTAT0 = 40, + UNIT_FIELD_BASESTAT1 = 41, + UNIT_FIELD_BASESTAT2 = 42, + UNIT_FIELD_BASESTAT3 = 43, + UNIT_FIELD_BASESTAT4 = 44, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 45, + UNIT_VIRTUAL_ITEM_INFO = 48, + UNIT_FIELD_FLAGS = 54, + UNIT_FIELD_COINAGE = 55, + UNIT_FIELD_AURA = 56, + UNIT_FIELD_AURAFLAGS = 112, + UNIT_FIELD_AURASTATE = 119, + UNIT_FIELD_MOD_DAMAGE_DONE = 120, + UNIT_FIELD_MOD_DAMAGE_TAKEN = 126, + UNIT_FIELD_MOD_CREATURE_DAMAGE_DONE = 132, + UNIT_FIELD_BASEATTACKTIME = 140, + UNIT_FIELD_RESISTANCES = 142, + UNIT_FIELD_BOUNDINGRADIUS = 148, + UNIT_FIELD_COMBATREACH = 149, + UNIT_FIELD_WEAPONREACH = 150, + UNIT_FIELD_DISPLAYID = 151, + UNIT_FIELD_MOUNTDISPLAYID = 152, + UNIT_FIELD_DAMAGE = 153, + UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = 154, + UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = 160, + UNIT_FIELD_RESISTANCEITEMMODS = 166, + UNIT_FIELD_BYTES_1 = 172, + UNIT_FIELD_PETNUMBER = 173, + UNIT_FIELD_PET_NAME_TIMESTAMP = 174, + UNIT_FIELD_PETEXPERIENCE = 175, + UNIT_FIELD_PETNEXTLEVELEXP = 176, + UNIT_DYNAMIC_FLAGS = 177, + UNIT_EMOTE_STATE = 178, + UNIT_CHANNEL_SPELL = 179, + UNIT_MOD_CAST_SPEED = 180, + UNIT_CREATED_BY_SPELL = 181, + UNIT_FIELD_BYTES_2 = 182, + UNIT_FIELD_PADDING = 183, + PLAYER_FIELD_INV_SLOT_1 = 184, + PLAYER_FIELD_PACK_SLOT_1 = 230, + PLAYER_FIELD_BANK_SLOT_1 = 262, + PLAYER_FIELD_BANKBAG_SLOT_1 = 310, + PLAYER_SELECTION = 322, + PLAYER_FARSIGHT = 324, + PLAYER_DUEL_ARBITER = 326, + PLAYER_FIELD_NUM_INV_SLOTS = 328, + PLAYER_GUILDID = 329, + PLAYER_GUILDRANK = 330, + PLAYER_BYTES = 331, + PLAYER_XP = 332, + PLAYER_NEXT_LEVEL_XP = 333, + PLAYER_SKILL_INFO_1_1 = 334, + PLAYER_BYTES_2 = 526, + PLAYER_QUEST_LOG_1_1 = 527, + PLAYER_CHARACTER_POINTS1 = 623, + PLAYER_CHARACTER_POINTS2 = 624, + PLAYER_TRACK_CREATURES = 625, + PLAYER_TRACK_RESOURCES = 626, + PLAYER_CHAT_FILTERS = 627, + PLAYER_DUEL_TEAM = 628, + PLAYER_BLOCK_PERCENTAGE = 629, + PLAYER_DODGE_PERCENTAGE = 630, + PLAYER_PARRY_PERCENTAGE = 631, + PLAYER_BASE_MANA = 632, + PLAYER_GUILD_TIMESTAMP = 633, + MAX = 634, + } + } +} diff --git a/Plugins/Alpha_3368/Handlers/AuthHandler.cs b/Plugins/Alpha_3368/Handlers/AuthHandler.cs new file mode 100644 index 0000000..e37102a --- /dev/null +++ b/Plugins/Alpha_3368/Handlers/AuthHandler.cs @@ -0,0 +1,75 @@ +using Common.Interfaces.Handlers; +using System; +using System.Text; +using Common.Interfaces; +using Common.Structs; +using System.Net.Sockets; +using Common.Constants; + +namespace Alpha_3368.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteUInt8(0); + writer.WriteUInt8(0); + writer.WriteUInt8(0); + writer.WriteUInt8(0); + writer.WriteUInt8(0); + writer.WriteUInt8(0); + return writer; + } + + public void HandleRealmList(Socket socket) + { + byte[] realmName = Encoding.ASCII.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.RedirectPort); + + PacketWriter writer = new PacketWriter(); + writer.WriteUInt8(1); + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + + socket.SendData(writer, "REALMLIST_REQUEST"); + socket.Close(); + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString(0xD).ToUpper(); + + Account account = new Account(); + account.Name = name; + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + + var character = manager.Account.ActiveCharacter; + if (character != null) + character.IsOnline = false; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(System.Text.Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + } +} diff --git a/Plugins/Alpha_3368/Handlers/CharHandler.cs b/Plugins/Alpha_3368/Handlers/CharHandler.cs new file mode 100644 index 0000000..60fbb96 --- /dev/null +++ b/Plugins/Alpha_3368/Handlers/CharHandler.cs @@ -0,0 +1,213 @@ +using Common.Interfaces.Handlers; +using System; +using Common.Interfaces; +using Common.Commands; +using Common.Structs; +using Common.Constants; +using Common.Extensions; +using System.Linq; + +namespace Alpha_3368.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.SetPowerType(true); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(manager.Account.ActiveCharacter.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + ulong TransportGuid = packet.ReadUInt64(); + float TransportX = packet.ReadFloat(); + float TransportY = packet.ReadFloat(); + float TransportZ = packet.ReadFloat(); + float TransportO = packet.ReadFloat(); + + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + nameCache.WriteUInt8(0); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if(emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Alpha_3368/Handlers/WorldHandler.cs b/Plugins/Alpha_3368/Handlers/WorldHandler.cs new file mode 100644 index 0000000..2364f3b --- /dev/null +++ b/Plugins/Alpha_3368/Handlers/WorldHandler.cs @@ -0,0 +1,82 @@ +using Common.Interfaces.Handlers; +using System; +using Common.Interfaces; +using Common.Constants; +using Common.Logging; +using Common.Extensions; + +namespace Alpha_3368.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt32(); + byte zone = packet.ReadUInt8(); + float x = packet.ReadFloat(); + float y = packet.ReadFloat(); + float z = packet.ReadFloat(); + float o = packet.ReadFloat(); + + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MOVE_WORLDPORT_ACK], "SMSG_MOVE_WORLDPORT_ACK"); + movementStatus.WriteUInt64(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + movementStatus.WriteUInt32(0x08000000); + manager.Send(movementStatus); + } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Alpha_3368/Opcodes.cs b/Plugins/Alpha_3368/Opcodes.cs new file mode 100644 index 0000000..f429f57 --- /dev/null +++ b/Plugins/Alpha_3368/Opcodes.cs @@ -0,0 +1,80 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Alpha_3368 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1DD }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1DF }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1DE }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x036 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x03A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x038 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x03C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x037 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x03B }, + { global::Opcodes.CMSG_PING, 0x1CD }, + { global::Opcodes.SMSG_PONG, 0x1CE }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x03D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0x0A9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x050 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x051 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x04B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x04D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x008 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x03E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x003F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0x0B5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0x0B6 }, + { global::Opcodes.MSG_MOVE_STOP, 0x0B7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0x0B8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0x0B9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0x0BA }, + { global::Opcodes.MSG_MOVE_JUMP, 0x0BB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0x0BC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0x0BD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0x0BE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0x0BF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0x0C0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0x0C1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0x0C2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0x0C3 }, + { global::Opcodes.MOVE_COLLIDE_REDIRECT, 0x0C9 }, + { global::Opcodes.MOVE_COLLIDE_STUCK, 0x0CA }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0x0CB }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0x0CC }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0x0D7 }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0x0D8 }, + { global::Opcodes.MSG_MOVE_ROOT, 0x0E7 }, + { global::Opcodes.MSG_MOVE_UNROOT, 0x0E8 }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0x0E9 }, + { global::Opcodes.SMSG_MOVE_WORLDPORT_ACK, 0x0C7 }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0x0D9 }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x096 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x095 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0x0DF }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0x0E1 }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1BF }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED , 0x42 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xF4 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x01E5 }, + { global::Opcodes.SMSG_EMOTE, 0x00F6 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x00F7 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x00F8 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + } +} diff --git a/Plugins/Alpha_3368/PacketReader.cs b/Plugins/Alpha_3368/PacketReader.cs new file mode 100644 index 0000000..31cf117 --- /dev/null +++ b/Plugins/Alpha_3368/PacketReader.cs @@ -0,0 +1,121 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Alpha_3368 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + //Packet header (0.5.3.3368): Size: 2 bytes + Cmd: 4 bytes + Size = (uint)((this.ReadUInt16() / 0x100) - 4); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Alpha_3368/PacketWriter.cs b/Plugins/Alpha_3368/PacketWriter.cs new file mode 100644 index 0000000..595a75f --- /dev/null +++ b/Plugins/Alpha_3368/PacketWriter.cs @@ -0,0 +1,125 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Alpha_3368 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + public void WritePacketHeader(uint opcode) + { + //Packet header (0.5.3.3368): Size: 2 bytes + Cmd: 2 bytes + //Packet header after SMSG_AUTH_CHALLENGE (0.5.3.3368): Size: 2 bytes + Cmd: 4 bytes + WriteUInt16(0); + WriteUInt8((byte)(opcode % 0x100)); + WriteUInt8((byte)(opcode / 0x100)); + + if (opcode != 0x1DD) + { + WriteUInt8(0); + WriteUInt8(0); + } + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + if (!PreAuth) + { + data[0] = (byte)(Size / 0x100); + data[1] = (byte)(Size % 0x100); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Alpha_3368/Properties/AssemblyInfo.cs b/Plugins/Alpha_3368/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..47cc887 --- /dev/null +++ b/Plugins/Alpha_3368/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Alpha-3368")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Alpha-3368")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1087e940-0b75-452b-b245-24c77ce80f13")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Alpha_3368/Sandbox.cs b/Plugins/Alpha_3368/Sandbox.cs new file mode 100644 index 0000000..789cd36 --- /dev/null +++ b/Plugins/Alpha_3368/Sandbox.cs @@ -0,0 +1,29 @@ +using Common.Interfaces; +using System; +using Common.Interfaces.Handlers; +using Alpha_3368.Handlers; +using Common.Structs; + +namespace Alpha_3368 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFAlpha (0.5.3) Sandbox"; + public int Build { get; set; } = 3368; + public int RealmPort { get; set; } = 9100; + public int RedirectPort { get; set; } = 9090; + public int WorldPort { get; set; } = 8100; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3592/Beta_3592.csproj b/Plugins/Beta_3592/Beta_3592.csproj new file mode 100644 index 0000000..385a18a --- /dev/null +++ b/Plugins/Beta_3592/Beta_3592.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {5B87983C-952D-4965-9E99-8F34FA31793D} + Library + Properties + Beta_3592 + Beta_3592 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7ff9-4b3e-aaad-cc7c329f924a} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3592/Character.cs b/Plugins/Beta_3592/Character.cs new file mode 100644 index 0000000..d6decb0 --- /dev/null +++ b/Plugins/Beta_3592/Character.cs @@ -0,0 +1,302 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Beta_3592 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); //ObjectGuid + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.GUID, this.Guid); + SetField(Fields.HIER_TYPE, (uint)0x19); + SetField(Fields.ENTRY, 0); + SetField(Fields.SCALE, 1f); + SetField(Fields.TARGET, (ulong)0); + SetField(Fields.HEALTH, this.Health); + SetField(Fields.MANA, this.Mana); + SetField(Fields.RAGE, 0); + SetField(Fields.FOCUS, this.Focus); + SetField(Fields.ENERGY, this.Energy); + SetField(Fields.MAX_HEALTH, this.Health); + SetField(Fields.MAX_MANA, this.Mana); + SetField(Fields.MAX_RAGE, this.Rage); + SetField(Fields.MAX_FOCUS, this.Focus); + SetField(Fields.MAX_ENERGY, this.Energy); + SetField(Fields.LEVEL, this.Level); + SetField(Fields.FACTION, 35); + SetField(Fields.UNIT_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.STRENGTH, this.Strength); + SetField(Fields.AGILITY, this.Agility); + SetField(Fields.STAMINA, this.Stamina); + SetField(Fields.INTELLECT, this.Intellect); + SetField(Fields.SPIRIT, this.Spirit); + SetField(Fields.BASE_STRENGTH, this.Strength); + SetField(Fields.BASE_AGILITY, this.Agility); + SetField(Fields.BASE_STAMINA, this.Stamina); + SetField(Fields.BASE_INTELLECT, this.Intellect); + SetField(Fields.BASE_SPIRIT, this.Spirit); + SetField(Fields.FLAGS, 0); + SetField(Fields.DISPLAYID, DisplayId); + SetField(Fields.MOUNT_DISPLAYID, MountDisplayId); + SetField(Fields.UNIT_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES_1, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.XP, 47); + SetField(Fields.NEXTLEVEL_XP, 200); + SetField(Fields.MIN_DAMAGE, 0); + SetField(Fields.BASEATTACKTIME0, 1f); + + for (int i = 0; i < 32; i++) + SetField(Fields.EXPLORED_ZONES + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MOVE_WORLDPORT_ACK], "SMSG_MOVE_WORLDPORT_ACK"); + movementStatus.WriteUInt32(0); //Transport ID + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + movementStatus.WriteUInt32(0); //Movement Flags + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt8((byte)map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + + internal enum Fields + { + GUID = 0, + HIER_TYPE = 2, + ENTRY = 3, + SCALE = 4, + PADDING = 5, + CHARMED = 6, + SUMMON = 8, + CHARMEDBY = 10, + SUMMONEDBY = 12, + CREATEDBY = 14, + TARGET = 16, + COMBO_TARGET = 18, + CHANNEL_OBJECT = 20, + HEALTH = 22, + POWER0 = 23, + MANA = 23, + RAGE = 24, + FOCUS = 25, + ENERGY = 26, + MAX_HEALTH = 27, + MAX_POWER0 = 28, + MAX_MANA = 28, + MAX_RAGE = 29, + MAX_FOCUS = 30, + MAX_ENERGY = 31, + LEVEL = 32, + FACTION = 33, + UNIT_BYTES_0 = 34, + STATS0 = 35, + STRENGTH = 35, + AGILITY = 36, + STAMINA = 37, + INTELLECT = 38, + SPIRIT = 39, + BASE_STRENGTH = 40, + BASE_AGILITY = 41, + BASE_STAMINA = 42, + BASE_INTELLECT = 43, + BASE_SPIRIT = 44, + VIRTUAL_ITEMSLOTDISPLAY = 45, + VIRTUAL_ITEMINFO = 48, + FLAGS = 54, + COINAGE = 55, + AURA = 56, + AURA_LEVELS = 112, + AURA_APPLICATIONS = 122, + AURA_FLAGS = 132, + AURA_STATE = 139, + BASEATTACKTIME0 = 140, + BASEATTACKTIME1 = 141, + RESISTANCE = 142, + RESIST_PHYSICAL = 142, + RESIST_HOLY = 143, + RESIST_FIRE = 144, + RESIST_NATURE = 145, + RESIST_FROST = 146, + RESIST_SHADOW = 147, + BOUNDING_RADIUS = 148, + COMBAT_REACH = 149, + WEAPON_REACH = 150, + DISPLAYID = 151, + MOUNT_DISPLAYID = 152, + MIN_DAMAGE = 153, + MAX_DAMAGE = 154, + MOD_DAMAGE_DONE = 155, + RESISTANCE_BUFF_POSITIVE = 161, + RESISTANCE_BUFF_NEGATIVE = 167, + RESISTANCE_ITEM_MODS = 173, + UNIT_BYTES_1 = 179, + PET_NUMBER = 180, + PET_NAME_TIMESTAMP = 181, + PET_EXPERIENCE = 182, + PET_NEXT_LEVE_EXP = 183, + DYNAMIC_FLAGS = 184, + EMOTE_STATE = 185, + CHANNEL_SPELL = 186, + MOD_CAST_SPEED = 187, + CREATED_BY_SPELL = 188, + NPC_FLAGS = 189, + UNIT_BYTES_2 = 190, + ATTACKPOWER = 191, + ATTACKPOWERMODIFIER = 192, + UNIT_PADDING = 193, + SELECTION = 194, + DUEL_ARBITER = 196, + GUILD_ID = 198, + GUILD_RANK = 199, + PLAYER_BYTES_1 = 200, + PLAYER_BYTES_2 = 201, + DUEL_TEAM = 202, + GUILD_TIMESTAMP = 203, + INV_SLOTS = 204, + PACK_SLOTS = 250, + BANK_SLOTS = 282, + BANKBAG_SLOTS = 330, + FARSIGHT = 342, + XP = 344, + NEXTLEVEL_XP = 345, + SKILL_INFO = 346, + QUEST_INFO = 730, + TALENT_POINTS = 810, + SKILL_POINTS = 811, + TRACK_CREATURES = 812, + TRACK_RESOURCES = 813, + CHAT_FILTERS = 814, + BLOCK_PERCENTAGE = 815, + DODGE_PERCENTAGE = 816, + PARRY_PERCENTAGE = 817, + BASE_MANA = 818, + EXPLORED_ZONES = 819, + REST_STATE_EXPERIENCE = 851, + MAX = 852 + } + } +} diff --git a/Plugins/Beta_3592/Handlers/AuthHandler.cs b/Plugins/Beta_3592/Handlers/AuthHandler.cs new file mode 100644 index 0000000..bf35ffa --- /dev/null +++ b/Plugins/Beta_3592/Handlers/AuthHandler.cs @@ -0,0 +1,107 @@ +using Common.Interfaces.Handlers; +using System; +using System.Text; +using Common.Interfaces; +using System.Net.Sockets; +using Common.Constants; +using Common.Structs; +using Common.Cryptography; + +namespace Beta_3592.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(System.Text.Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.ASCII.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.RedirectPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(11 + realmName.Length + redirect.Length)); + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3592/Handlers/CharHandler.cs b/Plugins/Beta_3592/Handlers/CharHandler.cs new file mode 100644 index 0000000..b8fdf35 --- /dev/null +++ b/Plugins/Beta_3592/Handlers/CharHandler.cs @@ -0,0 +1,212 @@ +using Common.Interfaces.Handlers; +using System; +using Common.Interfaces; +using Common.Commands; +using Common.Constants; +using Common.Structs; +using Common.Extensions; +using System.Linq; + +namespace Beta_3592.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 5); + cha.SetPowerType(true); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + uint Flags = packet.ReadUInt32(); + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3592/Handlers/WorldHandler.cs b/Plugins/Beta_3592/Handlers/WorldHandler.cs new file mode 100644 index 0000000..cc73a8c --- /dev/null +++ b/Plugins/Beta_3592/Handlers/WorldHandler.cs @@ -0,0 +1,97 @@ +using Common.Interfaces.Handlers; +using System; +using Common.Interfaces; +using Common.Constants; +using Common.Logging; +using Common.Extensions; + +namespace Beta_3592.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + //Enable UI : REQUIRED + PacketWriter uiconfig = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UI_CONFIG_MD5], "SMSG_UI_CONFIG_MD5"); + uiconfig.Write(new byte[80]); + manager.Send(uiconfig); + + HandleQueryTime(ref packet, ref manager); + + manager.Send(character.BuildUpdate()); + + manager.Send(uiconfig); //Fixes a UI load issue? + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt32(); + byte zone = packet.ReadUInt8(); + float x = packet.ReadFloat(); + float y = packet.ReadFloat(); + float z = packet.ReadFloat(); + float o = packet.ReadFloat(); + + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MOVE_WORLDPORT_ACK], "SMSG_MOVE_WORLDPORT_ACK"); + movementStatus.WriteUInt64(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(0); + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + movementStatus.WriteUInt32(0x08000000); + manager.Send(movementStatus); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3592/Opcodes.cs b/Plugins/Beta_3592/Opcodes.cs new file mode 100644 index 0000000..f068451 --- /dev/null +++ b/Plugins/Beta_3592/Opcodes.cs @@ -0,0 +1,82 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3592 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1DD }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1DF }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1DE }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x036 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x03A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x038 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x03C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x037 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x03B }, + { global::Opcodes.CMSG_PING, 0x1CD }, + { global::Opcodes.SMSG_PONG, 0x1CE }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x03D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0x0A9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x050 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x051 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x04B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x04D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x008 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x03E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x003F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0x0B5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0x0B6 }, + { global::Opcodes.MSG_MOVE_STOP, 0x0B7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0x0B8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0x0B9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0x0BA }, + { global::Opcodes.MSG_MOVE_JUMP, 0x0BB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0x0BC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0x0BD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0x0BE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0x0BF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0x0C0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0x0C1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0x0C2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0x0C3 }, + { global::Opcodes.MOVE_COLLIDE_REDIRECT, 0x0C9 }, + { global::Opcodes.MOVE_COLLIDE_STUCK, 0x0CA }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0x0CB }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0x0CC }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0x0D7 }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0x0D8 }, + { global::Opcodes.MSG_MOVE_ROOT, 0x0E7 }, + { global::Opcodes.MSG_MOVE_UNROOT, 0x0E8 }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0x0E9 }, + { global::Opcodes.SMSG_MOVE_WORLDPORT_ACK, 0x0C7 }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0x0D9 }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x096 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x095 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0x0DF }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0x0E1 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xF0 }, + { global::Opcodes.SMSG_UI_CONFIG_MD5, 0x1FA }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1BF }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xF4 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x01F4 }, + { global::Opcodes.SMSG_EMOTE, 0x00F6 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x00F7 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x00F8 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + } +} diff --git a/Plugins/Beta_3592/PacketReader.cs b/Plugins/Beta_3592/PacketReader.cs new file mode 100644 index 0000000..f396f47 --- /dev/null +++ b/Plugins/Beta_3592/PacketReader.cs @@ -0,0 +1,120 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3592 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3592/PacketWriter.cs b/Plugins/Beta_3592/PacketWriter.cs new file mode 100644 index 0000000..1a2b7a5 --- /dev/null +++ b/Plugins/Beta_3592/PacketWriter.cs @@ -0,0 +1,119 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3592 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt32(opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if(!PreAuth) + { + Size = (ushort)((Size >> 8) + ((Size & 0xFF) << 8)); + data[0] = (byte)(Size & 0xFF); + data[1] = (byte)(Size >> 8); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3592/Properties/AssemblyInfo.cs b/Plugins/Beta_3592/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c21ec32 --- /dev/null +++ b/Plugins/Beta_3592/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta_3592")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3592")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5b87983c-952d-4965-9e99-8f34fa31793d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3592/Sandbox.cs b/Plugins/Beta_3592/Sandbox.cs new file mode 100644 index 0000000..77a0129 --- /dev/null +++ b/Plugins/Beta_3592/Sandbox.cs @@ -0,0 +1,28 @@ +using Beta_3592.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; + +namespace Beta_3592 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 2 (0.6.0) Sandbox"; + public int Build { get; set; } = 3592; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 9001; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3694/Beta_3694.csproj b/Plugins/Beta_3694/Beta_3694.csproj new file mode 100644 index 0000000..8e7f0f0 --- /dev/null +++ b/Plugins/Beta_3694/Beta_3694.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {7C57B76D-DEF2-4EDC-BF5A-A3A2BCEF0BB1} + Library + Properties + Beta_3694 + Beta_3694 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3694/Character.cs b/Plugins/Beta_3694/Character.cs new file mode 100644 index 0000000..2b4b087 --- /dev/null +++ b/Plugins/Beta_3694/Character.cs @@ -0,0 +1,301 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3694 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(4.7222f); //Backwards SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.OBJECT_FIELD_PADDING, 0); + SetField(Fields.UNIT_FIELD_TARGET, (ulong)0); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, this.Mana); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_POWER3, this.Focus); + SetField(Fields.UNIT_FIELD_POWER4, this.Energy); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Mana); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_MAXPOWER3, this.Focus); + SetField(Fields.UNIT_FIELD_MAXPOWER4, this.Energy); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.PLAYER_FIELD_STAT0, this.Strength); + SetField(Fields.PLAYER_FIELD_STAT1, this.Agility); + SetField(Fields.PLAYER_FIELD_STAT2, this.Stamina); + SetField(Fields.PLAYER_FIELD_STAT3, this.Intellect); + SetField(Fields.PLAYER_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_FLAGS, 0); + SetField(Fields.PLAYER_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + SetField(Fields.PLAYER_FIELD_ATTACKPOWER, 10); + SetField(Fields.PLAYER_FIELD_BYTES, 0xEEEE0000); + SetField(Fields.UNIT_DYNAMIC_FLAGS, 0x1); + SetField(Fields.UNIT_FIELD_BASEATTACKTIME, 1f); + SetField(Fields.UNIT_FIELD_FACTIONTEMPLATE, 35); + + for (int i = 0; i < 32; i++) + SetField(Fields.PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_CHANNEL_OBJECT = 18, + UNIT_FIELD_HEALTH = 20, + UNIT_FIELD_POWER1 = 21, + UNIT_FIELD_POWER2 = 22, + UNIT_FIELD_POWER3 = 23, + UNIT_FIELD_POWER4 = 24, + UNIT_FIELD_POWER5 = 25, + UNIT_FIELD_MAXHEALTH = 26, + UNIT_FIELD_MAXPOWER1 = 27, + UNIT_FIELD_MAXPOWER2 = 28, + UNIT_FIELD_MAXPOWER3 = 29, + UNIT_FIELD_MAXPOWER4 = 30, + UNIT_FIELD_MAXPOWER5 = 31, + UNIT_FIELD_LEVEL = 32, + UNIT_FIELD_FACTIONTEMPLATE = 33, + UNIT_FIELD_BYTES_0 = 34, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 35, + UNIT_VIRTUAL_ITEM_INFO = 38, + UNIT_FIELD_FLAGS = 44, + UNIT_FIELD_AURA = 45, + UNIT_FIELD_AURALEVELS = 101, + UNIT_FIELD_AURAAPPLICATIONS = 111, + UNIT_FIELD_AURAFLAGS = 121, + UNIT_FIELD_AURASTATE = 128, + UNIT_FIELD_BASEATTACKTIME = 129, + UNIT_FIELD_BOUNDINGRADIUS = 131, + UNIT_FIELD_COMBATREACH = 132, + UNIT_FIELD_WEAPONREACH = 133, + UNIT_FIELD_DISPLAYID = 134, + UNIT_FIELD_MOUNTDISPLAYID = 135, + UNIT_FIELD_MINDAMAGE = 136, + UNIT_FIELD_MAXDAMAGE = 137, + UNIT_FIELD_BYTES_1 = 138, + UNIT_FIELD_PETNUMBER = 139, + UNIT_FIELD_PET_NAME_TIMESTAMP = 140, + UNIT_FIELD_PETEXPERIENCE = 141, + UNIT_FIELD_PETNEXTLEVELEXP = 142, + UNIT_DYNAMIC_FLAGS = 143, + UNIT_CHANNEL_SPELL = 144, + UNIT_MOD_CAST_SPEED = 145, + UNIT_CREATED_BY_SPELL = 146, + UNIT_NPC_FLAGS = 147, + UNIT_NPC_EMOTESTATE = 148, + UNIT_FIELD_PADDING = 149, + PLAYER_SELECTION = 150, + PLAYER_DUEL_ARBITER = 152, + PLAYER_GUILDID = 154, + PLAYER_GUILDRANK = 155, + PLAYER_BYTES = 156, + PLAYER_BYTES_2 = 157, + PLAYER_BYTES_3 = 158, + PLAYER_DUEL_TEAM = 159, + PLAYER_GUILD_TIMESTAMP = 160, + PLAYER_FIELD_PAD_0 = 161, + PLAYER_FIELD_INV_SLOT_HEAD = 162, + PLAYER_FIELD_PACK_SLOT_1 = 208, + PLAYER_FIELD_BANK_SLOT_1 = 240, + PLAYER_FIELD_BANKBAG_SLOT_1 = 288, + PLAYER_FARSIGHT = 300, + PLAYER_FIELD_COMBO_TARGET = 302, + PLAYER_XP = 304, + PLAYER_NEXT_LEVEL_XP = 305, + PLAYER_SKILL_INFO_1_1 = 306, + PLAYER_QUEST_LOG_1_1 = 690, + PLAYER_CHARACTER_POINTS1 = 770, + PLAYER_CHARACTER_POINTS2 = 771, + PLAYER_TRACK_CREATURES = 772, + PLAYER_TRACK_RESOURCES = 773, + PLAYER_CHAT_FILTERS = 774, + PLAYER_BLOCK_PERCENTAGE = 775, + PLAYER_DODGE_PERCENTAGE = 776, + PLAYER_PARRY_PERCENTAGE = 777, + PLAYER_BASE_MANA = 778, + PLAYER_EXPLORED_ZONES_1 = 779, + PLAYER_REST_STATE_EXPERIENCE = 811, + PLAYER_FIELD_COINAGE = 812, + PLAYER_FIELD_STAT0 = 813, + PLAYER_FIELD_STAT1 = 814, + PLAYER_FIELD_STAT2 = 815, + PLAYER_FIELD_STAT3 = 816, + PLAYER_FIELD_STAT4 = 817, + PLAYER_FIELD_BASESTAT0 = 818, + PLAYER_FIELD_BASESTAT1 = 819, + PLAYER_FIELD_BASESTAT2 = 820, + PLAYER_FIELD_BASESTAT3 = 821, + PLAYER_FIELD_BASESTAT4 = 822, + PLAYER_FIELD_RESISTANCES = 823, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 829, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 835, + PLAYER_FIELD_MOD_DAMAGE_DONE = 841, + PLAYER_FIELD_BYTES = 847, + PLAYER_FIELD_ATTACKPOWER = 848, + PLAYER_FIELD_ATTACKPOWERMODIFIER = 849, + MAX = 850, + } + } +} diff --git a/Plugins/Beta_3694/Handlers/AuthHandler.cs b/Plugins/Beta_3694/Handlers/AuthHandler.cs new file mode 100644 index 0000000..186b0b8 --- /dev/null +++ b/Plugins/Beta_3694/Handlers/AuthHandler.cs @@ -0,0 +1,112 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using System.Net.Sockets; +using Common.Structs; +using Common.Constants; +using Common.Cryptography; + +namespace Beta_3694.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(System.Text.Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.RedirectPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(16 + realmName.Length + redirect.Length)); //Packet length + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3694/Handlers/CharHandler.cs b/Plugins/Beta_3694/Handlers/CharHandler.cs new file mode 100644 index 0000000..febbafb --- /dev/null +++ b/Plugins/Beta_3694/Handlers/CharHandler.cs @@ -0,0 +1,217 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using Common.Constants; +using Common.Commands; +using Common.Structs; +using Common.Extensions; + +namespace Beta_3694.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 3); + cha.SetPowerType(true); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + long pos = packet.Position; + uint Flags = packet.ReadUInt32(); + packet.ReadUInt32(); + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3694/Handlers/WorldHandler.cs b/Plugins/Beta_3694/Handlers/WorldHandler.cs new file mode 100644 index 0000000..3fb2423 --- /dev/null +++ b/Plugins/Beta_3694/Handlers/WorldHandler.cs @@ -0,0 +1,89 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using Common.Constants; +using Common.Logging; +using Common.Extensions; + +namespace Beta_3694.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + accountdata.WriteBytes(new byte[80]); + manager.Send(accountdata); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + HandleQueryTime(ref packet, ref manager); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3694/Opcodes.cs b/Plugins/Beta_3694/Opcodes.cs new file mode 100644 index 0000000..f1294f3 --- /dev/null +++ b/Plugins/Beta_3694/Opcodes.cs @@ -0,0 +1,83 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3694 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EA }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EC }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1EB }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DA }, + { global::Opcodes.SMSG_PONG, 0x1DB }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEA }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xEB }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEC }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE4 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFB }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CC }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x207 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xFF }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F2 }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x234 }, + { global::Opcodes.SMSG_UPDATE_ACCOUNT_DATA, 0x20A }, + { global::Opcodes.SMSG_EMOTE, 0x101 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x102 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x103 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/Beta_3694/PacketReader.cs b/Plugins/Beta_3694/PacketReader.cs new file mode 100644 index 0000000..044706b --- /dev/null +++ b/Plugins/Beta_3694/PacketReader.cs @@ -0,0 +1,120 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3694 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3694/PacketWriter.cs b/Plugins/Beta_3694/PacketWriter.cs new file mode 100644 index 0000000..03a1875 --- /dev/null +++ b/Plugins/Beta_3694/PacketWriter.cs @@ -0,0 +1,119 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3694 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt32(opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + Size = (ushort)((Size >> 8) + ((Size & 0xFF) << 8)); + data[0] = (byte)(Size & 0xFF); + data[1] = (byte)(Size >> 8); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3694/Properties/AssemblyInfo.cs b/Plugins/Beta_3694/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9621b94 --- /dev/null +++ b/Plugins/Beta_3694/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta-3694")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3694")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7c57b76d-def2-4edc-bf5a-a3a2bcef0bb1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3694/Sandbox.cs b/Plugins/Beta_3694/Sandbox.cs new file mode 100644 index 0000000..b79da70 --- /dev/null +++ b/Plugins/Beta_3694/Sandbox.cs @@ -0,0 +1,32 @@ +using Beta_3694.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3694 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 3 (0.7.X) Sandbox"; + public int Build { get; set; } = 3694; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 9001; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3734/Beta_3734.csproj b/Plugins/Beta_3734/Beta_3734.csproj new file mode 100644 index 0000000..4b34e1b --- /dev/null +++ b/Plugins/Beta_3734/Beta_3734.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {625F1463-6F82-4F56-9B1E-C8F5D366C0B9} + Library + Properties + Beta_3734 + Beta_3734 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3734/Character.cs b/Plugins/Beta_3734/Character.cs new file mode 100644 index 0000000..44da855 --- /dev/null +++ b/Plugins/Beta_3734/Character.cs @@ -0,0 +1,309 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3734 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(4.7222f); //Backwards SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.OBJECT_FIELD_PADDING, 0); + SetField(Fields.UNIT_FIELD_TARGET, (ulong)0); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, this.Mana); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_POWER3, this.Focus); + SetField(Fields.UNIT_FIELD_POWER4, this.Energy); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Mana); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_MAXPOWER3, this.Focus); + SetField(Fields.UNIT_FIELD_MAXPOWER4, this.Energy); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.PLAYER_FIELD_STAT0, this.Strength); + SetField(Fields.PLAYER_FIELD_STAT1, this.Agility); + SetField(Fields.PLAYER_FIELD_STAT2, this.Stamina); + SetField(Fields.PLAYER_FIELD_STAT3, this.Intellect); + SetField(Fields.PLAYER_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_FLAGS, 0); + SetField(Fields.PLAYER_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + SetField(Fields.PLAYER_FIELD_ATTACKPOWER, 10); + SetField(Fields.PLAYER_FIELD_BYTES, 0xEEEE0000); + SetField(Fields.UNIT_DYNAMIC_FLAGS, 0x1); + SetField(Fields.UNIT_FIELD_BASEATTACKTIME, 1f); + SetField(Fields.UNIT_FIELD_FACTIONTEMPLATE, 35); + + for (int i = 0; i < 32; i++) + SetField(Fields.PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_CHANNEL_OBJECT = 18, + UNIT_FIELD_HEALTH = 20, + UNIT_FIELD_POWER1 = 21, + UNIT_FIELD_POWER2 = 22, + UNIT_FIELD_POWER3 = 23, + UNIT_FIELD_POWER4 = 24, + UNIT_FIELD_POWER5 = 25, + UNIT_FIELD_MAXHEALTH = 26, + UNIT_FIELD_MAXPOWER1 = 27, + UNIT_FIELD_MAXPOWER2 = 28, + UNIT_FIELD_MAXPOWER3 = 29, + UNIT_FIELD_MAXPOWER4 = 30, + UNIT_FIELD_MAXPOWER5 = 31, + UNIT_FIELD_LEVEL = 32, + UNIT_FIELD_FACTIONTEMPLATE = 33, + UNIT_FIELD_BYTES_0 = 34, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 35, + UNIT_VIRTUAL_ITEM_INFO = 38, + UNIT_FIELD_FLAGS = 44, + UNIT_FIELD_AURA = 45, + UNIT_FIELD_AURALEVELS = 101, + UNIT_FIELD_AURAAPPLICATIONS = 111, + UNIT_FIELD_AURAFLAGS = 121, + UNIT_FIELD_AURASTATE = 128, + UNIT_FIELD_BASEATTACKTIME = 129, + UNIT_FIELD_BOUNDINGRADIUS = 131, + UNIT_FIELD_COMBATREACH = 132, + UNIT_FIELD_WEAPONREACH = 133, + UNIT_FIELD_DISPLAYID = 134, + UNIT_FIELD_MOUNTDISPLAYID = 135, + UNIT_FIELD_MINDAMAGE = 136, + UNIT_FIELD_MAXDAMAGE = 137, + UNIT_FIELD_BYTES_1 = 138, + UNIT_FIELD_PETNUMBER = 139, + UNIT_FIELD_PET_NAME_TIMESTAMP = 140, + UNIT_FIELD_PETEXPERIENCE = 141, + UNIT_FIELD_PETNEXTLEVELEXP = 142, + UNIT_DYNAMIC_FLAGS = 143, + UNIT_CHANNEL_SPELL = 144, + UNIT_MOD_CAST_SPEED = 145, + UNIT_CREATED_BY_SPELL = 146, + UNIT_NPC_FLAGS = 147, + UNIT_NPC_EMOTESTATE = 148, + UNIT_FIELD_PADDING = 149, + PLAYER_SELECTION = 150, + PLAYER_DUEL_ARBITER = 152, + PLAYER_GUILDID = 154, + PLAYER_GUILDRANK = 155, + PLAYER_BYTES = 156, + PLAYER_BYTES_2 = 157, + PLAYER_BYTES_3 = 158, + PLAYER_DUEL_TEAM = 159, + PLAYER_GUILD_TIMESTAMP = 160, + PLAYER_FIELD_PAD_0 = 161, + PLAYER_FIELD_INV_SLOT_HEAD = 162, + PLAYER_FIELD_PACK_SLOT_1 = 208, + PLAYER_FIELD_BANK_SLOT_1 = 240, + PLAYER_FIELD_BANKBAG_SLOT_1 = 288, + PLAYER_FARSIGHT = 300, + PLAYER__FIELD_COMBO_TARGET = 302, + PLAYER_XP = 304, + PLAYER_NEXT_LEVEL_XP = 305, + PLAYER_SKILL_INFO_1_1 = 306, + PLAYER_QUEST_LOG_1_1 = 690, + PLAYER_CHARACTER_POINTS1 = 770, + PLAYER_CHARACTER_POINTS2 = 771, + PLAYER_TRACK_CREATURES = 772, + PLAYER_TRACK_RESOURCES = 773, + PLAYER_CHAT_FILTERS = 774, + PLAYER_BLOCK_PERCENTAGE = 775, + PLAYER_DODGE_PERCENTAGE = 776, + PLAYER_PARRY_PERCENTAGE = 777, + PLAYER_BASE_MANA = 778, + PLAYER_EXPLORED_ZONES_1 = 779, + PLAYER_REST_STATE_EXPERIENCE = 811, + PLAYER_FIELD_COINAGE = 812, + PLAYER_FIELD_STAT0 = 813, + PLAYER_FIELD_STAT1 = 814, + PLAYER_FIELD_STAT2 = 815, + PLAYER_FIELD_STAT3 = 816, + PLAYER_FIELD_STAT4 = 817, + PLAYER_FIELD_POSSTAT0 = 818, + PLAYER_FIELD_POSSTAT1 = 819, + PLAYER_FIELD_POSSTAT2 = 820, + PLAYER_FIELD_POSSTAT3 = 821, + PLAYER_FIELD_POSSTAT4 = 822, + PLAYER_FIELD_NEGSTAT0 = 823, + PLAYER_FIELD_NEGSTAT1 = 824, + PLAYER_FIELD_NEGSTAT2 = 825, + PLAYER_FIELD_NEGSTAT3 = 826, + PLAYER_FIELD_NEGSTAT4 = 827, + PLAYER_FIELD_RESISTANCES = 828, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 834, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 840, + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = 846, + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = 852, + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = 858, + PLAYER_FIELD_BYTES = 864, + PLAYER_FIELD_ATTACKPOWER = 865, + PLAYER_FIELD_ATTACKPOWERMODPOS = 866, + PLAYER_FIELD_ATTACKPOWERMODNEG = 867, + PLAYER_AMMO_ID = 868, + PLAYER_FIELD_PADDING = 869, + MAX = 870, + } + } +} diff --git a/Plugins/Beta_3734/Handlers/AuthHandler.cs b/Plugins/Beta_3734/Handlers/AuthHandler.cs new file mode 100644 index 0000000..bb8c59f --- /dev/null +++ b/Plugins/Beta_3734/Handlers/AuthHandler.cs @@ -0,0 +1,112 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using System.Net.Sockets; +using Common.Constants; +using Common.Structs; +using Common.Cryptography; + +namespace Beta_3734.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(System.Text.Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.RedirectPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(16 + realmName.Length + redirect.Length)); //Packet length + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3734/Handlers/CharHandler.cs b/Plugins/Beta_3734/Handlers/CharHandler.cs new file mode 100644 index 0000000..c32c358 --- /dev/null +++ b/Plugins/Beta_3734/Handlers/CharHandler.cs @@ -0,0 +1,216 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using Common.Commands; +using Common.Constants; +using Common.Structs; +using Common.Extensions; + +namespace Beta_3734.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x=> x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 2); + cha.SetPowerType(true); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + ulong Flags = packet.ReadUInt64(); + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3734/Handlers/WorldHandler.cs b/Plugins/Beta_3734/Handlers/WorldHandler.cs new file mode 100644 index 0000000..1ca21b6 --- /dev/null +++ b/Plugins/Beta_3734/Handlers/WorldHandler.cs @@ -0,0 +1,89 @@ +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Interfaces; +using Common.Constants; +using Common.Logging; +using Common.Extensions; + +namespace Beta_3734.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + accountdata.WriteBytes(new byte[80]); + manager.Send(accountdata); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 5; i++) + tutorial.WriteInt32(0); + manager.Send(tutorial); + + HandleQueryTime(ref packet, ref manager); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3734/Opcodes.cs b/Plugins/Beta_3734/Opcodes.cs new file mode 100644 index 0000000..f29b94e --- /dev/null +++ b/Plugins/Beta_3734/Opcodes.cs @@ -0,0 +1,82 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3734 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EA }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EC }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1EB }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DA }, + { global::Opcodes.SMSG_PONG, 0x1DB }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEA }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xEB }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEC }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE4 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFB }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CC }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x207 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xFF }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F2 }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x234 }, + { global::Opcodes.SMSG_EMOTE, 0x101 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x102 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x103 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/Beta_3734/PacketReader.cs b/Plugins/Beta_3734/PacketReader.cs new file mode 100644 index 0000000..b2f80cd --- /dev/null +++ b/Plugins/Beta_3734/PacketReader.cs @@ -0,0 +1,120 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3734 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3734/PacketWriter.cs b/Plugins/Beta_3734/PacketWriter.cs new file mode 100644 index 0000000..8723f42 --- /dev/null +++ b/Plugins/Beta_3734/PacketWriter.cs @@ -0,0 +1,119 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3734 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt32(opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + Size = (ushort)((Size >> 8) + ((Size & 0xFF) << 8)); + data[0] = (byte)(Size & 0xFF); + data[1] = (byte)(Size >> 8); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3734/Properties/AssemblyInfo.cs b/Plugins/Beta_3734/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c6f2f0f --- /dev/null +++ b/Plugins/Beta_3734/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta_3734")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3734")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("625f1463-6f82-4f56-9b1e-c8f5d366c0b9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3734/Sandbox.cs b/Plugins/Beta_3734/Sandbox.cs new file mode 100644 index 0000000..24c64f8 --- /dev/null +++ b/Plugins/Beta_3734/Sandbox.cs @@ -0,0 +1,32 @@ +using Beta_3734.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3734 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 3 (0.8.0) Sandbox"; + public int Build { get; set; } = 3734; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 9001; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3807/Beta_3807.csproj b/Plugins/Beta_3807/Beta_3807.csproj new file mode 100644 index 0000000..b16f21f --- /dev/null +++ b/Plugins/Beta_3807/Beta_3807.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {0C8366A3-73B1-405F-8742-C356AF0EAC8E} + Library + Properties + Beta_3807 + Beta_3807 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3807/Character.cs b/Plugins/Beta_3807/Character.cs new file mode 100644 index 0000000..1c61fb4 --- /dev/null +++ b/Plugins/Beta_3807/Character.cs @@ -0,0 +1,306 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(4.7222f); //Backwards SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.OBJECT_FIELD_PADDING, 0); + SetField(Fields.UNIT_FIELD_TARGET, (ulong)0); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, this.Mana); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_POWER3, this.Focus); + SetField(Fields.UNIT_FIELD_POWER4, this.Energy); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Mana); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_MAXPOWER3, this.Focus); + SetField(Fields.UNIT_FIELD_MAXPOWER4, this.Energy); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.UNIT_FIELD_STAT0, this.Strength); + SetField(Fields.UNIT_FIELD_STAT1, this.Agility); + SetField(Fields.UNIT_FIELD_STAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_STAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_FLAGS, 0); + SetField(Fields.UNIT_FIELD_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_BYTES_3, (uint)this.Gender); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + + for (int i = 0; i < 32; i++) + SetField(Fields.PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + OBJECT_END = 6, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_CHANNEL_OBJECT = 18, + UNIT_FIELD_HEALTH = 20, + UNIT_FIELD_POWER1 = 21, + UNIT_FIELD_POWER2 = 22, + UNIT_FIELD_POWER3 = 23, + UNIT_FIELD_POWER4 = 24, + UNIT_FIELD_POWER5 = 25, + UNIT_FIELD_MAXHEALTH = 26, + UNIT_FIELD_MAXPOWER1 = 27, + UNIT_FIELD_MAXPOWER2 = 28, + UNIT_FIELD_MAXPOWER3 = 29, + UNIT_FIELD_MAXPOWER4 = 30, + UNIT_FIELD_MAXPOWER5 = 31, + UNIT_FIELD_LEVEL = 32, + UNIT_FIELD_FACTIONTEMPLATE = 33, + UNIT_FIELD_BYTES_0 = 34, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 35, + UNIT_VIRTUAL_ITEM_INFO = 38, + UNIT_FIELD_FLAGS = 44, + UNIT_FIELD_AURA = 45, + UNIT_FIELD_AURALEVELS = 101, + UNIT_FIELD_AURAAPPLICATIONS = 111, + UNIT_FIELD_AURAFLAGS = 121, + UNIT_FIELD_AURASTATE = 128, + UNIT_FIELD_BASEATTACKTIME = 129, + UNIT_FIELD_BOUNDINGRADIUS = 131, + UNIT_FIELD_COMBATREACH = 132, + UNIT_FIELD_WEAPONREACH = 133, + UNIT_FIELD_DISPLAYID = 134, + UNIT_FIELD_MOUNTDISPLAYID = 135, + UNIT_FIELD_MINDAMAGE = 136, + UNIT_FIELD_MAXDAMAGE = 137, + UNIT_FIELD_BYTES_1 = 138, + UNIT_FIELD_PETNUMBER = 139, + UNIT_FIELD_PET_NAME_TIMESTAMP = 140, + UNIT_FIELD_PETEXPERIENCE = 141, + UNIT_FIELD_PETNEXTLEVELEXP = 142, + UNIT_DYNAMIC_FLAGS = 143, + UNIT_CHANNEL_SPELL = 144, + UNIT_MOD_CAST_SPEED = 145, + UNIT_CREATED_BY_SPELL = 146, + UNIT_NPC_FLAGS = 147, + UNIT_NPC_EMOTESTATE = 148, + UNIT_TRAINING_POINTS = 149, + UNIT_FIELD_STAT0 = 150, + UNIT_FIELD_STAT1 = 151, + UNIT_FIELD_STAT2 = 152, + UNIT_FIELD_STAT3 = 153, + UNIT_FIELD_STAT4 = 154, + UNIT_FIELD_RESISTANCES = 155, + UNIT_FIELD_ATTACKPOWER = 162, + UNIT_FIELD_BASE_MANA = 163, + PLAYER_SELECTION = 164, + PLAYER_DUEL_ARBITER = 166, + PLAYER_GUILDID = 168, + PLAYER_GUILDRANK = 169, + PLAYER_BYTES = 170, + PLAYER_BYTES_2 = 171, + PLAYER_BYTES_3 = 172, + PLAYER_DUEL_TEAM = 173, + PLAYER_GUILD_TIMESTAMP = 174, + PLAYER_FIELD_PAD_0 = 175, + PLAYER_FIELD_INV_SLOT_HEAD = 176, + PLAYER_FIELD_PACK_SLOT_1 = 222, + PLAYER_FIELD_BANK_SLOT_1 = 254, + PLAYER_FIELD_BANKBAG_SLOT_1 = 302, + PLAYER_FARSIGHT = 314, + PLAYER__FIELD_COMBO_TARGET = 316, + PLAYER_XP = 318, + PLAYER_NEXT_LEVEL_XP = 319, + PLAYER_SKILL_INFO_1_1 = 320, + PLAYER_QUEST_LOG_1_1 = 704, + PLAYER_CHARACTER_POINTS1 = 764, + PLAYER_CHARACTER_POINTS2 = 765, + PLAYER_TRACK_CREATURES = 766, + PLAYER_TRACK_RESOURCES = 767, + PLAYER_CHAT_FILTERS = 768, + PLAYER_BLOCK_PERCENTAGE = 769, + PLAYER_DODGE_PERCENTAGE = 770, + PLAYER_PARRY_PERCENTAGE = 771, + PLAYER_CRIT_PERCENTAGE = 772, + PLAYER_EXPLORED_ZONES_1 = 773, + PLAYER_REST_STATE_EXPERIENCE = 805, + PLAYER_FIELD_COINAGE = 806, + PLAYER_FIELD_POSSTAT0 = 807, + PLAYER_FIELD_POSSTAT1 = 808, + PLAYER_FIELD_POSSTAT2 = 809, + PLAYER_FIELD_POSSTAT3 = 810, + PLAYER_FIELD_POSSTAT4 = 811, + PLAYER_FIELD_NEGSTAT0 = 812, + PLAYER_FIELD_NEGSTAT1 = 813, + PLAYER_FIELD_NEGSTAT2 = 814, + PLAYER_FIELD_NEGSTAT3 = 815, + PLAYER_FIELD_NEGSTAT4 = 816, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 817, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 824, + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = 831, + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = 838, + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = 845, + PLAYER_FIELD_BYTES = 852, + PLAYER_FIELD_ATTACKPOWERMODPOS = 853, + PLAYER_FIELD_ATTACKPOWERMODNEG = 854, + PLAYER_AMMO_ID = 855, + MAX = 856, + } + } +} diff --git a/Plugins/Beta_3807/Handlers/AuthHandler.cs b/Plugins/Beta_3807/Handlers/AuthHandler.cs new file mode 100644 index 0000000..2385921 --- /dev/null +++ b/Plugins/Beta_3807/Handlers/AuthHandler.cs @@ -0,0 +1,112 @@ +using Common.Constants; +using Common.Cryptography; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(System.Text.Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.RedirectPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(16 + realmName.Length + redirect.Length)); //Packet length + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3807/Handlers/CharHandler.cs b/Plugins/Beta_3807/Handlers/CharHandler.cs new file mode 100644 index 0000000..988459f --- /dev/null +++ b/Plugins/Beta_3807/Handlers/CharHandler.cs @@ -0,0 +1,216 @@ +using Common.Commands; +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 2); + cha.SetPowerType(); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + ulong Flags = packet.ReadUInt64(); + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3807/Handlers/WorldHandler.cs b/Plugins/Beta_3807/Handlers/WorldHandler.cs new file mode 100644 index 0000000..0b17933 --- /dev/null +++ b/Plugins/Beta_3807/Handlers/WorldHandler.cs @@ -0,0 +1,90 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + accountdata.WriteBytes(new byte[80]); + manager.Send(accountdata); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + manager.Send(character.BuildUpdate()); + + HandleQueryTime(ref packet, ref manager); + + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3807/Opcodes.cs b/Plugins/Beta_3807/Opcodes.cs new file mode 100644 index 0000000..8694e53 --- /dev/null +++ b/Plugins/Beta_3807/Opcodes.cs @@ -0,0 +1,82 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EA }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EC }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1EB }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DA }, + { global::Opcodes.SMSG_PONG, 0x1DB }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEA }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xEB }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEC }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE4 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFB }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CC }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x207 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xFF }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F2 }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x234 }, + { global::Opcodes.SMSG_EMOTE, 0x101 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x102 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x103 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/Beta_3807/PacketReader.cs b/Plugins/Beta_3807/PacketReader.cs new file mode 100644 index 0000000..12ba355 --- /dev/null +++ b/Plugins/Beta_3807/PacketReader.cs @@ -0,0 +1,120 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3807/PacketWriter.cs b/Plugins/Beta_3807/PacketWriter.cs new file mode 100644 index 0000000..a7ff64a --- /dev/null +++ b/Plugins/Beta_3807/PacketWriter.cs @@ -0,0 +1,119 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt16((ushort)opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + //Size = (ushort)((Size >> 8) + ((Size & 0xFF) << 8)); + data[0] = (byte)(Size >> 8); + data[1] = (byte)(Size & 255); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3807/Properties/AssemblyInfo.cs b/Plugins/Beta_3807/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c80a709 --- /dev/null +++ b/Plugins/Beta_3807/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta_3807")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3807")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0c8366a3-73b1-405f-8742-c356af0eac8e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3807/Sandbox.cs b/Plugins/Beta_3807/Sandbox.cs new file mode 100644 index 0000000..7d70697 --- /dev/null +++ b/Plugins/Beta_3807/Sandbox.cs @@ -0,0 +1,32 @@ +using Beta_3807.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3807 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 3 (0.9.0) Sandbox"; + public int Build { get; set; } = 3807; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 9001; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3892/Beta_3892.csproj b/Plugins/Beta_3892/Beta_3892.csproj new file mode 100644 index 0000000..94fe6ff --- /dev/null +++ b/Plugins/Beta_3892/Beta_3892.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {276409AF-8D5E-415B-A61E-BE460AE2FDBC} + Library + Properties + Beta_3892 + Beta_3892 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3892/Character.cs b/Plugins/Beta_3892/Character.cs new file mode 100644 index 0000000..d2ee628 --- /dev/null +++ b/Plugins/Beta_3892/Character.cs @@ -0,0 +1,313 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(4.7222f); //Backwards SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.OBJECT_FIELD_PADDING, 0); + SetField(Fields.UNIT_FIELD_TARGET, (ulong)0); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, this.Mana); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_POWER3, this.Focus); + SetField(Fields.UNIT_FIELD_POWER4, this.Energy); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Mana); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_MAXPOWER3, this.Focus); + SetField(Fields.UNIT_FIELD_MAXPOWER4, this.Energy); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.UNIT_FIELD_STAT0, this.Strength); + SetField(Fields.UNIT_FIELD_STAT1, this.Agility); + SetField(Fields.UNIT_FIELD_STAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_STAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_FLAGS, 0); + SetField(Fields.UNIT_FIELD_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_BYTES_3, (uint)this.Gender); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + + for (int i = 0; i < 32; i++) + SetField(Fields.PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_CHANNEL_OBJECT = 18, + UNIT_FIELD_HEALTH = 20, + UNIT_FIELD_POWER1 = 21, + UNIT_FIELD_POWER2 = 22, + UNIT_FIELD_POWER3 = 23, + UNIT_FIELD_POWER4 = 24, + UNIT_FIELD_POWER5 = 25, + UNIT_FIELD_MAXHEALTH = 26, + UNIT_FIELD_MAXPOWER1 = 27, + UNIT_FIELD_MAXPOWER2 = 28, + UNIT_FIELD_MAXPOWER3 = 29, + UNIT_FIELD_MAXPOWER4 = 30, + UNIT_FIELD_MAXPOWER5 = 31, + UNIT_FIELD_LEVEL = 32, + UNIT_FIELD_FACTIONTEMPLATE = 33, + UNIT_FIELD_BYTES_0 = 34, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 35, + UNIT_VIRTUAL_ITEM_INFO = 38, + UNIT_FIELD_FLAGS = 44, + UNIT_FIELD_AURA = 45, + UNIT_FIELD_AURALEVELS = 101, + UNIT_FIELD_AURAAPPLICATIONS = 111, + UNIT_FIELD_AURAFLAGS = 121, + UNIT_FIELD_AURASTATE = 128, + UNIT_FIELD_BASEATTACKTIME = 129, + UNIT_FIELD_BOUNDINGRADIUS = 131, + UNIT_FIELD_COMBATREACH = 132, + UNIT_FIELD_DISPLAYID = 133, + UNIT_FIELD_NATIVEDISPLAYID = 134, + UNIT_FIELD_MOUNTDISPLAYID = 135, + UNIT_FIELD_MINDAMAGE = 136, + UNIT_FIELD_MAXDAMAGE = 137, + UNIT_FIELD_BYTES_1 = 138, + UNIT_FIELD_PETNUMBER = 139, + UNIT_FIELD_PET_NAME_TIMESTAMP = 140, + UNIT_FIELD_PETEXPERIENCE = 141, + UNIT_FIELD_PETNEXTLEVELEXP = 142, + UNIT_DYNAMIC_FLAGS = 143, + UNIT_CHANNEL_SPELL = 144, + UNIT_MOD_CAST_SPEED = 145, + UNIT_CREATED_BY_SPELL = 146, + UNIT_NPC_FLAGS = 147, + UNIT_NPC_EMOTESTATE = 148, + UNIT_TRAINING_POINTS = 149, + UNIT_FIELD_STAT0 = 150, + UNIT_FIELD_STAT1 = 151, + UNIT_FIELD_STAT2 = 152, + UNIT_FIELD_STAT3 = 153, + UNIT_FIELD_STAT4 = 154, + UNIT_FIELD_RESISTANCES = 155, + UNIT_FIELD_ATTACKPOWER = 162, + UNIT_FIELD_BASE_MANA = 163, + UNIT_ATTACK_POWER_MODS = 164, + UNIT_FIELD_PADDING = 165, + PLAYER_SELECTION = 166, + PLAYER_DUEL_ARBITER = 168, + PLAYER_GUILDID = 170, + PLAYER_GUILDRANK = 171, + PLAYER_BYTES = 172, + PLAYER_BYTES_2 = 173, + PLAYER_BYTES_3 = 174, + PLAYER_DUEL_TEAM = 175, + PLAYER_GUILD_TIMESTAMP = 176, + PLAYER_FIELD_PAD_0 = 177, + PLAYER_FIELD_INV_SLOT_HEAD = 178, + PLAYER_FIELD_PACK_SLOT_1 = 224, + PLAYER_FIELD_BANK_SLOT_1 = 256, + PLAYER_FIELD_BANKBAG_SLOT_1 = 304, + PLAYER_FIELD_VENDORBUYBACK_SLOT = 316, + PLAYER_FARSIGHT = 318, + PLAYER_FIELD_COMBO_TARGET = 320, + PLAYER_FIELD_BUYBACK_NPC = 322, + PLAYER_XP = 324, + PLAYER_NEXT_LEVEL_XP = 325, + PLAYER_SKILL_INFO_1_1 = 326, + PLAYER_QUEST_LOG_1_1 = 710, + PLAYER_CHARACTER_POINTS1 = 770, + PLAYER_CHARACTER_POINTS2 = 771, + PLAYER_TRACK_CREATURES = 772, + PLAYER_TRACK_RESOURCES = 773, + PLAYER_CHAT_FILTERS = 774, + PLAYER_BLOCK_PERCENTAGE = 775, + PLAYER_DODGE_PERCENTAGE = 776, + PLAYER_PARRY_PERCENTAGE = 777, + PLAYER_CRIT_PERCENTAGE = 778, + PLAYER_EXPLORED_ZONES_1 = 779, + PLAYER_REST_STATE_EXPERIENCE = 811, + PLAYER_FIELD_COINAGE = 812, + PLAYER_FIELD_POSSTAT0 = 813, + PLAYER_FIELD_POSSTAT1 = 814, + PLAYER_FIELD_POSSTAT2 = 815, + PLAYER_FIELD_POSSTAT3 = 816, + PLAYER_FIELD_POSSTAT4 = 817, + PLAYER_FIELD_NEGSTAT0 = 818, + PLAYER_FIELD_NEGSTAT1 = 819, + PLAYER_FIELD_NEGSTAT2 = 820, + PLAYER_FIELD_NEGSTAT3 = 821, + PLAYER_FIELD_NEGSTAT4 = 822, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 823, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 830, + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = 837, + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = 844, + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = 851, + PLAYER_FIELD_BYTES = 858, + PLAYER_AMMO_ID = 859, + PLAYER_FIELD_PVP_MEDALS = 860, + PLAYER_FIELD_BUYBACK_ITEM_ID = 861, + PLAYER_FIELD_BUYBACK_RANDOM_PROPERTIES_ID = 862, + PLAYER_FIELD_BUYBACK_SEED = 863, + PLAYER_FIELD_BUYBACK_PRICE = 864, + PLAYER_FIELD_PADDING = 865, + MAX = 866, + } + } +} diff --git a/Plugins/Beta_3892/Handlers/AuthHandler.cs b/Plugins/Beta_3892/Handlers/AuthHandler.cs new file mode 100644 index 0000000..8cd6e75 --- /dev/null +++ b/Plugins/Beta_3892/Handlers/AuthHandler.cs @@ -0,0 +1,120 @@ +using Common.Constants; +using Common.Cryptography; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteUInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + writer.WriteBytes(ClientAuth.LogonProof(packet)); + break; + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(21 + realmName.Length + redirect.Length)); //Packet length + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteFloat(0); + + writer.WriteUInt8(0); + writer.WriteUInt8(1); + writer.WriteUInt8(2); + writer.WriteUInt8(0); + writer.WriteUInt8(0x2); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3892/Handlers/CharHandler.cs b/Plugins/Beta_3892/Handlers/CharHandler.cs new file mode 100644 index 0000000..9c36a12 --- /dev/null +++ b/Plugins/Beta_3892/Handlers/CharHandler.cs @@ -0,0 +1,216 @@ +using Common.Commands; +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 2); + cha.SetPowerType(); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x28); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x2C); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + ulong Flags = packet.ReadUInt64(); + manager.Account.ActiveCharacter.Location.Update(packet, true); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3892/Handlers/WorldHandler.cs b/Plugins/Beta_3892/Handlers/WorldHandler.cs new file mode 100644 index 0000000..539fb7d --- /dev/null +++ b/Plugins/Beta_3892/Handlers/WorldHandler.cs @@ -0,0 +1,89 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + accountdata.WriteBytes(new byte[80]); + manager.Send(accountdata); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + HandleQueryTime(ref packet, ref manager); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3892/Opcodes.cs b/Plugins/Beta_3892/Opcodes.cs new file mode 100644 index 0000000..989f1b5 --- /dev/null +++ b/Plugins/Beta_3892/Opcodes.cs @@ -0,0 +1,82 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EA }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EC }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1EB }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DA }, + { global::Opcodes.SMSG_PONG, 0x1DB }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEA }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xEB }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEC }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE4 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFB }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CC }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x207 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0xFF }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F2 }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x234 }, + { global::Opcodes.SMSG_EMOTE, 0x101 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x102 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x103 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/Beta_3892/PacketReader.cs b/Plugins/Beta_3892/PacketReader.cs new file mode 100644 index 0000000..1a7a928 --- /dev/null +++ b/Plugins/Beta_3892/PacketReader.cs @@ -0,0 +1,120 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3892/PacketWriter.cs b/Plugins/Beta_3892/PacketWriter.cs new file mode 100644 index 0000000..71a973b --- /dev/null +++ b/Plugins/Beta_3892/PacketWriter.cs @@ -0,0 +1,119 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt16((ushort)opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + //Size = (ushort)((Size >> 8) + ((Size & 0xFF) << 8)); + data[0] = (byte)(Size >> 8); + data[1] = (byte)(Size & 255); + } + return data; + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3892/Properties/AssemblyInfo.cs b/Plugins/Beta_3892/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2d9b2c5 --- /dev/null +++ b/Plugins/Beta_3892/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta_3892")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3892")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("276409af-8d5e-415b-a61e-be460ae2fdbc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3892/Sandbox.cs b/Plugins/Beta_3892/Sandbox.cs new file mode 100644 index 0000000..82e0fa0 --- /dev/null +++ b/Plugins/Beta_3892/Sandbox.cs @@ -0,0 +1,32 @@ +using Beta_3892.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3892 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 3 (0.10.0/0.11.0) Sandbox"; + public int Build { get; set; } = 3892; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 8129; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/Beta_3988/Beta_3988.csproj b/Plugins/Beta_3988/Beta_3988.csproj new file mode 100644 index 0000000..55ed349 --- /dev/null +++ b/Plugins/Beta_3988/Beta_3988.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {85FA1408-8526-4F9E-9A04-C9C78D2F31F0} + Library + Properties + Beta_3988 + Beta_3988 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/Plugins/Beta_3988/Character.cs b/Plugins/Beta_3988/Character.cs new file mode 100644 index 0000000..7580cee --- /dev/null +++ b/Plugins/Beta_3988/Character.cs @@ -0,0 +1,314 @@ +using Common.Extensions; +using Common.Interfaces; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common.Constants; + +namespace Beta_3988 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 31) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(2); //UpdateType + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.0f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.7222f); //SwimSpeed + writer.WriteFloat(4.7222f); //Backwards SwimSpeed + writer.WriteFloat(3.14f); //TurnSpeed + + writer.WriteUInt32(1); //Flags, 1 - Player + writer.WriteUInt32(1); //AttackCycle + writer.WriteUInt32(0); //TimerId + writer.WriteUInt64(0); //VictimGuid + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_ENTRY, 0); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + SetField(Fields.OBJECT_FIELD_PADDING, 0); + SetField(Fields.UNIT_FIELD_TARGET, (ulong)0); + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER2, 0); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER2, this.Rage); + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.UNIT_FIELD_STAT0, this.Strength); + SetField(Fields.UNIT_FIELD_STAT1, this.Agility); + SetField(Fields.UNIT_FIELD_STAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_STAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_STAT4, this.Spirit); + SetField(Fields.UNIT_FIELD_FLAGS, 0); + SetField(Fields.UNIT_FIELD_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_SELECTION, (ulong)0); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_BYTES_3, (uint)this.Gender); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + + for (int i = 0; i < 32; i++) + SetField(Fields.PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF); + + //FillInPartialObjectData + writer.WriteUInt8(maskSize); //UpdateMaskBlocks + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_PERSUADED = 18, + UNIT_FIELD_CHANNEL_OBJECT = 20, + UNIT_FIELD_HEALTH = 22, + UNIT_FIELD_POWER1 = 23, + UNIT_FIELD_POWER2 = 24, + UNIT_FIELD_POWER3 = 25, + UNIT_FIELD_POWER4 = 26, + UNIT_FIELD_POWER5 = 27, + UNIT_FIELD_MAXHEALTH = 28, + UNIT_FIELD_MAXPOWER1 = 29, + UNIT_FIELD_MAXPOWER2 = 30, + UNIT_FIELD_MAXPOWER3 = 31, + UNIT_FIELD_MAXPOWER4 = 32, + UNIT_FIELD_MAXPOWER5 = 33, + UNIT_FIELD_LEVEL = 34, + UNIT_FIELD_FACTIONTEMPLATE = 35, + UNIT_FIELD_BYTES_0 = 36, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 37, + UNIT_VIRTUAL_ITEM_INFO = 40, + UNIT_FIELD_FLAGS = 46, + UNIT_FIELD_AURA = 47, + UNIT_FIELD_AURALEVELS = 103, + UNIT_FIELD_AURAAPPLICATIONS = 113, + UNIT_FIELD_AURAFLAGS = 123, + UNIT_FIELD_AURASTATE = 130, + UNIT_FIELD_BASEATTACKTIME = 131, + UNIT_FIELD_RANGEDATTACKTIME = 133, + UNIT_FIELD_BOUNDINGRADIUS = 134, + UNIT_FIELD_COMBATREACH = 135, + UNIT_FIELD_DISPLAYID = 136, + UNIT_FIELD_NATIVEDISPLAYID = 137, + UNIT_FIELD_MOUNTDISPLAYID = 138, + UNIT_FIELD_MINDAMAGE = 139, + UNIT_FIELD_MAXDAMAGE = 140, + UNIT_FIELD_BYTES_1 = 141, + UNIT_FIELD_PETNUMBER = 142, + UNIT_FIELD_PET_NAME_TIMESTAMP = 143, + UNIT_FIELD_PETEXPERIENCE = 144, + UNIT_FIELD_PETNEXTLEVELEXP = 145, + UNIT_DYNAMIC_FLAGS = 146, + UNIT_CHANNEL_SPELL = 147, + UNIT_MOD_CAST_SPEED = 148, + UNIT_CREATED_BY_SPELL = 149, + UNIT_NPC_FLAGS = 150, + UNIT_NPC_EMOTESTATE = 151, + UNIT_TRAINING_POINTS = 152, + UNIT_FIELD_STAT0 = 153, + UNIT_FIELD_STAT1 = 154, + UNIT_FIELD_STAT2 = 155, + UNIT_FIELD_STAT3 = 156, + UNIT_FIELD_STAT4 = 157, + UNIT_FIELD_RESISTANCES = 158, + UNIT_FIELD_ATTACKPOWER = 165, + UNIT_FIELD_BASE_MANA = 166, + UNIT_FIELD_ATTACK_POWER_MODS = 167, + UNIT_FIELD_BYTES_2 = 168, + UNIT_FIELD_RANGEDATTACKPOWER = 169, + UNIT_FIELD_RANGED_ATTACK_POWER_MODS = 170, + UNIT_FIELD_MINRANGEDDAMAGE = 171, + UNIT_FIELD_MAXRANGEDDAMAGE = 172, + UNIT_FIELD_PADDING = 173, + PLAYER_SELECTION = 174, + PLAYER_DUEL_ARBITER = 176, + PLAYER_FLAGS = 178, + PLAYER_GUILDID = 179, + PLAYER_GUILDRANK = 180, + PLAYER_BYTES = 181, + PLAYER_BYTES_2 = 182, + PLAYER_BYTES_3 = 183, + PLAYER_DUEL_TEAM = 184, + PLAYER_GUILD_TIMESTAMP = 185, + PLAYER_FIELD_INV_SLOT_HEAD = 186, + PLAYER_FIELD_PACK_SLOT_1 = 232, + PLAYER_FIELD_BANK_SLOT_1 = 264, + PLAYER_FIELD_BANKBAG_SLOT_1 = 312, + PLAYER_FIELD_VENDORBUYBACK_SLOT = 324, + PLAYER_FARSIGHT = 326, + PLAYER_FIELD_COMBO_TARGET = 328, + PLAYER_FIELD_BUYBACK_NPC = 330, + PLAYER_XP = 332, + PLAYER_NEXT_LEVEL_XP = 333, + PLAYER_SKILL_INFO_1_1 = 334, + PLAYER_QUEST_LOG_1_1 = 718, + PLAYER_CHARACTER_POINTS1 = 778, + PLAYER_CHARACTER_POINTS2 = 779, + PLAYER_TRACK_CREATURES = 780, + PLAYER_TRACK_RESOURCES = 781, + PLAYER_CHAT_FILTERS = 782, + PLAYER_BLOCK_PERCENTAGE = 783, + PLAYER_DODGE_PERCENTAGE = 784, + PLAYER_PARRY_PERCENTAGE = 785, + PLAYER_CRIT_PERCENTAGE = 786, + PLAYER_EXPLORED_ZONES_1 = 787, + PLAYER_REST_STATE_EXPERIENCE = 819, + PLAYER_FIELD_COINAGE = 820, + PLAYER_FIELD_POSSTAT0 = 821, + PLAYER_FIELD_POSSTAT1 = 822, + PLAYER_FIELD_POSSTAT2 = 823, + PLAYER_FIELD_POSSTAT3 = 824, + PLAYER_FIELD_POSSTAT4 = 825, + PLAYER_FIELD_NEGSTAT0 = 826, + PLAYER_FIELD_NEGSTAT1 = 827, + PLAYER_FIELD_NEGSTAT2 = 828, + PLAYER_FIELD_NEGSTAT3 = 829, + PLAYER_FIELD_NEGSTAT4 = 830, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 831, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 838, + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = 845, + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = 852, + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = 859, + PLAYER_FIELD_BYTES = 866, + PLAYER_AMMO_ID = 867, + PLAYER_FIELD_PVP_MEDALS = 868, + PLAYER_FIELD_BUYBACK_ITEM_ID = 869, + PLAYER_FIELD_BUYBACK_RANDOM_PROPERTIES_ID = 870, + PLAYER_FIELD_BUYBACK_SEED = 871, + PLAYER_FIELD_BUYBACK_PRICE = 872, + PLAYER_FIELD_PADDING = 873, + MAX = 874, + } + } +} diff --git a/Plugins/Beta_3988/Handlers/AuthHandler.cs b/Plugins/Beta_3988/Handlers/AuthHandler.cs new file mode 100644 index 0000000..992e974 --- /dev/null +++ b/Plugins/Beta_3988/Handlers/AuthHandler.cs @@ -0,0 +1,121 @@ +using Common.Constants; +using Common.Cryptography; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Net.Sockets; +using System.Text; + +namespace Beta_3988.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + ClientAuth.Clear(); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0x0C); //AUTH_OK + writer.WriteUInt32(0); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + writer.WriteBytes(ClientAuth.LogonProof(packet)); + break; + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort); + + writer.WriteUInt8(0x10); + writer.WriteUInt16((ushort)(21 + realmName.Length + redirect.Length)); //Packet length + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + writer.WriteFloat(0); + + writer.WriteUInt8(0); + writer.WriteUInt8(1); + writer.WriteUInt8(2); + writer.WriteUInt8(0); + writer.WriteUInt8(0x2); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/Beta_3988/Handlers/CharHandler.cs b/Plugins/Beta_3988/Handlers/CharHandler.cs new file mode 100644 index 0000000..da9b7ff --- /dev/null +++ b/Plugins/Beta_3988/Handlers/CharHandler.cs @@ -0,0 +1,224 @@ +using Common.Commands; +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x=> x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 2); + cha.SetPowerType(); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x2D); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x34); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + uint opcode = packet.Opcode; + long pos = packet.Position; + + var character = manager.Account.ActiveCharacter; + ulong Flags = packet.ReadUInt64(); + character.Location.Update(packet, true); + + packet.Position = pos; + PacketWriter writer = new PacketWriter(opcode, Sandbox.Instance.Opcodes[opcode].ToString()); + writer.WriteBytes(packet.ReadToEnd()); + manager.Send(writer); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/Beta_3988/Handlers/WorldHandler.cs b/Plugins/Beta_3988/Handlers/WorldHandler.cs new file mode 100644 index 0000000..8c86d85 --- /dev/null +++ b/Plugins/Beta_3988/Handlers/WorldHandler.cs @@ -0,0 +1,87 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + accountdata.WriteBytes(new byte[80]); + manager.Send(accountdata); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/Beta_3988/Opcodes.cs b/Plugins/Beta_3988/Opcodes.cs new file mode 100644 index 0000000..24cd28d --- /dev/null +++ b/Plugins/Beta_3988/Opcodes.cs @@ -0,0 +1,84 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EC }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EE }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1ED }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DC }, + { global::Opcodes.SMSG_PONG, 0x1DD }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEC }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xED }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEE }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE6 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFD }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CE }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x209 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0x101 }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F4 }, + { global::Opcodes.CMSG_UPDATE_ACCOUNT_DATA, 0x20B }, + { global::Opcodes.SMSG_UPDATE_ACCOUNT_DATA, 0x20C }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x236 }, + { global::Opcodes.SMSG_EMOTE, 0x103 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x104 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x105 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/Beta_3988/PacketReader.cs b/Plugins/Beta_3988/PacketReader.cs new file mode 100644 index 0000000..eea72b6 --- /dev/null +++ b/Plugins/Beta_3988/PacketReader.cs @@ -0,0 +1,139 @@ +using Common.Cryptography; +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get => BaseStream.Position; set => BaseStream.Position = value; } + + private const int SHA_DIGEST_LENGTH = 40; + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if (parse) + { + Decode(ref data); + ushort size = this.ReadUInt16(); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = this.ReadUInt32(); + } + } + + private void Decode(ref byte[] data) + { + if (!ClientAuth.Encode || data.Length < 6) + return; + + for (int i = 0; i < 6; i++) + { + ClientAuth.Key[1] %= SHA_DIGEST_LENGTH; + byte x = (byte)((data[i] - ClientAuth.Key[0]) ^ ClientAuth.SS_Hash[ClientAuth.Key[1]]); + ++ClientAuth.Key[1]; + ClientAuth.Key[0] = data[i]; + data[i] = x; + } + } + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/Beta_3988/PacketWriter.cs b/Plugins/Beta_3988/PacketWriter.cs new file mode 100644 index 0000000..bac5b4a --- /dev/null +++ b/Plugins/Beta_3988/PacketWriter.cs @@ -0,0 +1,135 @@ +using Common.Cryptography; +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + private const int SHA_DIGEST_LENGTH = 40; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt16((ushort)opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + data[0] = (byte)(Size >> 8); + data[1] = (byte)(Size & 255); + Encode(ref data); + } + return data; + } + + private void Encode(ref byte[] data) + { + if (!ClientAuth.Encode || data.Length < 4) + return; + + for (int i = 0; i < 4; i++) + { + ClientAuth.Key[3] %= SHA_DIGEST_LENGTH; + byte x = (byte)((data[i] ^ ClientAuth.SS_Hash[ClientAuth.Key[3]]) + ClientAuth.Key[2]); + ++ClientAuth.Key[3]; + data[i] = ClientAuth.Key[2] = x; + } + } + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/Beta_3988/Properties/AssemblyInfo.cs b/Plugins/Beta_3988/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e611338 --- /dev/null +++ b/Plugins/Beta_3988/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Beta_3988")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Beta_3988")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("85fa1408-8526-4f9e-9a04-c9c78d2f31f0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Beta_3988/Sandbox.cs b/Plugins/Beta_3988/Sandbox.cs new file mode 100644 index 0000000..e2ff225 --- /dev/null +++ b/Plugins/Beta_3988/Sandbox.cs @@ -0,0 +1,31 @@ +using Beta_3988.Handlers; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Beta_3988 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "|cFF00FFFFBeta 3 (0.12.0) Sandbox"; + public int Build { get; set; } = 3988; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 8129; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/TBC_Alpha_5610/Character.cs b/Plugins/TBC_Alpha_5610/Character.cs new file mode 100644 index 0000000..b098bc2 --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Character.cs @@ -0,0 +1,478 @@ +using Common.Interfaces; +using Common.Structs; +using Common.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Common.Constants; + +namespace TBC_Alpha_5610 +{ + [Serializable] + public class Character : ICharacter + { + public int Build { get; set; } = Sandbox.Instance.Build; + + public ulong Guid { get; set; } + public string Name { get; set; } + public byte Race { get; set; } + public byte Class { get; set; } + public byte Gender { get; set; } + public byte Skin { get; set; } + public byte Face { get; set; } + public byte HairStyle { get; set; } + public byte HairColor { get; set; } + public byte FacialHair { get; set; } + public uint Level { get; set; } = 11; + public uint Zone { get; set; } + public Location Location { get; set; } + public bool IsOnline { get; set; } = false; + public uint Health { get; set; } = 100; + public uint Mana { get; set; } = 100; + public uint Rage { get; set; } = 1000; + public uint Focus { get; set; } = 100; + public uint Energy { get; set; } = 100; + public uint Strength { get; set; } = 10; + public uint Agility { get; set; } = 10; + public uint Stamina { get; set; } = 10; + public uint Intellect { get; set; } = 10; + public uint Spirit { get; set; } = 10; + public byte PowerType { get; set; } = 1; + public byte ResetedState { get; set; } = 3; + public StandState StandState { get; set; } = StandState.STANDING; + public bool IsTeleporting { get; set; } = false; + public uint DisplayId { get; set; } + public uint MountDisplayId { get; set; } + + public IPacketWriter BuildUpdate() + { + byte maskSize = ((int)Fields.MAX + 32) / 32; + SortedDictionary fieldData = new SortedDictionary(); + byte[] maskArray = new byte[maskSize * 4]; + + Action SetField = (place, value) => this.SetField((int)place, value, ref fieldData, ref maskArray); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_UPDATE_OBJECT], "SMSG_UPDATE_OBJECT"); + writer.WriteUInt32(1); //Number of transactions + writer.WriteUInt8(0); + writer.WriteUInt8(3); //UpdateType + writer.WriteUInt8(0xFF); //?? + writer.WriteUInt64(this.Guid); + writer.WriteUInt8(4); //ObjectType, 4 = Player + writer.WriteUInt8(0x71); + + writer.WriteUInt32(0); //MovementFlagMask + writer.WriteUInt32((uint)Environment.TickCount); + writer.WriteFloat(Location.X); //x + writer.WriteFloat(Location.Y); //y + writer.WriteFloat(Location.Z); //z + writer.WriteFloat(Location.O); //w (o) + writer.WriteUInt32(0); //Falltime + + writer.WriteFloat(0f); //Transport vector4?? + writer.WriteFloat(1f); + writer.WriteFloat(0f); + writer.WriteFloat(0f); + + writer.WriteFloat(2.5f); //WalkSpeed + writer.WriteFloat(7.5f); //RunSpeed + writer.WriteFloat(2.5f); //Backwards WalkSpeed + writer.WriteFloat(4.722222f); //FlySpeed + writer.WriteFloat(2.5f); //Backwards FlySpeed + writer.WriteFloat(3.141593f); //TurnSpeed + + writer.WriteUInt32(1); + + SetField(Fields.OBJECT_FIELD_GUID, this.Guid); + SetField(Fields.OBJECT_FIELD_TYPE, (uint)0x19); + SetField(Fields.OBJECT_FIELD_SCALE_X, 1f); + + SetField(Fields.UNIT_FIELD_HEALTH, this.Health); + SetField(Fields.UNIT_FIELD_POWER1, 0); + SetField(Fields.UNIT_FIELD_MAXHEALTH, this.Health); + SetField(Fields.UNIT_FIELD_MAXPOWER1, this.Rage); + + SetField(Fields.UNIT_FIELD_LEVEL, this.Level); + SetField(Fields.UNIT_FIELD_FACTIONTEMPLATE, 0); + SetField(Fields.UNIT_FIELD_BYTES_0, BitConverter.ToUInt32(new byte[] { this.Race, this.Class, this.Gender, this.PowerType }, 0)); + SetField(Fields.UNIT_FIELD_FLAGS, 1); + + SetField(Fields.UNIT_FIELD_PETNUMBER, 0); + SetField(Fields.UNIT_FIELD_PET_NAME_TIMESTAMP, 0); + SetField(Fields.UNIT_FIELD_PETEXPERIENCE, 0); + SetField(Fields.UNIT_FIELD_PETNEXTLEVELEXP, 0); + SetField(Fields.UNIT_DYNAMIC_FLAGS, 0); + SetField(Fields.UNIT_CHANNEL_SPELL, 0); + SetField(Fields.UNIT_MOD_CAST_SPEED, 0); + + SetField(Fields.UNIT_NPC_FLAGS, 0); + SetField(Fields.UNIT_NPC_EMOTESTATE, 0); + + SetField(Fields.UNIT_FIELD_STAT0, this.Strength); + SetField(Fields.UNIT_FIELD_STAT1, this.Agility); + SetField(Fields.UNIT_FIELD_STAT2, this.Stamina); + SetField(Fields.UNIT_FIELD_STAT3, this.Intellect); + SetField(Fields.UNIT_FIELD_STAT4, this.Spirit); + + SetField(Fields.UNIT_FIELD_BASE_MANA, this.Mana); + SetField(Fields.UNIT_FIELD_BASE_HEALTH, this.Health); + + SetField(Fields.UNIT_FIELD_BYTES_2, 0); + SetField(Fields.UNIT_FIELD_ATTACK_POWER, 0); + SetField(Fields.UNIT_FIELD_ATTACK_POWER_MODS, 0); + SetField(Fields.UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0); + SetField(Fields.UNIT_FIELD_RANGED_ATTACK_POWER, 0); + + for(int i = 0; i < 4; i++) + SetField(Fields.UNIT_FIELD_POWER_COST_MODIFIER + 2 + i, 0); + SetField(Fields.UNIT_FIELD_POWER_COST_MULTIPLIER + 3, 0); + + SetField(Fields.PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE + 3, 0); + SetField((Fields)1353, 0); + + SetField(Fields.UNIT_FIELD_DISPLAYID, DisplayId); + SetField(Fields.UNIT_FIELD_MOUNTDISPLAYID, MountDisplayId); + SetField(Fields.UNIT_FIELD_BYTES_1, BitConverter.ToUInt32(new byte[] { (byte)StandState, 0, 0, 0 }, 0)); + SetField(Fields.PLAYER_BYTES, BitConverter.ToUInt32(new byte[] { Skin, Face, HairStyle, HairColor }, 0)); + SetField(Fields.PLAYER_BYTES_2, BitConverter.ToUInt32(new byte[] { 0, FacialHair, 0, ResetedState }, 0)); + SetField(Fields.PLAYER_BYTES_3, (uint)this.Gender); + SetField(Fields.PLAYER_XP, 47); + SetField(Fields.PLAYER_NEXT_LEVEL_XP, 200); + + writer.WriteBytes(maskArray); + foreach (var kvp in fieldData) + writer.WriteBytes(kvp.Value); //Data + + return writer; + } + + public IPacketWriter BuildMessage(string text) + { + PacketWriter message = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + return this.BuildMessage(message, text); + } + + public void Teleport(float x, float y, float z, float o, uint map, ref IWorldManager manager) + { + IsTeleporting = true; + + if (Location.Map == map) + { + PacketWriter movementStatus = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.MSG_MOVE_TELEPORT_ACK], "MSG_MOVE_TELEPORT_ACK"); + movementStatus.WriteUInt64(this.Guid); + movementStatus.WriteUInt64(0); //Flags + movementStatus.WriteFloat(x); + movementStatus.WriteFloat(y); + movementStatus.WriteFloat(z); + movementStatus.WriteFloat(o); + movementStatus.WriteFloat(0); + manager.Send(movementStatus); + } + else + { + //Loading screen + PacketWriter transferPending = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TRANSFER_PENDING], "SMSG_TRANSFER_PENDING"); + transferPending.WriteUInt32(map); + manager.Send(transferPending); + + //New world transfer + PacketWriter newWorld = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NEW_WORLD], "SMSG_NEW_WORLD"); + newWorld.WriteUInt32(map); + newWorld.WriteFloat(x); + newWorld.WriteFloat(y); + newWorld.WriteFloat(z); + newWorld.WriteFloat(o); + manager.Send(newWorld); + } + + System.Threading.Thread.Sleep(150); //Pause to factor unsent packets + + Location = new Location(x, y, z, o, map); + manager.Send(BuildUpdate()); + + IsTeleporting = false; + } + + public IPacketWriter BuildForceSpeed(float modifier, bool swim = false) + { + var opcode = swim ? global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE : global::Opcodes.SMSG_FORCE_SPEED_CHANGE; + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[opcode], opcode.ToString()); + writer.WriteUInt64(this.Guid); + return this.BuildForceSpeed(writer, modifier); + } + + public void Demorph() => DisplayId = this.GetDisplayId(); + + internal enum Fields + { + OBJECT_FIELD_GUID = 0, + OBJECT_FIELD_TYPE = 2, + OBJECT_FIELD_ENTRY = 3, + OBJECT_FIELD_SCALE_X = 4, + OBJECT_FIELD_PADDING = 5, + UNIT_FIELD_CHARM = 6, + UNIT_FIELD_SUMMON = 8, + UNIT_FIELD_CHARMEDBY = 10, + UNIT_FIELD_SUMMONEDBY = 12, + UNIT_FIELD_CREATEDBY = 14, + UNIT_FIELD_TARGET = 16, + UNIT_FIELD_PERSUADED = 18, + UNIT_FIELD_CHANNEL_OBJECT = 20, + UNIT_FIELD_HEALTH = 22, + UNIT_FIELD_POWER1 = 23, + UNIT_FIELD_POWER2 = 24, + UNIT_FIELD_POWER3 = 25, + UNIT_FIELD_POWER4 = 26, + UNIT_FIELD_POWER5 = 27, + UNIT_FIELD_MAXHEALTH = 28, + UNIT_FIELD_MAXPOWER1 = 29, + UNIT_FIELD_MAXPOWER2 = 30, + UNIT_FIELD_MAXPOWER3 = 31, + UNIT_FIELD_MAXPOWER4 = 32, + UNIT_FIELD_MAXPOWER5 = 33, + UNIT_FIELD_LEVEL = 34, + UNIT_FIELD_FACTIONTEMPLATE = 35, + UNIT_FIELD_BYTES_0 = 36, + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = 37, + UNIT_VIRTUAL_ITEM_INFO = 40, + UNIT_FIELD_FLAGS = 46, + UNIT_FIELD_AURA = 47, + UNIT_FIELD_AURAFLAGS = 95, + UNIT_FIELD_AURALEVELS = 101, + UNIT_FIELD_AURAAPPLICATIONS = 113, + UNIT_FIELD_AURASTATE = 125, + UNIT_FIELD_BASEATTACKTIME = 126, + UNIT_FIELD_RANGEDATTACKTIME = 128, + UNIT_FIELD_BOUNDINGRADIUS = 129, + UNIT_FIELD_COMBATREACH = 130, + UNIT_FIELD_DISPLAYID = 131, + UNIT_FIELD_NATIVEDISPLAYID = 132, + UNIT_FIELD_MOUNTDISPLAYID = 133, + UNIT_FIELD_MINDAMAGE = 134, + UNIT_FIELD_MAXDAMAGE = 135, + UNIT_FIELD_MINOFFHANDDAMAGE = 136, + UNIT_FIELD_MAXOFFHANDDAMAGE = 137, + UNIT_FIELD_BYTES_1 = 138, + UNIT_FIELD_PETNUMBER = 139, + UNIT_FIELD_PET_NAME_TIMESTAMP = 140, + UNIT_FIELD_PETEXPERIENCE = 141, + UNIT_FIELD_PETNEXTLEVELEXP = 142, + UNIT_DYNAMIC_FLAGS = 143, + UNIT_CHANNEL_SPELL = 144, + UNIT_MOD_CAST_SPEED = 145, + UNIT_CREATED_BY_SPELL = 146, + UNIT_NPC_FLAGS = 147, + UNIT_NPC_EMOTESTATE = 148, + UNIT_TRAINING_POINTS = 149, + UNIT_FIELD_STAT0 = 150, + UNIT_FIELD_STAT1 = 151, + UNIT_FIELD_STAT2 = 152, + UNIT_FIELD_STAT3 = 153, + UNIT_FIELD_STAT4 = 154, + UNIT_FIELD_RESISTANCES = 155, + UNIT_FIELD_BASE_MANA = 162, + UNIT_FIELD_BASE_HEALTH = 163, + UNIT_FIELD_BYTES_2 = 164, + UNIT_FIELD_ATTACK_POWER = 165, + UNIT_FIELD_ATTACK_POWER_MODS = 166, + UNIT_FIELD_ATTACK_POWER_MULTIPLIER = 167, + UNIT_FIELD_RANGED_ATTACK_POWER = 168, + UNIT_FIELD_RANGED_ATTACK_POWER_MODS = 169, + UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = 170, + UNIT_FIELD_MINRANGEDDAMAGE = 171, + UNIT_FIELD_MAXRANGEDDAMAGE = 172, + UNIT_FIELD_POWER_COST_MODIFIER = 173, + UNIT_FIELD_POWER_COST_MULTIPLIER = 180, + UNIT_FIELD_PADDING = 187, + PLAYER_DUEL_ARBITER = 188, + PLAYER_FLAGS = 190, + PLAYER_GUILDID = 191, + PLAYER_GUILDRANK = 192, + PLAYER_BYTES = 193, + PLAYER_BYTES_2 = 194, + PLAYER_BYTES_3 = 195, + PLAYER_DUEL_TEAM = 196, + PLAYER_GUILD_TIMESTAMP = 197, + PLAYER_QUEST_LOG_1_1 = 198, + PLAYER_QUEST_LOG_1_2 = 199, + PLAYER_QUEST_LOG_2_1 = 201, + PLAYER_QUEST_LOG_2_2 = 202, + PLAYER_QUEST_LOG_3_1 = 204, + PLAYER_QUEST_LOG_3_2 = 205, + PLAYER_QUEST_LOG_4_1 = 207, + PLAYER_QUEST_LOG_4_2 = 208, + PLAYER_QUEST_LOG_5_1 = 210, + PLAYER_QUEST_LOG_5_2 = 211, + PLAYER_QUEST_LOG_6_1 = 213, + PLAYER_QUEST_LOG_6_2 = 214, + PLAYER_QUEST_LOG_7_1 = 216, + PLAYER_QUEST_LOG_7_2 = 217, + PLAYER_QUEST_LOG_8_1 = 219, + PLAYER_QUEST_LOG_8_2 = 220, + PLAYER_QUEST_LOG_9_1 = 222, + PLAYER_QUEST_LOG_9_2 = 223, + PLAYER_QUEST_LOG_10_1 = 225, + PLAYER_QUEST_LOG_10_2 = 226, + PLAYER_QUEST_LOG_11_1 = 228, + PLAYER_QUEST_LOG_11_2 = 229, + PLAYER_QUEST_LOG_12_1 = 231, + PLAYER_QUEST_LOG_12_2 = 232, + PLAYER_QUEST_LOG_13_1 = 234, + PLAYER_QUEST_LOG_13_2 = 235, + PLAYER_QUEST_LOG_14_1 = 237, + PLAYER_QUEST_LOG_14_2 = 238, + PLAYER_QUEST_LOG_15_1 = 240, + PLAYER_QUEST_LOG_15_2 = 241, + PLAYER_QUEST_LOG_16_1 = 243, + PLAYER_QUEST_LOG_16_2 = 244, + PLAYER_QUEST_LOG_17_1 = 246, + PLAYER_QUEST_LOG_17_2 = 247, + PLAYER_QUEST_LOG_18_1 = 249, + PLAYER_QUEST_LOG_18_2 = 250, + PLAYER_QUEST_LOG_19_1 = 252, + PLAYER_QUEST_LOG_19_2 = 253, + PLAYER_QUEST_LOG_20_1 = 255, + PLAYER_QUEST_LOG_20_2 = 256, + PLAYER_VISIBLE_ITEM_1_CREATOR = 258, + PLAYER_VISIBLE_ITEM_1_0 = 260, + PLAYER_VISIBLE_ITEM_1_PROPERTIES = 272, + PLAYER_VISIBLE_ITEM_1_PAD = 273, + PLAYER_VISIBLE_ITEM_2_CREATOR = 274, + PLAYER_VISIBLE_ITEM_2_0 = 276, + PLAYER_VISIBLE_ITEM_2_PROPERTIES = 288, + PLAYER_VISIBLE_ITEM_2_PAD = 289, + PLAYER_VISIBLE_ITEM_3_CREATOR = 290, + PLAYER_VISIBLE_ITEM_3_0 = 292, + PLAYER_VISIBLE_ITEM_3_PROPERTIES = 304, + PLAYER_VISIBLE_ITEM_3_PAD = 305, + PLAYER_VISIBLE_ITEM_4_CREATOR = 306, + PLAYER_VISIBLE_ITEM_4_0 = 308, + PLAYER_VISIBLE_ITEM_4_PROPERTIES = 320, + PLAYER_VISIBLE_ITEM_4_PAD = 321, + PLAYER_VISIBLE_ITEM_5_CREATOR = 322, + PLAYER_VISIBLE_ITEM_5_0 = 324, + PLAYER_VISIBLE_ITEM_5_PROPERTIES = 336, + PLAYER_VISIBLE_ITEM_5_PAD = 337, + PLAYER_VISIBLE_ITEM_6_CREATOR = 338, + PLAYER_VISIBLE_ITEM_6_0 = 340, + PLAYER_VISIBLE_ITEM_6_PROPERTIES = 352, + PLAYER_VISIBLE_ITEM_6_PAD = 353, + PLAYER_VISIBLE_ITEM_7_CREATOR = 354, + PLAYER_VISIBLE_ITEM_7_0 = 356, + PLAYER_VISIBLE_ITEM_7_PROPERTIES = 368, + PLAYER_VISIBLE_ITEM_7_PAD = 369, + PLAYER_VISIBLE_ITEM_8_CREATOR = 370, + PLAYER_VISIBLE_ITEM_8_0 = 372, + PLAYER_VISIBLE_ITEM_8_PROPERTIES = 384, + PLAYER_VISIBLE_ITEM_8_PAD = 385, + PLAYER_VISIBLE_ITEM_9_CREATOR = 386, + PLAYER_VISIBLE_ITEM_9_0 = 388, + PLAYER_VISIBLE_ITEM_9_PROPERTIES = 400, + PLAYER_VISIBLE_ITEM_9_PAD = 401, + PLAYER_VISIBLE_ITEM_10_CREATOR = 402, + PLAYER_VISIBLE_ITEM_10_0 = 404, + PLAYER_VISIBLE_ITEM_10_PROPERTIES = 416, + PLAYER_VISIBLE_ITEM_10_PAD = 417, + PLAYER_VISIBLE_ITEM_11_CREATOR = 418, + PLAYER_VISIBLE_ITEM_11_0 = 420, + PLAYER_VISIBLE_ITEM_11_PROPERTIES = 432, + PLAYER_VISIBLE_ITEM_11_PAD = 433, + PLAYER_VISIBLE_ITEM_12_CREATOR = 434, + PLAYER_VISIBLE_ITEM_12_0 = 436, + PLAYER_VISIBLE_ITEM_12_PROPERTIES = 448, + PLAYER_VISIBLE_ITEM_12_PAD = 449, + PLAYER_VISIBLE_ITEM_13_CREATOR = 450, + PLAYER_VISIBLE_ITEM_13_0 = 452, + PLAYER_VISIBLE_ITEM_13_PROPERTIES = 464, + PLAYER_VISIBLE_ITEM_13_PAD = 465, + PLAYER_VISIBLE_ITEM_14_CREATOR = 466, + PLAYER_VISIBLE_ITEM_14_0 = 468, + PLAYER_VISIBLE_ITEM_14_PROPERTIES = 480, + PLAYER_VISIBLE_ITEM_14_PAD = 481, + PLAYER_VISIBLE_ITEM_15_CREATOR = 482, + PLAYER_VISIBLE_ITEM_15_0 = 484, + PLAYER_VISIBLE_ITEM_15_PROPERTIES = 496, + PLAYER_VISIBLE_ITEM_15_PAD = 497, + PLAYER_VISIBLE_ITEM_16_CREATOR = 498, + PLAYER_VISIBLE_ITEM_16_0 = 500, + PLAYER_VISIBLE_ITEM_16_PROPERTIES = 512, + PLAYER_VISIBLE_ITEM_16_PAD = 513, + PLAYER_VISIBLE_ITEM_17_CREATOR = 514, + PLAYER_VISIBLE_ITEM_17_0 = 516, + PLAYER_VISIBLE_ITEM_17_PROPERTIES = 528, + PLAYER_VISIBLE_ITEM_17_PAD = 529, + PLAYER_VISIBLE_ITEM_18_CREATOR = 530, + PLAYER_VISIBLE_ITEM_18_0 = 532, + PLAYER_VISIBLE_ITEM_18_PROPERTIES = 544, + PLAYER_VISIBLE_ITEM_18_PAD = 545, + PLAYER_VISIBLE_ITEM_19_CREATOR = 546, + PLAYER_VISIBLE_ITEM_19_0 = 548, + PLAYER_VISIBLE_ITEM_19_PROPERTIES = 560, + PLAYER_VISIBLE_ITEM_19_PAD = 561, + PLAYER_FIELD_INV_SLOT_HEAD = 562, + PLAYER_FIELD_PACK_SLOT_1 = 608, + PLAYER_FIELD_BANK_SLOT_1 = 640, + PLAYER_FIELD_BANKBAG_SLOT_1 = 688, + PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = 700, + PLAYER_FIELD_KEYRING_SLOT_1 = 724, + PLAYER_FARSIGHT = 788, + PLAYER_FIELD_COMBO_TARGET = 790, + PLAYER_XP = 792, + PLAYER_NEXT_LEVEL_XP = 793, + PLAYER_SKILL_INFO_1_1 = 794, + PLAYER_CHARACTER_POINTS1 = 1178, + PLAYER_CHARACTER_POINTS2 = 1179, + PLAYER_TRACK_CREATURES = 1180, + PLAYER_TRACK_RESOURCES = 1181, + PLAYER_BLOCK_PERCENTAGE = 1182, + PLAYER_DODGE_PERCENTAGE = 1183, + PLAYER_PARRY_PERCENTAGE = 1184, + PLAYER_CRIT_PERCENTAGE = 1185, + PLAYER_RANGED_CRIT_PERCENTAGE = 1186, + PLAYER_EXPLORED_ZONES_1 = 1187, + PLAYER_REST_STATE_EXPERIENCE = 1251, + PLAYER_FIELD_COINAGE = 1252, + PLAYER_FIELD_POSSTAT0 = 1253, + PLAYER_FIELD_POSSTAT1 = 1254, + PLAYER_FIELD_POSSTAT2 = 1255, + PLAYER_FIELD_POSSTAT3 = 1256, + PLAYER_FIELD_POSSTAT4 = 1257, + PLAYER_FIELD_NEGSTAT0 = 1258, + PLAYER_FIELD_NEGSTAT1 = 1259, + PLAYER_FIELD_NEGSTAT2 = 1260, + PLAYER_FIELD_NEGSTAT3 = 1261, + PLAYER_FIELD_NEGSTAT4 = 1262, + PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE = 1263, + PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE = 1270, + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = 1277, + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = 1284, + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = 1291, + PLAYER_FIELD_BYTES = 1298, + PLAYER_AMMO_ID = 1299, + PLAYER_SELF_RES_SPELL = 1300, + PLAYER_FIELD_PVP_MEDALS = 1301, + PLAYER_FIELD_BUYBACK_PRICE_1 = 1302, + PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = 1314, + PLAYER_FIELD_SESSION_KILLS = 1326, + PLAYER_FIELD_YESTERDAY_KILLS = 1327, + PLAYER_FIELD_LAST_WEEK_KILLS = 1328, + PLAYER_FIELD_THIS_WEEK_KILLS = 1329, + PLAYER_FIELD_THIS_WEEK_CONTRIBUTION = 1330, + PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = 1331, + PLAYER_FIELD_LIFETIME_DISHONORBALE_KILLS = 1332, + PLAYER_FIELD_YESTERDAY_CONTRIBUTION = 1333, + PLAYER_FIELD_LAST_WEEK_CONTRIBUTION = 1334, + PLAYER_FIELD_LAST_WEEK_RANK = 1335, + PLAYER_FIELD_BYTES2 = 1336, + PLAYER_FIELD_WATCHED_FACTION_INDEX = 1337, + PLAYER_FIELD_COMBAT_RATING_1 = 1338, + PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = 1358, + PLAYER_FIELD_HONOR_CURRENCY = 1376, + PLAYER_FIELD_ARENA_CURRENCY = 1377, + MAX = 1378, + } + } +} diff --git a/Plugins/TBC_Alpha_5610/Handlers/AuthHandler.cs b/Plugins/TBC_Alpha_5610/Handlers/AuthHandler.cs new file mode 100644 index 0000000..89dd75f --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Handlers/AuthHandler.cs @@ -0,0 +1,129 @@ +using Common.Constants; +using Common.Cryptography; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610.Handlers +{ + public class AuthHandler : IAuthHandler + { + public IPacketWriter HandleAuthChallenge() + { + ClientAuth.Clear(); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_CHALLENGE], "SMSG_AUTH_CHALLENGE"); + writer.WriteUInt32(0); + return writer; + } + + public IPacketWriter HandleRedirect() + { + PacketWriter proxyWriter = new PacketWriter(); + proxyWriter.WriteBytes(Encoding.ASCII.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort)); + proxyWriter.WriteUInt8(0); + return proxyWriter; + } + + public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager) + { + ClientAuth.Encode = true; + + packet.ReadUInt64(); + string name = packet.ReadString().ToUpper(); + + Account account = new Account(name); + account.Load(); + manager.Account = account; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE"); + writer.WriteUInt8(0xC); //AUTH_OK + writer.WriteUInt32(0); + writer.WriteUInt8(0); + writer.WriteUInt32(0); + writer.WriteUInt8(0); + manager.Send(writer); + } + + public void HandleLogoutRequest(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + if (character != null) + { + PacketWriter logoutComplete = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGOUT_COMPLETE], "SMSG_LOGOUT_COMPLETE"); + manager.Send(logoutComplete); + character.IsOnline = false; + manager.Account.Save(); + } + } + + public void HandleRealmList(Socket socket) + { + while (socket.Connected) + { + System.Threading.Thread.Sleep(1); + if (socket.Available > 0) + { + byte[] buffer = new byte[socket.Available]; + socket.Receive(buffer, buffer.Length, SocketFlags.None); + + PacketReader packet = new PacketReader(buffer, false); + PacketWriter writer = new PacketWriter(); + + var op = (RealmlistOpcodes)packet.ReadByte(); + switch (op) + { + case RealmlistOpcodes.LOGON_CHALLENGE: + writer.WriteBytes(ClientAuth.LogonChallenge(packet)); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.RECONNECT_CHALLENGE: + writer.WriteBytes(ClientAuth.Reconnect_Challenge); + break; + case RealmlistOpcodes.LOGON_PROOF: + writer.WriteBytes(ClientAuth.LogonProof(packet)); + break; + case RealmlistOpcodes.RECONNECT_PROOF: + writer.WriteUInt8((byte)RealmlistOpcodes.RECONNECT_PROOF); + writer.WriteUInt8(0); + break; + case RealmlistOpcodes.REALMLIST_REQUEST: + //Send Realm List + byte[] realmName = Encoding.UTF8.GetBytes(Sandbox.Instance.RealmName); + byte[] redirect = Encoding.UTF8.GetBytes("127.0.0.1:" + Sandbox.Instance.WorldPort); + + writer.WriteUInt8((byte)RealmlistOpcodes.REALMLIST_REQUEST); + writer.WriteUInt16((ushort)(21 + realmName.Length + redirect.Length)); //Packet length + + writer.WriteUInt32(0); + writer.WriteUInt8(1); //Realm count + + writer.WriteUInt32(1); //Icon + writer.WriteUInt8(0); //Colour + writer.WriteBytes(realmName); + writer.WriteUInt8(0); + writer.WriteBytes(redirect); + writer.WriteUInt8(0); + + writer.WriteFloat(0); //Population + writer.WriteUInt8(0); //Players + writer.WriteUInt8(1); //Timezone + writer.WriteUInt8(0); + writer.WriteUInt16(0x2); + break; + } + + if (writer.BaseStream.Length > 0) + socket.SendData(writer, op.ToString()); + } + } + + socket.Close(); + } + } +} diff --git a/Plugins/TBC_Alpha_5610/Handlers/CharHandler.cs b/Plugins/TBC_Alpha_5610/Handlers/CharHandler.cs new file mode 100644 index 0000000..2fb5904 --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Handlers/CharHandler.cs @@ -0,0 +1,225 @@ +using Common.Commands; +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610.Handlers +{ + public class CharHandler : ICharHandler + { + public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager) + { + string name = packet.ReadString(); + + Character cha = new Character() + { + Name = name.ToUpperFirst(), + Race = packet.ReadByte(), + Class = packet.ReadByte(), + Gender = packet.ReadByte(), + Skin = packet.ReadByte(), + Face = packet.ReadByte(), + HairStyle = packet.ReadByte(), + HairColor = packet.ReadByte(), + FacialHair = packet.ReadByte() + }; + + var result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE"); + + if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase))) + { + writer.WriteUInt8(0x2B); //Duplicate name + manager.Send(writer); + return; + } + + cha.Guid = (ulong)(manager.Account.Characters.Count + 1); + cha.Location = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0); + cha.ResetedState = (byte)new Random().Next(1, 2); + cha.SetPowerType(); + + manager.Account.Characters.Add(cha); + manager.Account.Save(); + + //Success + writer.WriteUInt8(0x2E); + manager.Send(writer); + } + + public void HandleCharDelete(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + var character = manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_DELETE], "SMSG_CHAR_DELETE"); + writer.WriteUInt8(0x34); + manager.Send(writer); + + if (character != null) + { + manager.Account.Characters.Remove(character); + manager.Account.Save(); + } + + } + + public void HandleCharEnum(ref IPacketReader packet, ref IWorldManager manager) + { + var account = manager.Account; + var result = account.Characters.Where(x => x.Build == Sandbox.Instance.Build); + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_ENUM], "SMSG_CHAR_ENUM"); + writer.WriteUInt8((byte)result.Count()); + + foreach (Character c in result) + { + writer.WriteUInt64(c.Guid); + writer.WriteString(c.Name); + + writer.WriteUInt8(c.Race); + writer.WriteUInt8(c.Class); + writer.WriteUInt8(c.Gender); + writer.WriteUInt8(c.Skin); + writer.WriteUInt8(c.Face); + writer.WriteUInt8(c.HairStyle); + writer.WriteUInt8(c.HairColor); + writer.WriteUInt8(c.FacialHair); + writer.WriteUInt8((byte)c.Level); + + writer.WriteUInt32(c.Zone); + writer.WriteUInt32(c.Location.Map); + + writer.WriteFloat(c.Location.X); + writer.WriteFloat(c.Location.Y); + writer.WriteFloat(c.Location.Z); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt8(c.ResetedState); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + //Items + for (int j = 0; j < 0x14; j++) + { + writer.WriteUInt32(0); //DisplayId + writer.WriteUInt8(0); //InventoryType + } + } + + manager.Send(writer); + } + + public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager) + { + var character = manager.Account.ActiveCharacter; + + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT"); + writer.WriteUInt8((byte)packet.ReadInt32()); //System Message + packet.ReadUInt32(); + writer.WriteUInt32(0); //Language: General + writer.WriteUInt64(character.Guid); + + string message = packet.ReadString(); + writer.WriteString(message); + writer.WriteUInt8(0); + + if (!CommandManager.InvokeHandler(message, manager)) + manager.Send(writer); + } + + public void HandleMovementStatus(ref IPacketReader packet, ref IWorldManager manager) + { + if (manager.Account.ActiveCharacter.IsTeleporting) + return; + + uint opcode = packet.Opcode; + long pos = packet.Position; + + var character = manager.Account.ActiveCharacter; + ulong Flags = packet.ReadUInt64(); + character.Location.Update(packet, true); + + packet.Position = pos; + PacketWriter writer = new PacketWriter(opcode, Sandbox.Instance.Opcodes[opcode].ToString()); + writer.WriteBytes(packet.ReadToEnd()); + manager.Send(writer); + } + + public void HandleNameCache(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + + PacketWriter nameCache = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_NAME_QUERY_RESPONSE], "SMSG_NAME_QUERY_RESPONSE"); + nameCache.WriteUInt64(guid); + nameCache.WriteString(character.Name); + nameCache.WriteUInt32(character.Race); + nameCache.WriteUInt32(character.Gender); + nameCache.WriteUInt32(character.Class); + manager.Send(nameCache); + } + + public void HandleStandState(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.StandState = (StandState)packet.ReadUInt32(); + manager.Send(manager.Account.ActiveCharacter.BuildUpdate()); + } + + public void HandleTextEmote(ref IPacketReader packet, ref IWorldManager manager) + { + uint emote = packet.ReadUInt32(); + ulong guid = packet.ReadUInt64(); + uint emoteId = Emotes.Get((TextEmotes)emote); + Character character = (Character)manager.Account.ActiveCharacter; + + PacketWriter pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TEXT_EMOTE], "SMSG_TEXT_EMOTE"); + pw.Write(character.Guid); + pw.Write(emote); + + if (guid == character.Guid) + pw.WriteString(character.Name); + else + pw.WriteUInt8(0); + + manager.Send(pw); + + switch ((TextEmotes)emote) + { + case TextEmotes.EMOTE_SIT: + character.StandState = StandState.SITTING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_STAND: + character.StandState = StandState.STANDING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_SLEEP: + character.StandState = StandState.SLEEPING; + manager.Send(character.BuildUpdate()); + return; + case TextEmotes.EMOTE_KNEEL: + character.StandState = StandState.KNEEL; + manager.Send(character.BuildUpdate()); + return; + } + + if (emoteId > 0) + { + pw = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_EMOTE], "SMSG_EMOTE"); + pw.WriteUInt32(emoteId); + pw.WriteUInt64(character.Guid); + manager.Send(pw); + } + } + } +} diff --git a/Plugins/TBC_Alpha_5610/Handlers/WorldHandler.cs b/Plugins/TBC_Alpha_5610/Handlers/WorldHandler.cs new file mode 100644 index 0000000..644d1cd --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Handlers/WorldHandler.cs @@ -0,0 +1,90 @@ +using Common.Constants; +using Common.Extensions; +using Common.Interfaces; +using Common.Interfaces.Handlers; +using Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610.Handlers +{ + public class WorldHandler : IWorldHandler + { + public void HandlePing(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_PONG], "SMSG_PONG"); + writer.WriteUInt32(packet.ReadUInt32()); + manager.Send(writer); + } + + public void HandleQueryTime(ref IPacketReader packet, ref IWorldManager manager) + { + PacketWriter queryTime = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_SETTIMESPEED], "SMSG_LOGIN_SETTIMESPEED"); + queryTime.WriteInt32(this.GetTime()); + queryTime.WriteFloat(0.01666667f); + manager.Send(queryTime); + } + + public void HandlePlayerLogin(ref IPacketReader packet, ref IWorldManager manager) + { + ulong guid = packet.ReadUInt64(); + Character character = (Character)manager.Account.Characters.Find(x => x.Guid == guid && x.Build == Sandbox.Instance.Build); + character.IsOnline = true; + character.DisplayId = character.GetDisplayId(); + + //Verify World : REQUIRED + PacketWriter verify = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_LOGIN_VERIFY_WORLD], "SMSG_LOGIN_VERIFY_WORLD"); + verify.WriteUInt32(character.Location.Map); + verify.WriteFloat(character.Location.X); + verify.WriteFloat(character.Location.Y); + verify.WriteFloat(character.Location.Z); + verify.WriteFloat(character.Location.O); + manager.Send(verify); + + //Account Data Hash : REQUIRED + PacketWriter accountdata = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ACCOUNT_DATA_MD5], "SMSG_ACCOUNT_DATA_MD5"); + for (int i = 0; i < 31; i++) + accountdata.WriteInt32(0); + manager.Send(accountdata); + + //Tutorial Flags : REQUIRED + PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS"); + for (int i = 0; i < 8; i++) + tutorial.WriteInt32(-1); + manager.Send(tutorial); + + HandleQueryTime(ref packet, ref manager); + + manager.Send(character.BuildUpdate()); + } + + public void HandleWorldTeleport(ref IPacketReader packet, ref IWorldManager manager) + { + throw new NotImplementedException(); + } + + public void HandleWorldPortAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleWorldTeleportAck(ref IPacketReader packet, ref IWorldManager manager) { } + + public void HandleAreaTrigger(ref IPacketReader packet, ref IWorldManager manager) + { + uint id = packet.ReadUInt32(); + if (AreaTriggers.Triggers.ContainsKey(id)) + { + var loc = AreaTriggers.Triggers[id]; + manager.Account.ActiveCharacter.Teleport(loc.X, loc.Y, loc.Z, loc.O, loc.Map, ref manager); + } + else + Log.Message(LogType.ERROR, "AreaTrigger for {0} missing.", id); + } + + public void HandleZoneUpdate(ref IPacketReader packet, ref IWorldManager manager) + { + manager.Account.ActiveCharacter.Zone = packet.ReadUInt32(); + } + } +} diff --git a/Plugins/TBC_Alpha_5610/Opcodes.cs b/Plugins/TBC_Alpha_5610/Opcodes.cs new file mode 100644 index 0000000..6557739 --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Opcodes.cs @@ -0,0 +1,84 @@ +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610 +{ + public class Opcodes : IOpcodes + { + readonly IDictionary opcodes = new Dictionary() + { + { global::Opcodes.SMSG_AUTH_CHALLENGE, 0x1EC }, + { global::Opcodes.SMSG_AUTH_RESPONSE, 0x1EE }, + { global::Opcodes.CMSG_AUTH_SESSION, 0x1ED }, + { global::Opcodes.CMSG_CHAR_CREATE, 0x36 }, + { global::Opcodes.SMSG_CHAR_CREATE, 0x3A }, + { global::Opcodes.CMSG_CHAR_DELETE, 0x38 }, + { global::Opcodes.SMSG_CHAR_DELETE, 0x3C }, + { global::Opcodes.CMSG_CHAR_ENUM, 0x37 }, + { global::Opcodes.SMSG_CHAR_ENUM, 0x3B }, + { global::Opcodes.CMSG_PING, 0x1DC }, + { global::Opcodes.SMSG_PONG, 0x1DD }, + { global::Opcodes.CMSG_PLAYER_LOGIN, 0x3D }, + { global::Opcodes.SMSG_UPDATE_OBJECT, 0xA9 }, + { global::Opcodes.CMSG_NAME_QUERY, 0x50 }, + { global::Opcodes.SMSG_NAME_QUERY_RESPONSE, 0x51 }, + { global::Opcodes.CMSG_LOGOUT_REQUEST, 0x4B }, + { global::Opcodes.SMSG_LOGOUT_COMPLETE, 0x4D }, + { global::Opcodes.CMSG_WORLD_TELEPORT, 0x8 }, + { global::Opcodes.SMSG_NEW_WORLD, 0x3E }, + { global::Opcodes.SMSG_TRANSFER_PENDING, 0x3F }, + { global::Opcodes.MSG_MOVE_START_FORWARD, 0xB5 }, + { global::Opcodes.MSG_MOVE_START_BACKWARD, 0xB6 }, + { global::Opcodes.MSG_MOVE_STOP, 0xB7 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_LEFT, 0xB8 }, + { global::Opcodes.MSG_MOVE_START_STRAFE_RIGHT, 0xB9 }, + { global::Opcodes.MSG_MOVE_STOP_STRAFE, 0xBA }, + { global::Opcodes.MSG_MOVE_JUMP, 0xBB }, + { global::Opcodes.MSG_MOVE_START_TURN_LEFT, 0xBC }, + { global::Opcodes.MSG_MOVE_START_TURN_RIGHT, 0xBD }, + { global::Opcodes.MSG_MOVE_STOP_TURN, 0xBE }, + { global::Opcodes.MSG_MOVE_START_PITCH_UP, 0xBF }, + { global::Opcodes.MSG_MOVE_START_PITCH_DOWN, 0xC0 }, + { global::Opcodes.MSG_MOVE_STOP_PITCH, 0xC1 }, + { global::Opcodes.MSG_MOVE_SET_RUN_MODE, 0xC2 }, + { global::Opcodes.MSG_MOVE_SET_WALK_MODE, 0xC3 }, + { global::Opcodes.MSG_MOVE_START_SWIM, 0xCA }, + { global::Opcodes.MSG_MOVE_STOP_SWIM, 0xCB }, + { global::Opcodes.MSG_MOVE_SET_FACING, 0xDA }, + { global::Opcodes.MSG_MOVE_SET_PITCH, 0xDB }, + { global::Opcodes.MSG_MOVE_ROOT, 0xEC }, + { global::Opcodes.MSG_MOVE_UNROOT, 0xED }, + { global::Opcodes.MSG_MOVE_HEARTBEAT, 0xEE }, + { global::Opcodes.MSG_MOVE_WORLDPORT_ACK, 0xDC }, + { global::Opcodes.SMSG_MESSAGECHAT, 0x96 }, + { global::Opcodes.CMSG_MESSAGECHAT, 0x95 }, + { global::Opcodes.SMSG_FORCE_SPEED_CHANGE, 0xE2 }, + { global::Opcodes.SMSG_FORCE_SWIM_SPEED_CHANGE, 0xE6 }, + { global::Opcodes.SMSG_TUTORIAL_FLAGS, 0xFD }, + { global::Opcodes.CMSG_QUERY_TIME, 0x1CE }, + { global::Opcodes.SMSG_LOGIN_SETTIMESPEED, 0x42 }, + { global::Opcodes.SMSG_ACCOUNT_DATA_MD5, 0x209 }, + { global::Opcodes.CMSG_STANDSTATECHANGE, 0x101 }, + { global::Opcodes.MSG_MOVE_TELEPORT_ACK, 0xC7 }, + { global::Opcodes.CMSG_AREATRIGGER, 0xB4 }, + { global::Opcodes.CMSG_ZONEUPDATE, 0x1F4 }, + { global::Opcodes.CMSG_UPDATE_ACCOUNT_DATA, 0x20B }, + { global::Opcodes.SMSG_UPDATE_ACCOUNT_DATA, 0x20C }, + { global::Opcodes.SMSG_LOGIN_VERIFY_WORLD, 0x236 }, + { global::Opcodes.SMSG_EMOTE, 0x103 }, + { global::Opcodes.CMSG_TEXT_EMOTE, 0x104 }, + { global::Opcodes.SMSG_TEXT_EMOTE, 0x105 }, + }; + + public uint this[global::Opcodes opcode] => opcodes[opcode]; + + public global::Opcodes this[uint opcode] => opcodes.First(x => x.Value == opcode).Key; + + public bool OpcodeExists(uint opcode) => opcodes.Any(x => x.Value == opcode); + + } +} diff --git a/Plugins/TBC_Alpha_5610/PacketReader.cs b/Plugins/TBC_Alpha_5610/PacketReader.cs new file mode 100644 index 0000000..a1fe1bb --- /dev/null +++ b/Plugins/TBC_Alpha_5610/PacketReader.cs @@ -0,0 +1,141 @@ +using Common.Cryptography; +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610 +{ + public class PacketReader : BinaryReader, IPacketReader + { + public uint Opcode { get; set; } + public uint Size { get; set; } + public long Position { get => BaseStream.Position; set => BaseStream.Position = value; } + + private const int SHA_DIGEST_LENGTH = 40; + + public PacketReader(byte[] data, bool parse = true) : base(new MemoryStream(data)) + { + if(parse) + { + Decode(ref data); + ushort size = BitConverter.ToUInt16(data, 0); + Size = (ushort)((size >> 8) + ((size & 0xFF) << 8) + 2); + Opcode = BitConverter.ToUInt32(data, 2); + + Position = 6; + } + } + + private void Decode(ref byte[] data) + { + if (!ClientAuth.Encode || data.Length < 6) + return; + + for (int i = 0; i < 6; i++) + { + ClientAuth.Key[1] %= SHA_DIGEST_LENGTH; + byte x = (byte)((data[i] - ClientAuth.Key[0]) ^ ClientAuth.SS_Hash[ClientAuth.Key[1]]); + ++ClientAuth.Key[1]; + ClientAuth.Key[0] = data[i]; + data[i] = x; + } + } + + + public sbyte ReadInt8() + { + return base.ReadSByte(); + } + + public new short ReadInt16() + { + return base.ReadInt16(); + } + + public new int ReadInt32() + { + return base.ReadInt32(); + } + + public new long ReadInt64() + { + return base.ReadInt64(); + } + + public byte ReadUInt8() + { + return base.ReadByte(); + } + + public new ushort ReadUInt16() + { + return base.ReadUInt16(); + } + + public new uint ReadUInt32() + { + return base.ReadUInt32(); + } + + public new ulong ReadUInt64() + { + return base.ReadUInt64(); + } + + public float ReadFloat() + { + return base.ReadSingle(); + } + + public new double ReadDouble() + { + return base.ReadDouble(); + } + + public string ReadString(byte terminator = 0) + { + StringBuilder tmpString = new StringBuilder(); + char tmpChar = base.ReadChar(); + char tmpEndChar = Convert.ToChar(terminator); + + while (tmpChar != tmpEndChar) + { + tmpString.Append(tmpChar); + tmpChar = base.ReadChar(); + } + + return tmpString.ToString(); + } + + public new string ReadString() + { + return ReadString(0); + } + + public new byte[] ReadBytes(int count) + { + return base.ReadBytes(count); + } + + public byte[] ReadToEnd() + { + return base.ReadBytes((int)(BaseStream.Length - BaseStream.Position)); + } + + public string ReadStringFromBytes(int count) + { + byte[] stringArray = base.ReadBytes(count); + Array.Reverse(stringArray); + return Encoding.ASCII.GetString(stringArray); + } + + public void SkipBytes(int count) + { + base.BaseStream.Position += count; + } + } +} diff --git a/Plugins/TBC_Alpha_5610/PacketWriter.cs b/Plugins/TBC_Alpha_5610/PacketWriter.cs new file mode 100644 index 0000000..ee28539 --- /dev/null +++ b/Plugins/TBC_Alpha_5610/PacketWriter.cs @@ -0,0 +1,137 @@ +using Common.Cryptography; +using Common.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TBC_Alpha_5610 +{ + public class PacketWriter : BinaryWriter, IPacketWriter + { + public string Name { get; set; } + public uint Opcode { get; set; } + public uint Size { get; set; } + public bool PreAuth { get; set; } = false; + + private const int SHA_DIGEST_LENGTH = 40; + + + public PacketWriter() : base(new MemoryStream()) + { + PreAuth = true; + } + + public PacketWriter(uint opcode, string name) : base(new MemoryStream()) + { + Name = name; + Opcode = opcode; + WritePacketHeader(opcode); + } + + + public void WritePacketHeader(uint opcode) + { + WriteUInt16(0); + WriteUInt16((ushort)opcode); + } + + public byte[] ReadDataToSend() + { + byte[] data = new byte[BaseStream.Length]; + Seek(0, SeekOrigin.Begin); + + BaseStream.Read(data, 0, (int)BaseStream.Length); + + Size = (ushort)(data.Length - 2); + + if (!PreAuth) + { + data[0] = (byte)(Size >> 8); + data[1] = (byte)(Size & 255); + Encode(ref data); + } + + return data; + } + + private void Encode(ref byte[] data) + { + if (!ClientAuth.Encode || data.Length < 4) + return; + + for (int i = 0; i < 4; i++) + { + ClientAuth.Key[3] %= SHA_DIGEST_LENGTH; + byte x = (byte)((data[i] ^ ClientAuth.SS_Hash[ClientAuth.Key[3]]) + ClientAuth.Key[2]); + ++ClientAuth.Key[3]; + data[i] = ClientAuth.Key[2] = x; + } + } + + + public void WriteInt8(sbyte data) + { + base.Write(data); + } + + public void WriteInt16(short data) + { + base.Write(data); + } + + public void WriteInt32(int data) + { + base.Write(data); + } + + public void WriteInt64(long data) + { + base.Write(data); + } + + public void WriteUInt8(byte data) + { + base.Write(data); + } + + public void WriteUInt16(ushort data) + { + base.Write(data); + } + + public void WriteUInt32(uint data) + { + base.Write(data); + } + + public void WriteUInt64(ulong data) + { + base.Write(data); + } + + public void WriteFloat(float data) + { + base.Write(data); + } + + public void WriteDouble(double data) + { + base.Write(data); + } + + public void WriteString(string data) + { + byte[] sBytes = Encoding.ASCII.GetBytes(data); + this.WriteBytes(sBytes); + base.Write((byte)0); //String null terminated + } + + public void WriteBytes(byte[] data) + { + base.Write(data); + } + } +} diff --git a/Plugins/TBC_Alpha_5610/Properties/AssemblyInfo.cs b/Plugins/TBC_Alpha_5610/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d8be94c --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TBC_Alpha_5610")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TBC_Alpha_5610")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("19dd917c-87c6-407e-bfaa-9ecd37155040")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/TBC_Alpha_5610/Sandbox.cs b/Plugins/TBC_Alpha_5610/Sandbox.cs new file mode 100644 index 0000000..808a13a --- /dev/null +++ b/Plugins/TBC_Alpha_5610/Sandbox.cs @@ -0,0 +1,31 @@ +using Common.Interfaces; +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TBC_Alpha_5610.Handlers; + +namespace TBC_Alpha_5610 +{ + public class Sandbox : ISandbox + { + public static Sandbox Instance => _instance; + static readonly Sandbox _instance = new Sandbox(); + + public string RealmName { get; set; } = "TBC Alpha (2.0.0) Sandbox"; + public int Build { get; set; } = 5610; + public int RealmPort { get; set; } = 3724; + public int RedirectPort { get; set; } = 9002; + public int WorldPort { get; set; } = 8129; + + public IOpcodes Opcodes { get; set; } = new Opcodes(); + + public IAuthHandler AuthHandler { get; set; } = new AuthHandler(); + public ICharHandler CharHandler { get; set; } = new CharHandler(); + public IWorldHandler WorldHandler { get; set; } = new WorldHandler(); + public IPacketReader ReadPacket(byte[] data, bool parse = true) => new PacketReader(data, parse); + public IPacketWriter WritePacket() => new PacketWriter(); + } +} diff --git a/Plugins/TBC_Alpha_5610/TBC_Alpha_5610.csproj b/Plugins/TBC_Alpha_5610/TBC_Alpha_5610.csproj new file mode 100644 index 0000000..1f10a67 --- /dev/null +++ b/Plugins/TBC_Alpha_5610/TBC_Alpha_5610.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {19DD917C-87C6-407E-BFAA-9ECD37155040} + Library + Properties + TBC_Alpha_5610 + TBC_Alpha_5610 + v4.6.1 + 512 + + + true + full + false + ..\..\bin\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\bin\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7FF9-4B3E-AAAD-CC7C329F924A} + Common + False + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba53c66 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# AIO Sandbox + +This project is an attempt to make a plugin based "All In One" server sandbox that supports all the pre-release builds of WoW with an emphasis on exploration. +You'll need [.Net Core 4.6.1](https://www.microsoft.com/en-gb/download/details.aspx?id=49981) to run this. + +##### Builds: ##### + +Clients for the below can be found [on this thread](http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-model-editing/406638-collection-exploration-patches-of-various-locations.html). + +| Build | Status | +| ----------------------- | :------------- | +| Alpha (0.5.3) | Working | +| Beta 1 (0.5.5) | Not Implemented| +| Beta 2 (0.6.0) | Working | +| Beta 3 (0.7.X) | Usable | +| Beta 3 (0.8.0) | Usable | +| Beta 3 (0.9.0) | Usable | +| Beta 3 (0.10.0 - 0.11.0)| Usable | +| Beta 3 (0.12.0) | Usable | +| TBC Alpha (2.0.0) | Can't login | + +\* Those marked 'Usable' have intermittent area triggers however the below commands negate this issue. + +##### Commands: ##### +* **.demorph** : resets current morph state +* **.help** : lists all commands and their parameters +* **.gps** : displays your current co-ordinates +* **.go {name}** : teleports you to the specified location; partial locations will suggest available options +* **.go {x} {y} {z} Optional: {mapid}** : teleports you to the supplied co-ordinates and map +* **.go instance {name}** : teleports inside the specified instance; partial locations will suggest available options +* **.go instance {id}** : teleports inside the specified areatrigger +* **.morph {id}** : morphs the player to the specified model +* **.nudge [1-100]** : teleports you forward X * one step in the direction you're facing; ideal for invisible walls +* **.speed [0.1 - 10] Optional: {run | swim | all}** : sets your speed; defaults to 'all' if no type is supplied + +For some interesting places to visit have a look at [Marlamin's map viewer](https://newmaps.marlam.in) - co-ordinates can be toggled with the "Enable technical details?" checkbox. diff --git a/WorldServer/Network/RealmManager.cs b/WorldServer/Network/RealmManager.cs new file mode 100644 index 0000000..f9b6167 --- /dev/null +++ b/WorldServer/Network/RealmManager.cs @@ -0,0 +1,31 @@ +using System; +using System.Net.Sockets; +using Common.Logging; +using Common.Constants; + +namespace WorldServer.Network +{ + public class RealmManager + { + public static RealmSocket RealmSession; + public Socket realmSocket; + public Socket proxySocket; + + public void RecieveRealm() + { + WorldServer.Sandbox.AuthHandler.HandleRealmList(realmSocket); + } + + public void RecieveProxy() + { + Log.Message(); + Log.Message(LogType.NORMAL, "Begin redirection to WorldServer."); + + proxySocket.SendData(WorldServer.Sandbox.AuthHandler.HandleRedirect()); + proxySocket.Close(); + + Log.Message(LogType.NORMAL, "Successfully redirected to WorldServer."); + Log.Message(); + } + } +} diff --git a/WorldServer/Network/RealmSocket.cs b/WorldServer/Network/RealmSocket.cs new file mode 100644 index 0000000..d4e3548 --- /dev/null +++ b/WorldServer/Network/RealmSocket.cs @@ -0,0 +1,81 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Common.Logging; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace WorldServer.Network +{ + public class RealmSocket + { + public bool Started { get; private set; } = false; + + private CancellationTokenSource token = new CancellationTokenSource(); + private TcpListener realmListener; + private TcpListener proxyListener; + + public bool Start() + { + try + { + realmListener = new TcpListener(IPAddress.Parse("127.0.0.1"), WorldServer.Sandbox.RealmPort); + realmListener.Start(); + + proxyListener = new TcpListener(IPAddress.Parse("127.0.0.1"), WorldServer.Sandbox.RedirectPort); + proxyListener.Start(); + + Started = true; + } + catch (Exception e) + { + Log.Message(LogType.ERROR, "{0}", e.Message); + Log.Message(); + Started = false; + } + + return Started; + } + + public void StartRealmThread() => Task.Run(() => AcceptRealmConnection(), token.Token); + + public void StartProxyThread() => Task.Run(() => AcceptProxyConnection(), token.Token); + + protected void AcceptRealmConnection() + { + while (!token.IsCancellationRequested) + { + Thread.Sleep(1); + if (realmListener.Pending()) + { + RealmManager Realm = new RealmManager(); + Realm.realmSocket = realmListener.AcceptSocket(); + Task.Run(() => Realm.RecieveRealm(), token.Token); + } + } + } + + protected void AcceptProxyConnection() + { + while (!token.IsCancellationRequested) + { + Thread.Sleep(1); + if (proxyListener.Pending()) + { + RealmManager Proxy = new RealmManager(); + Proxy.proxySocket = proxyListener.AcceptSocket(); + Task.Run(() => Proxy.RecieveProxy(), token.Token); + } + } + } + + public void Dispose() + { + token.Cancel(); + + realmListener.Stop(); + proxyListener.Stop(); + } + } +} diff --git a/WorldServer/Network/WorldManager.cs b/WorldServer/Network/WorldManager.cs new file mode 100644 index 0000000..84cf164 --- /dev/null +++ b/WorldServer/Network/WorldManager.cs @@ -0,0 +1,67 @@ +using System; +using System.Net.Sockets; +using System.Threading; +using Common.Logging; +using Common.Structs; +using Common.Interfaces; +using Common.Constants; +using Common.Network; +using System.Threading.Tasks; + +namespace WorldServer.Network +{ + public class WorldManager : IWorldManager + { + public Account Account { get; set; } + public Socket Socket { get; set; } + public static WorldSocket WorldSession { get; set; } + + private int autosave; + + public void Recieve() + { + autosave = Environment.TickCount + 60000; + + this.Send(WorldServer.Sandbox.AuthHandler.HandleAuthChallenge()); //SMSG_AUTH_CHALLENGE + + while (WorldSession.ListenWorldSocket && Socket.Connected) + { + Thread.Sleep(1); + if (Socket.Available > 0) + { + byte[] buffer = new byte[Socket.Available]; + Socket.Receive(buffer, buffer.Length, SocketFlags.None); + + IPacketReader pkt = WorldServer.Sandbox.ReadPacket(buffer); + if (WorldServer.Sandbox.Opcodes.OpcodeExists(pkt.Opcode)) + { + Opcodes opcode = WorldServer.Sandbox.Opcodes[pkt.Opcode]; + Log.Message(LogType.DUMP, "RECEIVED OPCODE: {0}, LENGTH: {1}", opcode.ToString(), pkt.Size); + PacketManager.InvokeHandler(pkt, this, opcode); + } + else + Log.Message(LogType.DEBUG, "UNKNOWN OPCODE: 0x{0} ({1}), LENGTH: {1}", pkt.Opcode.ToString("X"), (uint)pkt.Opcode, pkt.Size); + } + + //Auto save + if(Environment.TickCount >= autosave || Environment.TickCount < (autosave - 60000)) + { + autosave = Environment.TickCount + 60000; + Task.Run(() => Account?.Save()); + } + } + + Account?.Save(); + if (Account?.ActiveCharacter != null) + Account.ActiveCharacter.IsOnline = false; + + Log.Message(LogType.DEBUG, "CLIENT DISCONNECTED {0}", Account?.Name ?? string.Empty); + Socket.Close(); + } + + public void Send(IPacketWriter packet) + { + Socket.SendData(packet, packet.Name); + } + } +} diff --git a/WorldServer/Network/WorldSocket.cs b/WorldServer/Network/WorldSocket.cs new file mode 100644 index 0000000..0e4f5a6 --- /dev/null +++ b/WorldServer/Network/WorldSocket.cs @@ -0,0 +1,61 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Common.Logging; +using System.Threading.Tasks; +using System.Collections.Concurrent; + +namespace WorldServer.Network +{ + public class WorldSocket + { + public bool Started { get; private set; } = false; + public bool ListenWorldSocket { get; private set; } = true; + private CancellationTokenSource token = new CancellationTokenSource(); + private TcpListener worldListener; + + public bool Start() + { + try + { + worldListener = new TcpListener(IPAddress.Parse("127.0.0.1"), WorldServer.Sandbox.WorldPort); + worldListener.Start(); + Started = true; + } + catch (Exception e) + { + Log.Message(LogType.ERROR, "{0}", e.Message); + Log.Message(); + Started = false; + } + + return Started; + } + + public void StartConnectionThread() + { + new Thread(AcceptConnection).Start(); + } + + protected void AcceptConnection() + { + while (ListenWorldSocket) + { + Thread.Sleep(1); + if (worldListener.Pending()) + { + WorldManager World = new WorldManager(); + World.Socket = worldListener.AcceptSocket(); + Task.Run(() => World.Recieve(), token.Token); + } + } + } + + protected void Dispose() + { + token.Cancel(); + worldListener.Stop(); + } + } +} diff --git a/WorldServer/Packets/HandlerDefinitions.cs b/WorldServer/Packets/HandlerDefinitions.cs new file mode 100644 index 0000000..9b987ee --- /dev/null +++ b/WorldServer/Packets/HandlerDefinitions.cs @@ -0,0 +1,53 @@ +using Common.Network; + +namespace WorldServer.Packets +{ + public class HandlerDefinitions + { + public static void InitializePacketHandler() + { + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_AUTH_SESSION, WorldServer.Sandbox.AuthHandler.HandleAuthSession); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_LOGOUT_REQUEST, WorldServer.Sandbox.AuthHandler.HandleLogoutRequest); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_CHAR_ENUM, WorldServer.Sandbox.CharHandler.HandleCharEnum); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_CHAR_CREATE, WorldServer.Sandbox.CharHandler.HandleCharCreate); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_CHAR_DELETE, WorldServer.Sandbox.CharHandler.HandleCharDelete); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_PING, WorldServer.Sandbox.WorldHandler.HandlePing); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_PLAYER_LOGIN, WorldServer.Sandbox.WorldHandler.HandlePlayerLogin); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_QUERY_TIME, WorldServer.Sandbox.WorldHandler.HandleQueryTime); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_NAME_QUERY, WorldServer.Sandbox.CharHandler.HandleNameCache); + + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_WORLD_TELEPORT, WorldServer.Sandbox.WorldHandler.HandleWorldTeleport); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_WORLDPORT_ACK, WorldServer.Sandbox.WorldHandler.HandleWorldTeleportAck); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_AREATRIGGER, WorldServer.Sandbox.WorldHandler.HandleAreaTrigger); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_ZONEUPDATE, WorldServer.Sandbox.WorldHandler.HandleZoneUpdate); + + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_MESSAGECHAT, WorldServer.Sandbox.CharHandler.HandleMessageChat); + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_TEXT_EMOTE, WorldServer.Sandbox.CharHandler.HandleTextEmote); + + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_FORWARD, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_BACKWARD, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_STOP, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_STRAFE_LEFT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_STRAFE_RIGHT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_STOP_STRAFE, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_JUMP, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_TURN_LEFT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_TURN_RIGHT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_STOP_TURN, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_PITCH_UP, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_PITCH_DOWN, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_STOP_PITCH, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_SET_RUN_MODE, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_SET_WALK_MODE, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_START_SWIM, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_STOP_SWIM, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_SET_FACING, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_SET_PITCH, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_ROOT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_UNROOT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + PacketManager.DefineOpcodeHandler(Opcodes.MSG_MOVE_HEARTBEAT, WorldServer.Sandbox.CharHandler.HandleMovementStatus); + + PacketManager.DefineOpcodeHandler(Opcodes.CMSG_STANDSTATECHANGE, WorldServer.Sandbox.CharHandler.HandleStandState); + } + } +} diff --git a/WorldServer/Plugins/PluginHandler.cs b/WorldServer/Plugins/PluginHandler.cs new file mode 100644 index 0000000..9ed5813 --- /dev/null +++ b/WorldServer/Plugins/PluginHandler.cs @@ -0,0 +1,70 @@ +using Common.Interfaces; +using Common.Logging; +using Common.Structs; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace WorldServer.Plugins +{ + public class PluginHandler + { + public static List Sandboxes; + + public static void GetPlugins() + { + DirectoryInfo dInfo = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "Plugins")); + FileInfo[] files = dInfo.GetFiles("*.dll"); + + var assemblies = files.Select(x => Assembly.LoadFile(x.FullName)); + var availableTypes = assemblies.SelectMany(x => x.GetTypes()); + + Sandboxes = availableTypes.Where(x => x.GetInterfaces().Contains(typeof(ISandbox))) + .Select(x => new SandboxHost((ISandbox)Activator.CreateInstance(x))) + .OrderBy(x => x.Build) + .ToList(); + } + + public static SandboxHost SelectPlugin(bool firstLoad = false) + { + bool retry = false; + + //Print initial plugin options + if (firstLoad) + { + Log.Message(LogType.INIT, "Select a plugin from the below list:"); + for (int i = 0; i < Sandboxes.Count; i++) + { + string realm = Sandboxes[i].RealmName[0] == '|' ? Sandboxes[i].RealmName.Substring(10) : Sandboxes[i].RealmName; + Log.Message(LogType.INIT, $"{i + 1}. {realm}"); + } + Log.Message(); + } + + //Load plugin from user input + Log.SetType(LogType.MISC); //Set font colour + + if (int.TryParse(Console.ReadLine().Trim(), out int index)) + { + index--; + if (index < 0 || index >= Sandboxes.Count) //Out of range + retry = true; + } + else + { + retry = true; + } + + if (retry) + { + Log.Message(LogType.ERROR, "Invalid selection."); //Not an integer + return SelectPlugin(); + } + + return Sandboxes[index]; + } + } +} diff --git a/WorldServer/Properties/AssemblyInfo.cs b/WorldServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..13ec8d9 --- /dev/null +++ b/WorldServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AIO Sandbox")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("WorldServer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("33346bf3-5627-498a-8d19-ba8c7c60784e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/WorldServer/SandboxHost.cs b/WorldServer/SandboxHost.cs new file mode 100644 index 0000000..7fd3562 --- /dev/null +++ b/WorldServer/SandboxHost.cs @@ -0,0 +1,36 @@ +using Common.Interfaces; +using Common.Interfaces.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WorldServer +{ + public class SandboxHost + { + private readonly ISandbox _instance; + private readonly string _realmName; + + public SandboxHost(ISandbox sandbox) + { + _instance = sandbox; + _realmName = _instance.RealmName[0] == '|' ? _instance.RealmName.Substring(10) : _instance.RealmName; //Remove colour code + } + + public string RealmName => _realmName; + public int Build => _instance.Build; + public int RealmPort => _instance.RealmPort; + public int RedirectPort => _instance.RedirectPort; + public int WorldPort => _instance.WorldPort; + + public IOpcodes Opcodes => _instance.Opcodes; + + public IAuthHandler AuthHandler => _instance.AuthHandler; + public ICharHandler CharHandler => _instance.CharHandler; + public IWorldHandler WorldHandler => _instance.WorldHandler; + public IPacketReader ReadPacket(byte[] data, bool parse = true) => _instance.ReadPacket(data, parse); + public IPacketWriter WritePacket() => _instance.WritePacket(); + + } +} diff --git a/WorldServer/WorldServer.cs b/WorldServer/WorldServer.cs new file mode 100644 index 0000000..36f02b5 --- /dev/null +++ b/WorldServer/WorldServer.cs @@ -0,0 +1,68 @@ +using System; +using Common.Logging; +using WorldServer.Network; +using WorldServer.Packets; +using Common.Structs; +using System.Linq; +using Common.Cryptography; +using System.Configuration; +using WorldServer.Plugins; + +namespace WorldServer +{ + class WorldServer + { + public static SandboxHost Sandbox; + + static void Main() + { + Log.Message(LogType.INIT, " AIO SANDBOX "); + Log.Message(LogType.INIT, " REALM/PROXY/WORLD "); + Log.Message(); + Log.Message(LogType.NORMAL, "Starting AIO Sandbox WorldServer..."); + Log.Message(); + + //Load Plugins + PluginHandler.GetPlugins(); + Sandbox = PluginHandler.SelectPlugin(true); + + RealmManager.RealmSession = new RealmSocket(); + WorldManager.WorldSession = new WorldSocket(); + + if (WorldManager.WorldSession.Start() && RealmManager.RealmSession.Start()) + { + RealmManager.RealmSession.StartRealmThread(); + RealmManager.RealmSession.StartProxyThread(); + WorldManager.WorldSession.StartConnectionThread(); + + Log.Message(); + Log.Message(LogType.NORMAL, "RealmProxy listening on {0} port(s) {1}.", "127.0.0.1", Sandbox.RealmPort); + Log.Message(LogType.NORMAL, "RedirectServer listening on {0} port {1}.", "127.0.0.1", Sandbox.RedirectPort); + Log.Message(LogType.NORMAL, "WorldServer listening on {0} port {1}.", "127.0.0.1", Sandbox.WorldPort); + Log.Message(LogType.NORMAL, "Started {0}", Sandbox.RealmName); + Log.Message(); + + ClientAuth.Password = ConfigurationManager.AppSettings["Password"]; + if (!string.IsNullOrEmpty(ClientAuth.Password)) + { + Log.Message(LogType.NORMAL, "Default client password set to \"{0}\"", ClientAuth.Password); + Log.Message(); + } + + HandlerDefinitions.InitializePacketHandler(); + } + else + { + if (!WorldManager.WorldSession.Started) + Log.Message(LogType.ERROR, "WorldServer couldn't be started."); + if(!RealmManager.RealmSession.Started) + Log.Message(LogType.ERROR, "RealmServer couldn't be started."); + + Log.Message(); + } + + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } +} diff --git a/WorldServer/WorldServer.csproj b/WorldServer/WorldServer.csproj new file mode 100644 index 0000000..7222674 --- /dev/null +++ b/WorldServer/WorldServer.csproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + {B736A574-F0AD-43D2-BF24-A54EDC0BCD6B} + Exe + Properties + WorldServer + AIO Sandbox + v4.6.1 + 512 + + + + x86 + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + false + + + favicon.ico + + + + + + + + + + + + + + + + + + + + + + + + {63268176-7ff9-4b3e-aaad-cc7c329f924a} + Common + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WorldServer/app.config b/WorldServer/app.config new file mode 100644 index 0000000..1f3a901 --- /dev/null +++ b/WorldServer/app.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/WorldServer/favicon.ico b/WorldServer/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..23a3028a49113ee2de0a81e9d0d16afbcc304296 GIT binary patch literal 32038 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 literal 0 HcmV?d00001