diff --git a/src/Syroot.OnlineWorms.Server/Client.cs b/src/Syroot.OnlineWorms.Server/Client.cs
index 7164de3..95fac0d 100644
--- a/src/Syroot.OnlineWorms.Server/Client.cs
+++ b/src/Syroot.OnlineWorms.Server/Client.cs
@@ -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
};
}
diff --git a/src/Syroot.OnlineWorms.Server/Log.cs b/src/Syroot.OnlineWorms.Server/Log.cs
new file mode 100644
index 0000000..3e28946
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Log.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace Syroot.Worms.OnlineWorms.Server
+{
+ ///
+ /// Represents simplistic textual logging.
+ ///
+ 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
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs b/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs
index ef96c4f..09886f1 100644
--- a/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs
@@ -23,7 +23,6 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
= new Dictionary>();
private readonly Dictionary _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 class, handling the given
/// .
///
- /// The to communicate with.
+ /// The to communicate with.
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 ---------------------------------------------------------------------------------------------
+
+ ///
+ /// Gets or sets the with which the connection communicates.
+ ///
+ 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 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);
}
}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packet.cs b/src/Syroot.OnlineWorms.Server/Net/Packet.cs
index ad648d1..d28c03b 100644
--- a/src/Syroot.OnlineWorms.Server/Net/Packet.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/Packet.cs
@@ -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
{
///
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
@@ -6,6 +12,93 @@
///
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);
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs b/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs
index 04c423c..0785227 100644
--- a/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs
@@ -2,11 +2,19 @@
namespace Syroot.Worms.OnlineWorms.Server.Net
{
+ ///
+ /// Decorates a child class with the ID of the packet it represents.
+ ///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
internal class PacketAttribute : Attribute
{
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+ ///
+ /// Initializes a new instance of the class, decorating a class with the ID of the
+ /// packet it represents.
+ ///
+ /// The ID of the packet which the decorated class represents.
public PacketAttribute(ushort id)
{
ID = id;
@@ -14,6 +22,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
+ ///
+ /// Gets the ID of the packet the decorated class represents.
+ ///
public ushort ID { get; }
}
}
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs b/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs
index 9afd7e1..bb6d3ce 100644
--- a/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs
@@ -12,7 +12,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
- private static readonly Dictionary _packetTypeMap = new Dictionary();
+ private static readonly Dictionary _packetTypes = new Dictionary();
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@@ -23,9 +23,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
{
foreach (PacketAttribute packetAttribute in type.GetCustomAttributes())
{
- 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
}
///
/// Gets the ID for the class of the given .
///
- /// The type of the .
/// The packet, whose class ID will be returned.
/// The ID of the class.
- internal static ushort GetID(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;
}
}
}
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketStream.cs b/src/Syroot.OnlineWorms.Server/Net/PacketStream.cs
index fd7e0d7..a2a23d6 100644
--- a/src/Syroot.OnlineWorms.Server/Net/PacketStream.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketStream.cs
@@ -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);
}
}
}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs
index 066d6a4..7fe33a4 100644
--- a/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs
@@ -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);
}
}
}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs
index f3081bf..4ddee44 100644
--- a/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs
@@ -1,5 +1,4 @@
#if DEBUG
-
namespace Syroot.Worms.OnlineWorms.Server.Net
{
///
diff --git a/src/Syroot.OnlineWorms.Server/Server.cs b/src/Syroot.OnlineWorms.Server/Server.cs
index 5e70c17..2cfaf81 100644
--- a/src/Syroot.OnlineWorms.Server/Server.cs
+++ b/src/Syroot.OnlineWorms.Server/Server.cs
@@ -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
/// instances.
///
- public class Server
+ internal class Server
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private readonly List _clients = new List();
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ internal string Name => "Online Worms Private Server";
+ internal string RegionName => "Global";
+ internal ushort Version => 114;
+
+ internal Log Log { get; } = new Log();
+
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
///
@@ -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();
});
diff --git a/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj b/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj
index 3129073..0d8db27 100644
--- a/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj
+++ b/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj
@@ -4,9 +4,6 @@
netcoreapp2.1
latest
-
- DEBUG;TRACE
-