diff --git a/Source/BNetServer/Managers/LoginServiceManager.cs b/Source/BNetServer/Managers/LoginServiceManager.cs index cec2260f0..be39137d5 100644 --- a/Source/BNetServer/Managers/LoginServiceManager.cs +++ b/Source/BNetServer/Managers/LoginServiceManager.cs @@ -181,7 +181,7 @@ namespace BNetServer } [AttributeUsage(AttributeTargets.Method)] - public sealed class ServiceAttribute : System.Attribute + public sealed class ServiceAttribute : Attribute { public uint ServiceHash { get; set; } public uint MethodId { get; set; } diff --git a/Source/Game/Globals/Global.cs b/Source/Game/Globals/Global.cs index d9689d1f2..1fba11dcb 100644 --- a/Source/Game/Globals/Global.cs +++ b/Source/Game/Globals/Global.cs @@ -45,7 +45,7 @@ public static class Global public static ObjectManager ObjectMgr { get { return ObjectManager.Instance; } } public static WorldManager WorldMgr { get { return WorldManager.Instance; } } public static RealmManager RealmMgr { get { return RealmManager.Instance; } } - public static ServiceDispatcher ServiceMgr { get { return ServiceDispatcher.Instance; } } + public static WorldServiceManager ServiceMgr { get { return WorldServiceManager.Instance; } } //Guild public static PetitionManager PetitionMgr { get { return PetitionManager.Instance; } } diff --git a/Source/Game/Handlers/BattlenetHandler.cs b/Source/Game/Handlers/BattlenetHandler.cs index 8b8c56429..c5a526d56 100644 --- a/Source/Game/Handlers/BattlenetHandler.cs +++ b/Source/Game/Handlers/BattlenetHandler.cs @@ -28,7 +28,14 @@ namespace Game [WorldPacketHandler(ClientOpcodes.BattlenetRequest, Status = SessionStatus.Authed)] void HandleBattlenetRequest(BattlenetRequest request) { - Global.ServiceMgr.Dispatch(this, request.Method.GetServiceHash(), request.Method.Token, request.Method.GetMethodId(), new CodedInputStream(request.Data)); + var handler = Global.ServiceMgr.GetHandler(request.Method.GetServiceHash(), request.Method.GetMethodId()); + if (handler != null) + handler.Invoke(this, request.Method, new CodedInputStream(request.Data)); + else + { + SendBattlenetResponse(request.Method.GetServiceHash(), request.Method.GetMethodId(), request.Method.Token, BattlenetRpcErrorCode.RpcNotImplemented); + Log.outDebug(LogFilter.SessionRpc, "{0} tried to call invalid service {1}", GetPlayerInfo(), request.Method.GetServiceHash()); + } } [WorldPacketHandler(ClientOpcodes.ChangeRealmTicket, Status = SessionStatus.Authed)] diff --git a/Source/Game/Networking/Packets/BattlenetPackets.cs b/Source/Game/Networking/Packets/BattlenetPackets.cs index b25468df6..536b962e1 100644 --- a/Source/Game/Networking/Packets/BattlenetPackets.cs +++ b/Source/Game/Networking/Packets/BattlenetPackets.cs @@ -116,7 +116,7 @@ namespace Game.Networking.Packets public Array Secret = new Array(32); } - struct MethodCall + public struct MethodCall { public uint GetServiceHash() { return (uint)(Type >> 32); } public uint GetMethodId() { return (uint)(Type & 0xFFFFFFFF); } diff --git a/Source/Game/Services/GameUtilitiesService.cs b/Source/Game/Services/GameUtilitiesService.cs index 186f90643..da233e4e4 100644 --- a/Source/Game/Services/GameUtilitiesService.cs +++ b/Source/Game/Services/GameUtilitiesService.cs @@ -25,143 +25,9 @@ using System.Collections.Generic; namespace Game { - class GameUtilitiesService : ServiceBase + public partial class WorldSession { - public GameUtilitiesService(WorldSession session, uint serviceHash) : base(session, serviceHash) { } - - public override void CallServerMethod(uint token, uint methodId, CodedInputStream stream) - { - switch (methodId) - { - case 1: - { - ClientRequest request = new ClientRequest(); - request.MergeFrom(stream); - - - ClientResponse response = new ClientResponse(); - BattlenetRpcErrorCode status = HandleProcessClientRequest(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.ProcessClientRequest(bgs.protocol.game_utilities.v1.ClientRequest: {1}) returned bgs.protocol.game_utilities.v1.ClientResponse: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(1, token, response); - else - SendResponse(1, token, status); - break; - } - case 2: - { - PresenceChannelCreatedRequest request = new PresenceChannelCreatedRequest(); - request.MergeFrom(stream); - - - Bgs.Protocol.NoData response = new Bgs.Protocol.NoData(); - BattlenetRpcErrorCode status = HandlePresenceChannelCreated(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.PresenceChannelCreated(bgs.protocol.game_utilities.v1.PresenceChannelCreatedRequest: {1}) returned bgs.protocol.NoData: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(2, token, response); - else - SendResponse(2, token, status); - break; - } - case 3: - { - GetPlayerVariablesRequest request = new GetPlayerVariablesRequest(); - request.MergeFrom(stream); - - - GetPlayerVariablesResponse response = new GetPlayerVariablesResponse(); - BattlenetRpcErrorCode status = HandleGetPlayerVariables(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.GetPlayerVariables(bgs.protocol.game_utilities.v1.GetPlayerVariablesRequest: {1}) returned bgs.protocol.game_utilities.v1.GetPlayerVariablesResponse: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(3, token, response); - else - SendResponse(3, token, status); - break; - } - case 6: - { - ServerRequest request = new ServerRequest(); - request.MergeFrom(stream); - - - ServerResponse response = new ServerResponse(); - BattlenetRpcErrorCode status = HandleProcessServerRequest(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.ProcessServerRequest(bgs.protocol.game_utilities.v1.ServerRequest: {1}) returned bgs.protocol.game_utilities.v1.ServerResponse: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(6, token, response); - else - SendResponse(6, token, status); - break; - } - case 7: - { - GameAccountOnlineNotification request = new GameAccountOnlineNotification(); - request.MergeFrom(stream); - - - BattlenetRpcErrorCode status = HandleOnGameAccountOnline(request); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.OnGameAccountOnline(bgs.protocol.game_utilities.v1.GameAccountOnlineNotification: {1}) status: {2}.", - GetCallerInfo(), request.ToString(), status); - if (status != 0) - SendResponse(7, token, status); - break; - } - case 8: - { - GameAccountOfflineNotification request = new GameAccountOfflineNotification(); - request.MergeFrom(stream); - - - BattlenetRpcErrorCode status = HandleOnGameAccountOffline(request); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.OnGameAccountOffline(bgs.protocol.game_utilities.v1.GameAccountOfflineNotification: {1}) status: {2}.", - GetCallerInfo(), request.ToString(), status); - if (status != 0) - SendResponse(8, token, status); - break; - } - case 9: - { - GetAchievementsFileRequest request = new GetAchievementsFileRequest(); - request.MergeFrom(stream); - - - GetAchievementsFileResponse response = new GetAchievementsFileResponse(); - BattlenetRpcErrorCode status = HandleGetAchievementsFile(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.GetAchievementsFile(bgs.protocol.game_utilities.v1.GetAchievementsFileRequest: {1}) returned bgs.protocol.game_utilities.v1.GetAchievementsFileResponse: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(9, token, response); - else - SendResponse(9, token, status); - break; - } - case 10: - { - GetAllValuesForAttributeRequest request = new GetAllValuesForAttributeRequest(); - request.MergeFrom(stream); - - - GetAllValuesForAttributeResponse response = new GetAllValuesForAttributeResponse(); - BattlenetRpcErrorCode status = HandleGetAllValuesForAttribute(request, response); - Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server method GameUtilitiesService.GetAllValuesForAttribute(bgs.protocol.game_utilities.v1.GetAllValuesForAttributeRequest: {1}) returned bgs.protocol.game_utilities.v1.GetAllValuesForAttributeResponse: {2} status: {3}.", - GetCallerInfo(), request.ToString(), response.ToString(), status); - if (status == 0) - SendResponse(10, token, response); - else - SendResponse(10, token, status); - break; - } - default: - Log.outError(LogFilter.ServiceProtobuf, "Bad method id {0}.", methodId); - SendResponse(methodId, token, BattlenetRpcErrorCode.RpcInvalidMethod); - break; - } - } - + [Service(OriginalHash.GameUtilitiesService, 1)] BattlenetRpcErrorCode HandleProcessClientRequest(ClientRequest request, ClientResponse response) { Bgs.Protocol.Attribute command = null; @@ -177,7 +43,7 @@ namespace Game if (command == null) { - Log.outError(LogFilter.SessionRpc, "{0} sent ClientRequest with no command.", GetCallerInfo()); + Log.outError(LogFilter.SessionRpc, "{0} sent ClientRequest with no command.", GetPlayerInfo()); return BattlenetRpcErrorCode.RpcMalformedRequest; } @@ -189,48 +55,7 @@ namespace Game return BattlenetRpcErrorCode.RpcNotImplemented; } - BattlenetRpcErrorCode HandlePresenceChannelCreated(PresenceChannelCreatedRequest request, Bgs.Protocol.NoData response) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.PresenceChannelCreated: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - - BattlenetRpcErrorCode HandleGetPlayerVariables(GetPlayerVariablesRequest request, GetPlayerVariablesResponse response) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.GetPlayerVariables: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - - BattlenetRpcErrorCode HandleProcessServerRequest(ServerRequest request, ServerResponse response) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.ProcessServerRequest: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - - BattlenetRpcErrorCode HandleOnGameAccountOnline(GameAccountOnlineNotification request) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.OnGameAccountOnline: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - - BattlenetRpcErrorCode HandleOnGameAccountOffline(GameAccountOfflineNotification request) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.OnGameAccountOffline: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - - BattlenetRpcErrorCode HandleGetAchievementsFile(GetAchievementsFileRequest request, GetAchievementsFileResponse response) - { - Log.outError(LogFilter.ServiceProtobuf, "{0} Client tried to call not implemented method GameUtilitiesService.GetAchievementsFile: {1}", - GetCallerInfo(), request.ToString()); - return BattlenetRpcErrorCode.RpcNotImplemented; - } - + [Service(OriginalHash.GameUtilitiesService, 10)] BattlenetRpcErrorCode HandleGetAllValuesForAttribute(GetAllValuesForAttributeRequest request, GetAllValuesForAttributeResponse response) { if (request.AttributeKey == "Command_RealmListRequest_v1_b9") @@ -260,7 +85,7 @@ namespace Game response.Attribute.Add(attribute); var realmCharacterCounts = new RealmCharacterCountList(); - foreach (var characterCount in _session.GetRealmCharacterCounts()) + foreach (var characterCount in GetRealmCharacterCounts()) { RealmCharacterCountEntry countEntry = new RealmCharacterCountEntry(); countEntry.WowRealmAddress = (int)characterCount.Key; @@ -281,8 +106,8 @@ namespace Game { var realmAddress = Params.LookupByKey("Param_RealmAddress"); if (realmAddress != null) - return Global.RealmMgr.JoinRealm((uint)realmAddress.UintValue, Global.WorldMgr.GetRealm().Build, System.Net.IPAddress.Parse(_session.GetRemoteAddress()), _session.GetRealmListSecret(), - _session.GetSessionDbcLocale(), _session.GetOS(), _session.GetAccountName(), response); + return Global.RealmMgr.JoinRealm((uint)realmAddress.UintValue, Global.WorldMgr.GetRealm().Build, System.Net.IPAddress.Parse(GetRemoteAddress()), GetRealmListSecret(), + GetSessionDbcLocale(), GetOS(), GetAccountName(), response); return BattlenetRpcErrorCode.Ok; } diff --git a/Source/Game/Services/ServiceDispatcher.cs b/Source/Game/Services/ServiceDispatcher.cs deleted file mode 100644 index bcb79dee5..000000000 --- a/Source/Game/Services/ServiceDispatcher.cs +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2012-2016 CypherCore - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Framework.Constants; -using Google.Protobuf; -using System; -using System.Collections.Generic; - -namespace Game.Services -{ - public class ServiceDispatcher : Singleton - { - ServiceDispatcher() - { - AddService(NameHash.GameUtilitiesService); - } - - public void Dispatch(WorldSession session, uint serviceHash, uint token, uint methodId, CodedInputStream stream) - { - var action = _dispatchers.LookupByKey(serviceHash); - if (action != null) - action(session, serviceHash, token, methodId, stream); - else - Log.outDebug(LogFilter.SessionRpc, "{0} tried to call invalid service {1}", session.GetPlayerInfo(), serviceHash); - } - - void AddService(NameHash OriginalHash) where Service : ServiceBase - { - _dispatchers[(uint)OriginalHash] = Dispatch; - } - - void Dispatch(WorldSession session, uint serviceHash, uint token, uint methodId, CodedInputStream stream) where Service : ServiceBase - { - var obj = (Service)Activator.CreateInstance(typeof(Service), session, serviceHash); - obj.CallServerMethod(token, methodId, stream); - } - - Dictionary _dispatchers = new Dictionary(); - - delegate void DispatcherHandler(WorldSession session, uint serviceHash, uint token, uint methodId, CodedInputStream stream); - } - - abstract class ServiceBase - { - protected ServiceBase(WorldSession session, uint serviceHash) - { - _session = session; - _serviceHash = serviceHash; - } - - public abstract void CallServerMethod(uint token, uint methodId, CodedInputStream stream); - - public void SendRequest(uint methodId, IMessage request, Action callback) { _session.SendBattlenetRequest(_serviceHash, methodId, request, callback); } - public void SendRequest(uint methodId, IMessage request) { _session.SendBattlenetRequest(_serviceHash, methodId, request); } - public void SendResponse(uint methodId, uint token, BattlenetRpcErrorCode status) { _session.SendBattlenetResponse(_serviceHash, methodId, token, status); } - public void SendResponse(uint methodId, uint token, IMessage response) { _session.SendBattlenetResponse(_serviceHash, methodId, token, response); } - - public string GetCallerInfo() - { - return _session.GetPlayerInfo(); - } - - protected WorldSession _session; - protected uint _serviceHash; - } -} diff --git a/Source/Game/Services/WorldServiceManager.cs b/Source/Game/Services/WorldServiceManager.cs new file mode 100644 index 000000000..b38f1ee1f --- /dev/null +++ b/Source/Game/Services/WorldServiceManager.cs @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012-2016 CypherCore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Framework.Constants; +using Game.Networking.Packets; +using Google.Protobuf; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +namespace Game.Services +{ + public class WorldServiceManager : Singleton + { + ConcurrentDictionary<(uint ServiceHash, uint MethodId), WorldServiceHandler> serviceHandlers; + + WorldServiceManager() + { + serviceHandlers = new ConcurrentDictionary<(uint ServiceHash, uint MethodId), WorldServiceHandler>(); + + Assembly currentAsm = Assembly.GetExecutingAssembly(); + foreach (var type in currentAsm.GetTypes()) + { + foreach (var methodInfo in type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)) + { + foreach (var serviceAttr in methodInfo.GetCustomAttributes()) + { + if (serviceAttr == null) + continue; + + var key = (serviceAttr.ServiceHash, serviceAttr.MethodId); + if (serviceHandlers.ContainsKey(key)) + { + Log.outError(LogFilter.Network, $"Tried to override ServiceHandler: {serviceHandlers[key]} with {methodInfo.Name} (ServiceHash: {serviceAttr.ServiceHash} MethodId: {serviceAttr.MethodId})"); + continue; + } + + var parameters = methodInfo.GetParameters(); + if (parameters.Length == 0) + { + Log.outError(LogFilter.Network, $"Method: {methodInfo.Name} needs atleast one paramter"); + continue; + } + + serviceHandlers[key] = new WorldServiceHandler(methodInfo, parameters); + } + } + } + } + + public WorldServiceHandler GetHandler(uint serviceHash, uint methodId) + { + return serviceHandlers.LookupByKey((serviceHash, methodId)); + } + } + + public class WorldServiceHandler + { + Delegate methodCaller; + Type requestType; + Type responseType; + + public WorldServiceHandler(MethodInfo info, ParameterInfo[] parameters) + { + requestType = parameters[0].ParameterType; + if (parameters.Length > 1) + responseType = parameters[1].ParameterType; + + if (responseType != null) + methodCaller = info.CreateDelegate(Expression.GetDelegateType(new[] { typeof(WorldSession), requestType, responseType, info.ReturnType })); + else + methodCaller = info.CreateDelegate(Expression.GetDelegateType(new[] { typeof(WorldSession), requestType, info.ReturnType })); + } + + public void Invoke(WorldSession session, MethodCall methodCall, CodedInputStream stream) + { + var request = (IMessage)Activator.CreateInstance(requestType); + request.MergeFrom(stream); + + BattlenetRpcErrorCode status; + if (responseType != null) + { + var response = (IMessage)Activator.CreateInstance(responseType); + status = (BattlenetRpcErrorCode)methodCaller.DynamicInvoke(session, request, response); + Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server Method: {1}) Returned: {2} Status: {3}.", session.GetRemoteAddress(), request, response, status); + if (status == 0) + session.SendBattlenetResponse(methodCall.GetServiceHash(), methodCall.GetMethodId(), methodCall.Token, response); + else + session.SendBattlenetResponse(methodCall.GetServiceHash(), methodCall.GetMethodId(), methodCall.Token, status); + } + else + { + status = (BattlenetRpcErrorCode)methodCaller.DynamicInvoke(session, request); + Log.outDebug(LogFilter.ServiceProtobuf, "{0} Client called server Method: {1}) Status: {2}.", session.GetRemoteAddress(), request, status); + if (status != 0) + session.SendBattlenetResponse(methodCall.GetServiceHash(), methodCall.GetMethodId(), methodCall.Token, status); + } + } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class ServiceAttribute : Attribute + { + public uint ServiceHash { get; set; } + public uint MethodId { get; set; } + + public ServiceAttribute(OriginalHash serviceHash, uint methodId) + { + ServiceHash = (uint)serviceHash; + MethodId = methodId; + } + } +}