Files
CypherCore/Source/Game/Network/PacketManager.cs
T
Fabian a3dc7b3f48 Ported .Net Core commits:
hondacrx:
- Initial commit: Switch to .Net Core 2.0
- Fix build and removed not needed files
Fabi:
- Updated solution platforms.
- Changed folder structure.
- Change library target framework to netstandard2.0.
- Updated solution platforms again...
- Removed windows specific kernel32 function usage (Ctrl-C handler).
2017-10-26 17:23:44 +02:00

240 lines
8.9 KiB
C#

/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/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 <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Entities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
namespace Game.Network
{
public static class PacketManager
{
public static void Initialize()
{
Assembly currentAsm = Assembly.GetExecutingAssembly();
foreach (var type in currentAsm.GetTypes())
{
foreach (var methodInfo in type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
{
foreach (var msgAttr in methodInfo.GetCustomAttributes<WorldPacketHandlerAttribute>())
{
if (msgAttr == null)
continue;
if (msgAttr.Opcode == ClientOpcodes.Unknown)
{
Log.outError(LogFilter.Network, "Opcode {0} does not have a value", msgAttr.Opcode);
continue;
}
if (_clientPacketTable.ContainsKey(msgAttr.Opcode))
{
Log.outError(LogFilter.Network, "Tried to override OpcodeHandler of {0} with {1} (Opcode {2})", _clientPacketTable[msgAttr.Opcode].ToString(), methodInfo.Name, msgAttr.Opcode);
continue;
}
var parameters = methodInfo.GetParameters();
if (parameters.Length == 0)
{
Log.outError(LogFilter.Network, "Method: {0} Has no paramters", methodInfo.Name);
continue;
}
if (parameters[0].ParameterType.BaseType != typeof(ClientPacket))
{
Log.outError(LogFilter.Network, "Method: {0} has wrong BaseType", methodInfo.Name);
continue;
}
_clientPacketTable[msgAttr.Opcode] = new PacketHandler(methodInfo, msgAttr.Status, msgAttr.Processing, parameters[0].ParameterType);
}
}
}
}
public static bool TryPeek(this ConcurrentQueue<WorldPacket> queue, out WorldPacket result, PacketFilter filter)
{
result = null;
if (queue.IsEmpty)
return false;
if (!queue.TryPeek(out result))
return false;
if (!filter.Process(result))
return false;
return true;
}
public static PacketHandler GetHandler(ClientOpcodes opcode)
{
return _clientPacketTable.LookupByKey(opcode);
}
public static bool ContainsHandler(ClientOpcodes opcode)
{
return _clientPacketTable.ContainsKey(opcode);
}
static ConcurrentDictionary<ClientOpcodes, PacketHandler> _clientPacketTable = new ConcurrentDictionary<ClientOpcodes, PacketHandler>();
public static bool IsInstanceOnlyOpcode(ServerOpcodes opcode)
{
switch (opcode)
{
case ServerOpcodes.QuestGiverStatus: // ClientQuest
case ServerOpcodes.DuelRequested: // Client
case ServerOpcodes.DuelInBounds: // Client
case ServerOpcodes.QueryTimeResponse: // Client
case ServerOpcodes.DuelWinner: // Client
case ServerOpcodes.DuelComplete: // Client
case ServerOpcodes.DuelOutOfBounds: // Client
case ServerOpcodes.AttackStop: // Client
case ServerOpcodes.AttackStart: // Client
case ServerOpcodes.MountResult: // Client
return true;
default:
return false;
}
}
}
public class PacketHandler
{
public PacketHandler(MethodInfo info, SessionStatus status, PacketProcessing processingplace, Type type)
{
methodCaller = (Action<WorldSession, ClientPacket>)GetType().GetMethod("CreateDelegate", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).Invoke(null, new object[] { info });
sessionStatus = status;
processingPlace = processingplace;
packetType = type;
}
public void Invoke(WorldSession session, WorldPacket packet)
{
if (packetType == null)
return;
using (var clientPacket = (ClientPacket)Activator.CreateInstance(packetType, packet))
{
clientPacket.Read();
clientPacket.LogPacket(session);
methodCaller(session, clientPacket);
}
}
static Action<WorldSession, ClientPacket> CreateDelegate<P1>(MethodInfo method) where P1 : ClientPacket
{
// create first delegate. It is not fine because its
// signature contains unknown types T and P1
Action<WorldSession, P1> d = (Action<WorldSession, P1>)method.CreateDelegate(typeof(Action<WorldSession, P1>));
// create another delegate having necessary signature.
// It encapsulates first delegate with a closure
return delegate (WorldSession target, ClientPacket p) { d(target, (P1)p); };
}
Action<WorldSession, ClientPacket> methodCaller;
Type packetType;
public PacketProcessing processingPlace { get; private set; }
public SessionStatus sessionStatus { get; private set; }
}
public abstract class PacketFilter
{
public PacketFilter(WorldSession pSession)
{
m_pSession = pSession;
}
public abstract bool Process(WorldPacket packet);
public virtual bool ProcessUnsafe() { return false; }
protected WorldSession m_pSession;
}
public class MapSessionFilter : PacketFilter
{
public MapSessionFilter(WorldSession pSession) : base(pSession) { }
public override bool Process(WorldPacket packet)
{
PacketHandler opHandle = PacketManager.GetHandler((ClientOpcodes)packet.GetOpcode());
//check if packet handler is supposed to be safe
if (opHandle.processingPlace == PacketProcessing.Inplace)
return true;
//we do not process thread-unsafe packets
if (opHandle.processingPlace == PacketProcessing.ThreadUnsafe)
return false;
Player player = m_pSession.GetPlayer();
if (!player)
return false;
//in Map.Update() we do not process packets where player is not in world!
return player.IsInWorld;
}
}
public class WorldSessionFilter : PacketFilter
{
public WorldSessionFilter(WorldSession pSession) : base(pSession) { }
public override bool Process(WorldPacket packet)
{
PacketHandler opHandle = PacketManager.GetHandler((ClientOpcodes)packet.GetOpcode());
//check if packet handler is supposed to be safe
if (opHandle.processingPlace == PacketProcessing.Inplace)
return true;
//thread-unsafe packets should be processed in World.UpdateSessions()
if (opHandle.processingPlace == PacketProcessing.ThreadUnsafe)
return true;
//no player attached? . our client! ^^
Player player = m_pSession.GetPlayer();
if (!player)
return true;
//lets process all packets for non-in-the-world player
return !player.IsInWorld;
}
public override bool ProcessUnsafe() { return true; }
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class WorldPacketHandlerAttribute : Attribute
{
public WorldPacketHandlerAttribute(ClientOpcodes opcode)
{
Opcode = opcode;
Status = SessionStatus.Loggedin;
Processing = PacketProcessing.ThreadUnsafe;
}
public ClientOpcodes Opcode { get; private set; }
public SessionStatus Status { get; set; }
public PacketProcessing Processing { get; set; }
}
}