mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-04 09:25:22 +03:00
Add common server logging.
This commit is contained in:
parent
bb7d633f21
commit
30c1454522
@ -26,14 +26,27 @@ namespace Syroot.Worms.OnlineWorms.Server
|
||||
_server = server;
|
||||
}
|
||||
|
||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||
|
||||
protected override void OnPrePacketHandle(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Client, $"{TcpClient.Client.RemoteEndPoint} >> {packet}");
|
||||
}
|
||||
|
||||
protected override void OnPrePacketSend(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Server, $"{TcpClient.Client.RemoteEndPoint} << {packet}");
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private Packet HandleConnect(ConnectQueryPacket connectPacket)
|
||||
{
|
||||
return new ConnectReplyPacket
|
||||
{
|
||||
Unknown = "Online Worms Private Server",
|
||||
Version = "114"
|
||||
Unknown = _server.Name,
|
||||
Unknown2 = _server.RegionName,
|
||||
Version = _server.Version
|
||||
};
|
||||
}
|
||||
|
||||
|
40
src/Syroot.OnlineWorms.Server/Log.cs
Normal file
40
src/Syroot.OnlineWorms.Server/Log.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.OnlineWorms.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents simplistic textual logging.
|
||||
/// </summary>
|
||||
internal class Log
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private readonly object _lock = new object();
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal void Write(LogCategory category, string text) => Write((ConsoleColor)category, text);
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private void Write(ConsoleColor color, string text)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
ConsoleColor prevColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(text);
|
||||
Console.ForegroundColor = prevColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum LogCategory
|
||||
{
|
||||
Info = ConsoleColor.White,
|
||||
Connect = ConsoleColor.Cyan,
|
||||
Disconnect = ConsoleColor.Magenta,
|
||||
Client = ConsoleColor.DarkCyan,
|
||||
Server = ConsoleColor.DarkMagenta
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
= new Dictionary<Type, Dictionary<Type, MethodInfo>>();
|
||||
|
||||
private readonly Dictionary<Type, MethodInfo> _handlers;
|
||||
private readonly TcpClient _tcpClient;
|
||||
private readonly byte[] _receiveBuffer;
|
||||
private readonly byte[] _sendDataBuffer;
|
||||
private readonly PacketStream _receiveStream;
|
||||
@ -36,19 +35,25 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
/// Initializes a new instance of the <see cref="GameConnection"/> class, handling the given
|
||||
/// <paramref name="tcpClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="tcpClient">The <see cref="TcpClient"/> to communicate with.</param>
|
||||
/// <param name="tcpClient">The <see cref="System.Net.Sockets.TcpClient"/> to communicate with.</param>
|
||||
internal GameConnection(TcpClient tcpClient)
|
||||
{
|
||||
_tcpClient = tcpClient;
|
||||
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} connected");
|
||||
TcpClient = tcpClient;
|
||||
|
||||
_handlers = GetHandlers();
|
||||
_receiveBuffer = new byte[_maxDataSize];
|
||||
_receiveBuffer = new byte[_maxDataSize];
|
||||
_receiveStream = new PacketStream(_tcpClient.GetStream());
|
||||
_sendDataBuffer = new byte[_maxDataSize];
|
||||
_receiveStream = new PacketStream(TcpClient.GetStream());
|
||||
_sendStream = new PacketStream(new MemoryStream(_sendDataBuffer, 0, _maxDataSize, true));
|
||||
}
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TcpClient"/> with which the connection communicates.
|
||||
/// </summary>
|
||||
internal TcpClient TcpClient { get; }
|
||||
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
public void Dispose()
|
||||
@ -77,7 +82,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_tcpClient.Dispose();
|
||||
TcpClient.Dispose();
|
||||
_sendStream.Dispose();
|
||||
}
|
||||
|
||||
@ -85,6 +90,10 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPrePacketHandle(Packet packet) { }
|
||||
|
||||
protected virtual void OnPrePacketSend(Packet packet) { }
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private Dictionary<Type, MethodInfo> GetHandlers()
|
||||
@ -107,13 +116,13 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
|
||||
private void HandlePacket(Packet inPacket)
|
||||
{
|
||||
OnPrePacketHandle(inPacket);
|
||||
Type packetType = inPacket.GetType();
|
||||
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} >> {packetType.Name}");
|
||||
|
||||
// Invoke the handler and send back any packet resulting from it.
|
||||
if (_handlers[packetType].Invoke(this, new[] { inPacket }) is Packet outPacket)
|
||||
{
|
||||
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} << {outPacket.GetType().Name}");
|
||||
OnPrePacketSend(outPacket);
|
||||
SendPacket(outPacket);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
|
||||
@ -6,6 +12,93 @@
|
||||
/// </summary>
|
||||
internal abstract class Packet
|
||||
{
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if DEBUG
|
||||
string dump(object obj, int indentLevel)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string indent = new string(' ', indentLevel * 4);
|
||||
switch (obj)
|
||||
{
|
||||
case null:
|
||||
sb.Append(indent);
|
||||
sb.Append("null");
|
||||
break;
|
||||
case String stringValue:
|
||||
sb.Append(indent);
|
||||
sb.Append('"');
|
||||
sb.Append(stringValue);
|
||||
sb.Append('"');
|
||||
break;
|
||||
case Byte[] byteArrayValue:
|
||||
sb.Append(indent);
|
||||
sb.Append(String.Join(" ", byteArrayValue.Select(x => x.ToString("X2"))));
|
||||
break;
|
||||
case IEnumerable iEnumerableValue:
|
||||
sb.Append(indent);
|
||||
sb.Append("[ ");
|
||||
foreach (object element in iEnumerableValue)
|
||||
sb.Append(dump(element, indentLevel)).Append(" ");
|
||||
sb.Append("]");
|
||||
break;
|
||||
case Byte byteValue:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + byteValue.ToString("X2"));
|
||||
break;
|
||||
case Int16 int16Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + int16Value.ToString("X4"));
|
||||
break;
|
||||
case Int32 int32Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + int32Value.ToString("X8"));
|
||||
break;
|
||||
case Int64 int64Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + int64Value.ToString("X16"));
|
||||
break;
|
||||
case UInt16 uint16Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + uint16Value.ToString("X4"));
|
||||
break;
|
||||
case UInt32 uint32Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + uint32Value.ToString("X8"));
|
||||
break;
|
||||
case UInt64 uint64Value:
|
||||
sb.Append(indent);
|
||||
sb.Append("0x" + uint64Value.ToString("X16"));
|
||||
break;
|
||||
case Enum enumValue:
|
||||
sb.Append(indent);
|
||||
sb.Append(enumValue.ToString());
|
||||
break;
|
||||
default:
|
||||
foreach (PropertyInfo property in obj.GetType().GetProperties(
|
||||
BindingFlags.Instance | BindingFlags.NonPublic))
|
||||
{
|
||||
// Ignore indexers.
|
||||
if (property.GetIndexParameters().Length > 0)
|
||||
continue;
|
||||
sb.AppendLine();
|
||||
sb.Append((indent + property.Name).PadRight(20));
|
||||
sb.Append(" ");
|
||||
sb.Append(dump(property.GetValue(obj), indentLevel + 1));
|
||||
}
|
||||
sb.AppendLine();
|
||||
break;
|
||||
}
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
return String.Concat(GetType().Name, dump(this, 1));
|
||||
#else
|
||||
return GetType().Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal abstract void Deserialize(PacketStream stream);
|
||||
|
@ -2,11 +2,19 @@
|
||||
|
||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorates a <see cref="Packet"/> child class with the ID of the packet it represents.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
internal class PacketAttribute : Attribute
|
||||
{
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PacketAttribute"/> class, decorating a class with the ID of the
|
||||
/// packet it represents.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the packet which the decorated class represents.</param>
|
||||
public PacketAttribute(ushort id)
|
||||
{
|
||||
ID = id;
|
||||
@ -14,6 +22,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the packet the decorated class represents.
|
||||
/// </summary>
|
||||
public ushort ID { get; }
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly Dictionary<ushort, Type> _packetTypeMap = new Dictionary<ushort, Type>();
|
||||
private static readonly Dictionary<ushort, Type> _packetTypes = new Dictionary<ushort, Type>();
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
@ -23,9 +23,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
foreach (PacketAttribute packetAttribute in type.GetCustomAttributes<PacketAttribute>())
|
||||
{
|
||||
if (_packetTypeMap.ContainsKey(packetAttribute.ID))
|
||||
if (_packetTypes.ContainsKey(packetAttribute.ID))
|
||||
throw new InvalidOperationException($"Packet {packetAttribute.ID} mapped to multiple classes.");
|
||||
_packetTypeMap.Add(packetAttribute.ID, type);
|
||||
_packetTypes.Add(packetAttribute.ID, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,27 +41,27 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
internal static Packet Create(ushort id)
|
||||
{
|
||||
#if DEBUG
|
||||
return _packetTypeMap.TryGetValue(id, out Type type)
|
||||
return _packetTypes.TryGetValue(id, out Type type)
|
||||
? (Packet)Activator.CreateInstance(type, true)
|
||||
: new RawQueryPacket(id);
|
||||
#else
|
||||
return (Packet)Activator.CreateInstance(_packetTypeMap[id], true);
|
||||
return (Packet)Activator.CreateInstance(_packetTypes[id], true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for the class of the given <paramref name="packet"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="Packet"/>.</typeparam>
|
||||
/// <param name="packet">The packet, whose class ID will be returned.</param>
|
||||
/// <returns>The ID of the <see cref="Packet"/> class.</returns>
|
||||
internal static ushort GetID<T>(T packet) where T : Packet
|
||||
internal static ushort GetID(Packet packet)
|
||||
{
|
||||
#if DEBUG
|
||||
if (packet is RawQueryPacket rawPacket)
|
||||
return rawPacket.ID;
|
||||
else
|
||||
#endif
|
||||
return _packetTypeMap.Where(x => x.Value == packet.GetType()).First().Key;
|
||||
return _packetTypes.Where(x => x.Value == packet.GetType()).First().Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,17 +90,12 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
|
||||
internal string ReadString()
|
||||
{
|
||||
// Strings are word prefixed and 0 termianted.
|
||||
string value = _baseStream.ReadString(StringCoding.Int16CharCount);
|
||||
_baseStream.Seek(1);
|
||||
return value;
|
||||
return _baseStream.ReadString(StringCoding.Int16CharCount);
|
||||
}
|
||||
|
||||
internal void WriteString(string value)
|
||||
{
|
||||
// Strings are word prefixed and 0 termianted.
|
||||
_baseStream.WriteString(value, StringCoding.Int16CharCount, _win949Encoding);
|
||||
_baseStream.WriteByte(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
@ -10,9 +11,11 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
public string Unknown { get; set; }
|
||||
internal string Unknown { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
internal string Unknown2 { get; set; }
|
||||
|
||||
internal ushort Version { get; set; }
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
@ -21,7 +24,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
internal override void Serialize(PacketStream stream)
|
||||
{
|
||||
stream.WriteString(Unknown);
|
||||
stream.WriteString(Version);
|
||||
stream.WriteByte(0);
|
||||
stream.WriteString(Unknown2);
|
||||
stream.WriteUInt16(Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
#if DEBUG
|
||||
|
||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
@ -10,12 +9,20 @@ namespace Syroot.Worms.OnlineWorms.Server
|
||||
/// Represents a server listening for incoming client connections and dispatching them into <see cref="Client"/>
|
||||
/// instances.
|
||||
/// </summary>
|
||||
public class Server
|
||||
internal class Server
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private readonly List<Client> _clients = new List<Client>();
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
internal string Name => "Online Worms Private Server";
|
||||
internal string RegionName => "Global";
|
||||
internal ushort Version => 114;
|
||||
|
||||
internal Log Log { get; } = new Log();
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
@ -27,16 +34,20 @@ namespace Syroot.Worms.OnlineWorms.Server
|
||||
{
|
||||
TcpListener tcpListener = new TcpListener(IPAddress.Any, port);
|
||||
tcpListener.Start();
|
||||
Console.WriteLine($"Listening on port {port}...");
|
||||
Log.Write(LogCategory.Server, $"Listening on port {port}...");
|
||||
|
||||
// Continually accept clients and dispatch them to their listening thread.
|
||||
while (true)
|
||||
{
|
||||
Client client = new Client(tcpListener.AcceptTcpClient(), this);
|
||||
// Continually accept clients.
|
||||
TcpClient tcpClient = tcpListener.AcceptTcpClient();
|
||||
Log.Write(LogCategory.Connect, $"{tcpClient.Client.RemoteEndPoint} connected");
|
||||
Client client = new Client(tcpClient, this);
|
||||
_clients.Add(client);
|
||||
|
||||
// Dispatch the client into its listening thread and remove it when listening aborts.
|
||||
Task.Run(client.Listen).ContinueWith(_ =>
|
||||
{
|
||||
Log.Write(LogCategory.Disconnect, $"{client.TcpClient.Client.RemoteEndPoint} disconnected");
|
||||
_clients.Remove(client);
|
||||
client.Dispose();
|
||||
});
|
||||
|
@ -4,9 +4,6 @@
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Syroot.BinaryData" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user