mirror of https://github.com/icedream/npsharp.git
Remove NP server code from file tree.
parent
89d9a3f2c1
commit
58f34660d7
|
@ -18,8 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{F8
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPSharp.Client", "src\client\NPSharp.Client.csproj", "{C6F941A5-82AF-456A-9B3A-752E5B001035}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPSharp.Server", "src\server\NPSharp.Server.csproj", "{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using uhttpsharp;
|
||||
using uhttpsharp.Handlers;
|
||||
using uhttpsharp.Headers;
|
||||
using uhttpsharp.Listeners;
|
||||
using uhttpsharp.RequestProviders;
|
||||
|
||||
namespace NPSharp.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a session authentication server which uses the HTTP protocol to send out session tokens to
|
||||
/// authenticating NP clients.
|
||||
/// </summary>
|
||||
public class SessionAuthenticationServer
|
||||
{
|
||||
private readonly ILog _log;
|
||||
private HttpServer _http;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new session authentication server.
|
||||
/// </summary>
|
||||
public SessionAuthenticationServer()
|
||||
{
|
||||
SupportOldAuthentication = true;
|
||||
_log = LogManager.GetLogger("Auth");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Support oldskool "user&&pass" authentication format.
|
||||
/// </summary>
|
||||
public bool SupportOldAuthentication { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Will be triggered whenever a client tries to authenticate via this server.
|
||||
/// </summary>
|
||||
public event Func<string, string, SessionAuthenticationResult> Authenticating;
|
||||
|
||||
protected virtual SessionAuthenticationResult OnAuthenticating(string username, string password)
|
||||
{
|
||||
var handler = Authenticating;
|
||||
return handler != null
|
||||
? handler(username, password)
|
||||
: new SessionAuthenticationResult {Reason = "Login currently disabled"};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the authentication server.
|
||||
/// </summary>
|
||||
/// <param name="port">The port on which the authentication server should listen on.</param>
|
||||
public void Start(ushort port = 12003)
|
||||
{
|
||||
if (_http != null)
|
||||
{
|
||||
throw new InvalidOperationException("This server is already running");
|
||||
}
|
||||
|
||||
_log.Debug("Starting authentication server...");
|
||||
_http = new HttpServer(new HttpRequestProvider());
|
||||
_http.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Any, port)));
|
||||
_http.Use(new TcpListenerAdapter(new TcpListener(IPAddress.IPv6Any, port)));
|
||||
_http.Use(new HttpRouter().With("authenticate", new AuthenticateHandler(this)));
|
||||
_http.Use(new AnonymousHttpRequestHandler((ctx, task) =>
|
||||
{
|
||||
ctx.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "Not found",
|
||||
ctx.Request.Headers.KeepAliveConnection());
|
||||
return Task.Factory.GetCompleted();
|
||||
}));
|
||||
_http.Start();
|
||||
_log.Debug("Done starting authentication server.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the authentication server.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_http.Dispose();
|
||||
}
|
||||
|
||||
protected class AuthenticateHandler : IHttpRequestHandler
|
||||
{
|
||||
private readonly SessionAuthenticationServer _authServer;
|
||||
|
||||
public AuthenticateHandler(SessionAuthenticationServer sessionAuthenticationServer)
|
||||
{
|
||||
_authServer = sessionAuthenticationServer;
|
||||
}
|
||||
|
||||
public Task Handle(IHttpContext context, Func<Task> next)
|
||||
{
|
||||
SessionAuthenticationResult sar;
|
||||
|
||||
string username;
|
||||
string password;
|
||||
|
||||
if (!context.Request.Post.Parsed.TryGetByName("user", out username) ||
|
||||
!context.Request.Post.Parsed.TryGetByName("pass", out password))
|
||||
{
|
||||
var login = Encoding.UTF8.GetString(context.Request.Post.Raw)
|
||||
.Split(new[] {"&&"}, StringSplitOptions.None);
|
||||
if (login.Length != 2)
|
||||
{
|
||||
sar = new SessionAuthenticationResult {Reason = @"Invalid login data"};
|
||||
|
||||
context.Response = new HttpResponse(HttpResponseCode.Ok, sar.ToString(),
|
||||
context.Request.Headers.KeepAliveConnection());
|
||||
return Task.Factory.GetCompleted();
|
||||
}
|
||||
|
||||
username = login[0];
|
||||
password = login[1];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
sar = _authServer.OnAuthenticating(username, password);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_authServer._log.Error(@"Authentication handler error", error);
|
||||
sar = new SessionAuthenticationResult {Reason = @"Internal server error"};
|
||||
}
|
||||
|
||||
_authServer._log.DebugFormat("/authenticate reply is {0}", sar);
|
||||
|
||||
context.Response = new HttpResponse(HttpResponseCode.Ok, sar.ToString(),
|
||||
!sar.Success && context.Request.Headers.KeepAliveConnection());
|
||||
return Task.Factory.GetCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
using System;
|
||||
using NPSharp.NP;
|
||||
|
||||
namespace NPSharp.Events
|
||||
{
|
||||
public class ClientEventArgs : EventArgs
|
||||
{
|
||||
internal ClientEventArgs(NPServerClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public NPServerClient Client { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using NPSharp.NP;
|
||||
|
||||
namespace NPSharp.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate for all general client-related events.
|
||||
/// </summary>
|
||||
/// <param name="sender">The instance of the NP server</param>
|
||||
/// <param name="args">All related arguments to this event</param>
|
||||
public delegate void ClientEventHandler(NPServer sender, ClientEventArgs args);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
using NPSharp.NP;
|
||||
using NPSharp.RPC.Messages.Data;
|
||||
|
||||
namespace NPSharp.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler for all authentication-related requests.
|
||||
/// </summary>
|
||||
public interface IAuthenticationHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Authenticates a user based on username and password.
|
||||
/// </summary>
|
||||
/// <param name="client">The NP server client to authenticate</param>
|
||||
/// <param name="username">The username to use for authentication</param>
|
||||
/// <param name="password">The password to use for authentication</param>
|
||||
/// <returns>An instance of <seealso cref="NPAuthenticationResult" /></returns>
|
||||
NPAuthenticationResult AuthenticateUser(NPServerClient client, string username, string password);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a user based on a session token.
|
||||
/// </summary>
|
||||
/// <param name="client">The NP server client to authenticate</param>
|
||||
/// <param name="token">The session token to use for authentication</param>
|
||||
/// <returns>An instance of <seealso cref="NPAuthenticationResult" /></returns>
|
||||
NPAuthenticationResult AuthenticateUser(NPServerClient client, string token);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a dedicated server based on its license key.
|
||||
/// </summary>
|
||||
/// <param name="client">The NP server client of the dedicated server to authenticate</param>
|
||||
/// <param name="licenseKey">The license key to use for authentication</param>
|
||||
/// <returns>An instance of <see cref="NPAuthenticationResult" /></returns>
|
||||
NPAuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey);
|
||||
|
||||
/// <summary>
|
||||
/// Validates a ticket.
|
||||
/// </summary>
|
||||
/// <param name="client">The NP server client of the user who is trying to get the ticket validated</param>
|
||||
/// <param name="server">The server that the user wants to connect to using this ticket</param>
|
||||
/// <returns>A <see cref="TicketValidationResult" /> determining if the ticket is valid</returns>
|
||||
TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
using NPSharp.NP;
|
||||
|
||||
namespace NPSharp.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler for all file-related requests.
|
||||
/// </summary>
|
||||
public interface IFileServingHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a file assigned to the user.
|
||||
/// </summary>
|
||||
/// <returns>The file contents as byte array or null if the file could not be read, opened or generated</returns>
|
||||
/// <param name="client">NP server client of the user</param>
|
||||
/// <param name="file">The file name</param>
|
||||
byte[] ReadUserFile(NPServerClient client, string file);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a publisher file.
|
||||
/// </summary>
|
||||
/// <returns>The file contents as byte array or null if the file could not be read, opened or generated</returns>
|
||||
/// <param name="client">NP server client of the user</param>
|
||||
/// <param name="file">The file name</param>
|
||||
byte[] ReadPublisherFile(NPServerClient client, string file);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a file and assigns it to the client user.
|
||||
/// </summary>
|
||||
/// <param name="client">NP server client of the user</param>
|
||||
/// <param name="file">The file name</param>
|
||||
/// <param name="data">The file contents as byte array</param>
|
||||
void WriteUserFile(NPServerClient client, string file, byte[] data);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using NPSharp.NP;
|
||||
using NPSharp.RPC.Messages.Data;
|
||||
|
||||
namespace NPSharp.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler for all friends-related requests.
|
||||
/// </summary>
|
||||
public interface IFriendsHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Fetches all friends of the connected user.
|
||||
/// </summary>
|
||||
/// <param name="client">The NP server client of the user</param>
|
||||
/// <returns>All friend details found for the user</returns>
|
||||
IEnumerable<FriendDetails> GetFriends(NPServerClient client);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler for all user avatar-related requests.
|
||||
/// </summary>
|
||||
public interface IUserAvatarHandler
|
||||
{
|
||||
byte[] GetUserAvatar(CSteamID id);
|
||||
|
||||
byte[] GetDefaultAvatar();
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using NPSharp.Master.Messages;
|
||||
using NPSharp.Master.Messages.Client;
|
||||
|
||||
namespace NPSharp.Master
|
||||
{
|
||||
public class MasterServer
|
||||
{
|
||||
// TODO: !! Avoid socket fail if stopping then restarting
|
||||
|
||||
private readonly List<KeyValuePair<Type, Action<MasterClientMessage>>> _callbacks =
|
||||
new List<KeyValuePair<Type, Action<MasterClientMessage>>>();
|
||||
|
||||
private readonly ILog _log;
|
||||
private readonly ushort _port;
|
||||
|
||||
// TODO: Use the same kind of interfaces as in NP server to handle server addition and deletion
|
||||
private readonly List<DedicatedServerEntry> _registeredServers = new List<DedicatedServerEntry>();
|
||||
private readonly Socket _socket4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
private readonly Socket _socket6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
|
||||
public MasterServer(ushort port = 20810)
|
||||
{
|
||||
_port = port;
|
||||
_log = LogManager.GetLogger("MasterServer");
|
||||
|
||||
// Internal callbacks
|
||||
AddCallback<MasterGetServersMessage>(messages => { });
|
||||
}
|
||||
|
||||
internal void AddCallback<T>(Action<T> callback) where T : MasterClientMessage
|
||||
{
|
||||
_callbacks.Add(
|
||||
new KeyValuePair<Type, Action<MasterClientMessage>>(
|
||||
typeof (T),
|
||||
msg => callback.Invoke((T) msg)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts up the NP server.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (_socket4.IsBound || _socket6.IsBound)
|
||||
throw new InvalidOperationException("This server is already running");
|
||||
|
||||
try
|
||||
{
|
||||
// ReSharper disable once ObjectCreationAsStatement
|
||||
// TODO: fix this shit permission code
|
||||
new SocketPermission(NetworkAccess.Accept, TransportType.Udp, "", _port);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Error("Socket permission request failed, can't start server.");
|
||||
throw new SocketException(10013 /* Permission denied */);
|
||||
}
|
||||
|
||||
_socket4.Bind(new IPEndPoint(IPAddress.Any, _port));
|
||||
_socket4.Listen(100);
|
||||
|
||||
_socket6.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
|
||||
_socket6.Listen(100);
|
||||
|
||||
|
||||
// TODO: Implement IPv4 handling
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
_log.Debug("Listener loop (IPv6) started");
|
||||
|
||||
while (_socket6 != null && _socket6.IsBound)
|
||||
{
|
||||
var mergedBuffer = new List<byte>();
|
||||
while (true)
|
||||
{
|
||||
var buffer = new byte[1400];
|
||||
var clientEndPoint = (EndPoint) new IPEndPoint(IPAddress.IPv6Any, 0);
|
||||
var recvLength = _socket6.ReceiveFrom(buffer, ref clientEndPoint);
|
||||
if (recvLength <= buffer.Length)
|
||||
mergedBuffer.AddRange(buffer);
|
||||
if (recvLength < 1400)
|
||||
break;
|
||||
_handleClient(buffer, clientEndPoint);
|
||||
}
|
||||
}
|
||||
_log.Debug("Listener loop (IPv6) shut down");
|
||||
});
|
||||
}
|
||||
|
||||
private void _handleClient(byte[] buffer, EndPoint ep)
|
||||
{
|
||||
_log.DebugFormat("Handle client {0}", ep);
|
||||
|
||||
var message = MasterClientMessage.Deserialize(buffer);
|
||||
if (message == null)
|
||||
{
|
||||
_log.WarnFormat("Received invalid or empty request from {0}", ep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke (internal) callbacks for fitting message types
|
||||
foreach (var callback in _callbacks.Where(i => i.Key == message.GetType()).Select(i => i.Value))
|
||||
{
|
||||
callback.Invoke(message);
|
||||
}
|
||||
|
||||
_log.DebugFormat("Not handling client {0} anymore", ep);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
# Master API source code
|
||||
|
||||
This folder is supposed to contain the source code that will provide both, a master server and client.
|
||||
|
||||
alterIWnet and fourdeltaone made use of the open-sourced DP master server code to serve registration of dedicated servers
|
||||
and the dedicated server list for the game client.
|
||||
|
||||
This server is supposed to be replaced by a full implementation of the protocol, both as server and client, in C# and is
|
||||
to be integrated into libnpsharp.
|
||||
|
||||
## Protocol
|
||||
|
||||
The master server protocol is based on Quake 3's (http://src.gnu-darwin.org/ports/games/masterserver/work/masterserver-0.4.1/docs/PROTOCOLS):
|
||||
|
||||
Request message:
|
||||
AA AA AA AA|BB .. .. BB| A=Header, B=Body ending with line break or \x00
|
||||
|
||||
Response message:
|
||||
AA .. .. AA|0a|BB .. .. A=Message name, B=Body
|
||||
BB
|
||||
|
||||
Important: The response is supposed to be one or multiple specific-length plus a less-than-specific-length
|
||||
UDP packet(s).
|
||||
|
||||
TODO: Look up the specific length in the DP master source code. It's somewhere around 1400/1500 bytes.
|
||||
|
||||
The master server itself has to only serve for the "getservers" request and has to respond with a getServersResponse message.
|
||||
This getServersResponse message's body is serialized as following:
|
||||
|
||||
XX AA AA AA AA BB BB[CC X=ascii("\\"), A=IP (uint, network byte order), B=Port 1 (ushort, network byte order), C=Optional port 2 (ushort, network byte order)
|
||||
CC[..] ]XX .. .. .. ..
|
||||
XX YY YY YY 00 00 00[00 Y=ascii("EOT")
|
||||
00]
|
||||
|
||||
In short, serialized ip-port pairs/triples separated by ASCII backslashes and ended by an entry which decodes to "EOT".
|
||||
|
||||
## Protocol limits
|
||||
|
||||
(see https://github.com/kphillisjr/dpmaster/blob/master/src/messages.c for reference)
|
||||
|
||||
```c
|
||||
// Timeout after a valid infoResponse (in secondes)
|
||||
#define TIMEOUT_INFORESPONSE (15 * 60)
|
||||
|
||||
// Period of validity for a challenge string (in secondes)
|
||||
#define TIMEOUT_CHALLENGE 2
|
||||
|
||||
// Maximum size of a reponse packet
|
||||
#define MAX_PACKET_SIZE_OUT 1400
|
||||
```
|
||||
|
||||
## Internal management
|
||||
|
||||
Both the server and the client should be able to handle a variable amount of ports. For this, we plan following things:
|
||||
|
||||
1) Assume "\" at the beginning of an entry. If not true, stream out of sync -> throw exception/disconnect.
|
||||
2) Assume an IP address exists (4 bytes minimum).
|
||||
a) If IP address is ASCII "EOT" with \x00 bytes at the end, stop reading the message.
|
||||
3) Do not assume a fixed amount of ports. Instead
|
||||
a) ...if rest > 0 bytes, read 2 bytes and append to a ushort list for ports.
|
||||
b) ...if rest == 0 bytes, prepare for reading next entry.
|
||||
|
||||
This should be compatible with both, messages with 1 IP address + 1 port (game) and 1 IP address + 2 ports (game/query).
|
||||
|
||||
## Messages
|
||||
|
||||
(see https://github.com/kphillisjr/dpmaster/blob/master/src/messages.c for reference)
|
||||
|
||||
// Types of messages (with samples):
|
||||
|
||||
// Q3: "heartbeat QuakeArena-1\x0A"
|
||||
// DP: "heartbeat DarkPlaces\x0A"
|
||||
#define S2M_HEARTBEAT "heartbeat "
|
||||
|
||||
// Q3 & DP & QFusion: "getinfo A_Challenge"
|
||||
#define M2S_GETINFO "getinfo"
|
||||
|
||||
// Q3 & DP & QFusion: "infoResponse\x0A\\pure\\1\\..."
|
||||
#define S2M_INFORESPONSE "infoResponse\x0A"
|
||||
|
||||
// Q3: "getservers 67 ffa empty full"
|
||||
// DP: "getservers DarkPlaces-Quake 3 empty full"
|
||||
// DP: "getservers Transfusion 3 empty full"
|
||||
// QFusion: "getservers qfusion 39 empty full"
|
||||
#define C2M_GETSERVERS "getservers "
|
||||
|
||||
// DP: "getserversExt DarkPlaces-Quake 3 empty full ipv4 ipv6"
|
||||
// IOQuake3: "getserversExt 68 empty ipv6"
|
||||
#define C2M_GETSERVERSEXT "getserversExt "
|
||||
|
||||
// Q3 & DP & QFusion:
|
||||
// "getserversResponse\\...(6 bytes)...\\...(6 bytes)...\\EOT\0\0\0"
|
||||
#define M2C_GETSERVERSREPONSE "getserversResponse"
|
||||
|
||||
// DP & IOQuake3:
|
||||
// "getserversExtResponse\\...(6 bytes)...//...(18 bytes)...\\EOT\0\0\0"
|
||||
#define M2C_GETSERVERSEXTREPONSE "getserversExtResponse"
|
|
@ -1,732 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using NPSharp.Events;
|
||||
using NPSharp.Handlers;
|
||||
using NPSharp.RPC;
|
||||
using NPSharp.RPC.Messages.Client;
|
||||
using NPSharp.RPC.Messages.Data;
|
||||
using NPSharp.RPC.Messages.Server;
|
||||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp.NP
|
||||
{
|
||||
public class NPServer
|
||||
{
|
||||
private readonly List<NPServerClient> _clients;
|
||||
private readonly ILog _log;
|
||||
private readonly ushort _port;
|
||||
#if MONO_INCOMPATIBLE
|
||||
private readonly Socket _socket;
|
||||
#else
|
||||
private readonly Socket _socket4;
|
||||
private readonly Socket _socket6;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new NP server.
|
||||
/// </summary>
|
||||
public NPServer(ushort port = 3025)
|
||||
{
|
||||
_log = LogManager.GetLogger("NPServer");
|
||||
_clients = new List<NPServerClient>();
|
||||
|
||||
#if MONO_INCOMPATIBLE
|
||||
// Mono can't compile this since the constructor is proprietary to Windows' .NET library
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
||||
|
||||
// Mono can't compile this either since the IPv6Only socket option is completely missing.
|
||||
//_socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
||||
//_socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
|
||||
#else
|
||||
// So as much as this hurts me, I have to go with TWO sockets.
|
||||
// Guys, this is why I hate network programming sometimes. SOMETIMES.
|
||||
_socket4 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
||||
#endif
|
||||
_port = port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handler to use for file requests to this NP server.
|
||||
/// </summary>
|
||||
public IFileServingHandler FileServingHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler to use for user avatar requests to this NP server.
|
||||
/// </summary>
|
||||
public IUserAvatarHandler UserAvatarHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all currently connected clients
|
||||
/// </summary>
|
||||
public NPServerClient[] Clients
|
||||
{
|
||||
get { return _clients.ToArray(); } // Avoid race condition by IEnum changes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handler to use for authentication requests to this NP server.
|
||||
/// </summary>
|
||||
public IAuthenticationHandler AuthenticationHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler to use for friends-related requests to this NP server.
|
||||
/// </summary>
|
||||
public IFriendsHandler FriendsHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts up the NP server.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (
|
||||
#if MONO_INCOMPATIBLE
|
||||
_socket.IsBound
|
||||
#else
|
||||
_socket4.IsBound || _socket6.IsBound
|
||||
#endif
|
||||
)
|
||||
throw new InvalidOperationException("This server is already running");
|
||||
|
||||
try
|
||||
{
|
||||
// ReSharper disable once ObjectCreationAsStatement
|
||||
// TODO: fix this shit permission code
|
||||
new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, "", _port);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Error("Socket permission request failed, can't start server.");
|
||||
throw new SocketException(10013 /* Permission denied */);
|
||||
}
|
||||
|
||||
#if MONO_INCOMPATIBLE
|
||||
_socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
|
||||
_socket.Listen(100);
|
||||
#else
|
||||
_socket4.Bind(new IPEndPoint(IPAddress.Any, _port));
|
||||
_socket4.Listen(100);
|
||||
|
||||
_socket6.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
|
||||
_socket6.Listen(100);
|
||||
#endif
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
_log.Debug("Listener loop started");
|
||||
#if MONO_INCOMPATIBLE
|
||||
var socket = _socket;
|
||||
#else
|
||||
var socket = _socket4;
|
||||
#endif
|
||||
var allDone = new ManualResetEvent(false);
|
||||
while (
|
||||
#if MONO_INCOMPATIBLE
|
||||
_socket != null && _socket.IsBound
|
||||
#else
|
||||
_socket4 != null && _socket4.IsBound
|
||||
#endif
|
||||
)
|
||||
{
|
||||
allDone.Reset();
|
||||
socket.BeginAccept(ar =>
|
||||
{
|
||||
_log.Debug("Async accept client start");
|
||||
allDone.Set();
|
||||
|
||||
var serverSocket = (Socket) ar.AsyncState;
|
||||
var clientSocket = serverSocket.EndAccept(ar);
|
||||
|
||||
var npsc = new NPServerClient(this, new RPCServerStream(clientSocket));
|
||||
|
||||
_log.Debug("Async accept client end");
|
||||
|
||||
_handleClient(npsc);
|
||||
}, socket);
|
||||
allDone.WaitOne();
|
||||
}
|
||||
_log.Debug("Listener loop shut down");
|
||||
});
|
||||
|
||||
#if !MONO_INCOMPATIBLE
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
_log.Debug("Listener loop (IPv6) started");
|
||||
|
||||
var allDone = new ManualResetEvent(false);
|
||||
while (_socket6 != null && _socket6.IsBound)
|
||||
{
|
||||
allDone.Reset();
|
||||
_socket6.BeginAccept(ar =>
|
||||
{
|
||||
_log.Debug("Async accept (IPv6) client start");
|
||||
allDone.Set();
|
||||
|
||||
var serverSocket = (Socket) ar.AsyncState;
|
||||
var clientSocket = serverSocket.EndAccept(ar);
|
||||
|
||||
var npsc = new NPServerClient(this, new RPCServerStream(clientSocket));
|
||||
|
||||
_log.Debug("Async accept (IPv6) client end");
|
||||
|
||||
_handleClient(npsc);
|
||||
}, _socket6);
|
||||
allDone.WaitOne();
|
||||
}
|
||||
_log.Debug("Listener loop (IPv6) shut down");
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down all connections and stops the NP server.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
// TODO: Wait for sockets to COMPLETELY shut down
|
||||
#if MONO_INCOMPATIBLE
|
||||
_socket.Shutdown(SocketShutdown.Both);
|
||||
#else
|
||||
_socket4.Shutdown(SocketShutdown.Both);
|
||||
_socket6.Shutdown(SocketShutdown.Both);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void _handleClient(NPServerClient client)
|
||||
{
|
||||
_log.Debug("Client now being handled");
|
||||
|
||||
#region RPC authentication message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<AuthenticateWithKeyMessage>(msg =>
|
||||
{
|
||||
var result = new NPAuthenticationResult();
|
||||
if (AuthenticationHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = AuthenticationHandler.AuthenticateServer(client, msg.LicenseKey);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Error("Error occurred in authentication handler", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Send authentication result directly to client
|
||||
client.RPC.Send(new AuthenticateResultMessage
|
||||
{
|
||||
NPID = result.UserID,
|
||||
Result = result.Result ? 0 : 1,
|
||||
SessionToken = string.Empty
|
||||
});
|
||||
|
||||
// Authentication failed => close connection
|
||||
if (!result.Result)
|
||||
{
|
||||
client.RPC.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign login ID
|
||||
client.UserID = result.UserID;
|
||||
client.IsServer = true;
|
||||
|
||||
OnClientAuthenticated(client);
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<AuthenticateWithDetailsMessage>(msg =>
|
||||
{
|
||||
var result = new NPAuthenticationResult();
|
||||
if (AuthenticationHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = AuthenticationHandler.AuthenticateUser(client, msg.Username, msg.Password);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Error("Error occurred in authentication handler", error);
|
||||
result = new NPAuthenticationResult();
|
||||
}
|
||||
}
|
||||
|
||||
// Send authentication result directly to client
|
||||
client.RPC.Send(new AuthenticateResultMessage
|
||||
{
|
||||
NPID = result.UserID,
|
||||
Result = result.Result ? 0 : 1,
|
||||
SessionToken = string.Empty
|
||||
});
|
||||
|
||||
// Authentication failed => close connection
|
||||
if (!result.Result)
|
||||
{
|
||||
client.RPC.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign login ID
|
||||
client.UserID = result.UserID;
|
||||
|
||||
// Send "online" notification to all friends of this player
|
||||
foreach (var fconn in client.FriendConnections)
|
||||
{
|
||||
fconn.RPC.Send(new FriendsPresenceMessage
|
||||
{
|
||||
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID,
|
||||
Friend = client.UserID,
|
||||
Presence = client.PresenceData,
|
||||
PresenceState = client.DedicatedServer == null ? 1 : 2
|
||||
});
|
||||
}
|
||||
|
||||
// Send friends roster to player
|
||||
client.RPC.Send(new FriendsRosterMessage
|
||||
{
|
||||
Friends = client.Friends.ToArray()
|
||||
});
|
||||
|
||||
OnClientAuthenticated(client);
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<AuthenticateWithTokenMessage>(msg =>
|
||||
{
|
||||
var result = new NPAuthenticationResult();
|
||||
if (AuthenticationHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = AuthenticationHandler.AuthenticateUser(client, msg.Token);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Error("Error occurred in authentication handler", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Send authentication result directly to client
|
||||
client.RPC.Send(new AuthenticateResultMessage
|
||||
{
|
||||
NPID = result.UserID == null ? 0 : result.UserID.ConvertToUint64(),
|
||||
Result = result.Result ? 0 : 1,
|
||||
SessionToken = msg.Token
|
||||
});
|
||||
|
||||
// Authentication failed => close connection
|
||||
if (!result.Result)
|
||||
{
|
||||
client.RPC.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign login ID
|
||||
client.UserID = result.UserID;
|
||||
|
||||
// Send "online" notification to all friends of this player
|
||||
foreach (var fconn in client.FriendConnections)
|
||||
{
|
||||
fconn.RPC.Send(new FriendsPresenceMessage
|
||||
{
|
||||
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID,
|
||||
Friend = client.UserID,
|
||||
Presence = client.PresenceData,
|
||||
PresenceState = client.DedicatedServer == null ? 1 : 2
|
||||
});
|
||||
}
|
||||
|
||||
// Send friends roster to player
|
||||
client.RPC.Send(new FriendsRosterMessage
|
||||
{
|
||||
Friends = client.Friends.ToArray()
|
||||
});
|
||||
|
||||
OnClientAuthenticated(client);
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<AuthenticateValidateTicketMessage>(msg =>
|
||||
{
|
||||
var validTicket = false;
|
||||
|
||||
if (!client.IsDirty)
|
||||
{
|
||||
Ticket ticketData = null;
|
||||
|
||||
try
|
||||
{
|
||||
ticketData = new Ticket(msg.Ticket);
|
||||
|
||||
_log.DebugFormat("Ticket[Version={0},ServerID={1},Time={2}]", ticketData.Version,
|
||||
ticketData.ServerID, ticketData.Time);
|
||||
}
|
||||
catch (ArgumentException error)
|
||||
{
|
||||
_log.Warn("Got some weird-length ticket data", error);
|
||||
}
|
||||
|
||||
if (ticketData != null)
|
||||
{
|
||||
if (ticketData.Version == 1) // Version 1 enforcement
|
||||
{
|
||||
if (ticketData.ClientID == client.UserID) // NPID enforcement
|
||||
{
|
||||
var s =
|
||||
_clients.Where(c => c.IsServer && !c.IsDirty && c.UserID == ticketData.ServerID)
|
||||
.ToArray();
|
||||
|
||||
if (s.Any())
|
||||
{
|
||||
// TODO: Time validation. Problem is some clocks go wrong by minutes!
|
||||
client.DedicatedServer = s.First();
|
||||
validTicket = true;
|
||||
_log.Debug("Ticket validated");
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Warn("Ticket invalid, could not find any sane servers with requested server ID");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Warn("Ticket invalid, found NPID spoofing attempt");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Warn("Ticket invalid, found invalid version");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Warn("Ticket invalid, client is marked as dirty");
|
||||
}
|
||||
|
||||
// Invalid data buffer
|
||||
client.RPC.Send(new AuthenticateValidateTicketResultMessage
|
||||
{
|
||||
GroupID = client.GroupID,
|
||||
NPID = client.UserID,
|
||||
Result = validTicket ? 0 : 1
|
||||
});
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region RPC friend message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<FriendsSetPresenceMessage>(msg =>
|
||||
{
|
||||
foreach (var pdata in msg.Presence)
|
||||
{
|
||||
client.SetPresence(pdata.Key, pdata.Value);
|
||||
_log.DebugFormat("Client says presence \"{0}\" is \"{1}\"", pdata.Key, pdata.Value);
|
||||
}
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<FriendsGetUserAvatarMessage>(msg =>
|
||||
{
|
||||
// TODO: Not compatible with non-public accounts
|
||||
var npid = new CSteamID((uint) msg.Guid, EUniverse.Public,
|
||||
EAccountType.Individual).ConvertToUint64();
|
||||
|
||||
var avatar = UserAvatarHandler.GetUserAvatar(npid) ?? UserAvatarHandler.GetDefaultAvatar();
|
||||
|
||||
client.RPC.Send(new FriendsGetUserAvatarResultMessage
|
||||
{
|
||||
FileData = avatar,
|
||||
Guid = msg.Guid,
|
||||
Result = avatar != null ? 0 : 1
|
||||
});
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<FriendsGetProfileDataMessage>(msg =>
|
||||
{
|
||||
// TODO
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region RPC storage message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<StorageGetPublisherFileMessage>(msg =>
|
||||
{
|
||||
if (FileServingHandler == null)
|
||||
{
|
||||
client.RPC.Send(new StoragePublisherFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (client.UserID == null)
|
||||
{
|
||||
client.RPC.Send(new StoragePublisherFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
_log.WarnFormat("Client tried to read publisher file {0} while not logged in", msg.FileName);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = FileServingHandler.ReadPublisherFile(client, msg.FileName);
|
||||
if (data == null)
|
||||
{
|
||||
client.RPC.Send(new StoragePublisherFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 1,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
_log.DebugFormat("Could not open publisher file {0}", msg.FileName);
|
||||
return;
|
||||
}
|
||||
|
||||
client.RPC.Send(new StoragePublisherFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 0,
|
||||
FileName = msg.FileName,
|
||||
FileData = data
|
||||
});
|
||||
_log.DebugFormat("Sent publisher file {0}", msg.FileName);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Warn("GetPublisherFile handler error", error);
|
||||
client.RPC.Send(new StoragePublisherFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<StorageGetUserFileMessage>(msg =>
|
||||
{
|
||||
if (FileServingHandler == null)
|
||||
{
|
||||
client.RPC.Send(new StorageUserFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (client.UserID == null)
|
||||
{
|
||||
client.RPC.Send(new StorageUserFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName,
|
||||
NPID = client.UserID
|
||||
});
|
||||
_log.WarnFormat("Client tried to read user file {0} while not logged in", msg.FileName);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = FileServingHandler.ReadUserFile(client, msg.FileName);
|
||||
if (data == null)
|
||||
{
|
||||
client.RPC.Send(new StorageUserFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 1,
|
||||
FileName = msg.FileName,
|
||||
NPID = client.UserID
|
||||
});
|
||||
_log.DebugFormat("Could not open user file {0}", msg.FileName);
|
||||
return;
|
||||
}
|
||||
|
||||
client.RPC.Send(new StorageUserFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 0,
|
||||
FileName = msg.FileName,
|
||||
FileData = data,
|
||||
NPID = client.UserID
|
||||
});
|
||||
_log.DebugFormat("Sent user file {0}", msg.FileName);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Warn("GetUserFile handler error", error);
|
||||
client.RPC.Send(new StorageUserFileMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<StorageSendRandomStringMessage>(msg =>
|
||||
{
|
||||
// TODO: Handle "random string" messages
|
||||
});
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<StorageWriteUserFileMessage>(msg =>
|
||||
{
|
||||
if (FileServingHandler == null)
|
||||
{
|
||||
client.RPC.Send(new StorageWriteUserFileResultMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (client.UserID == null)
|
||||
{
|
||||
client.RPC.Send(new StorageWriteUserFileResultMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName,
|
||||
NPID = client.UserID
|
||||
});
|
||||
_log.WarnFormat("Client tried to write user file {0} while not logged in", msg.FileName);
|
||||
return;
|
||||
}
|
||||
|
||||
FileServingHandler.WriteUserFile(client, msg.FileName, msg.FileData);
|
||||
|
||||
client.RPC.Send(new StorageWriteUserFileResultMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 0,
|
||||
FileName = msg.FileName,
|
||||
NPID = client.UserID
|
||||
});
|
||||
_log.DebugFormat("Received and wrote user file {0}", msg.FileName);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Warn("WriteUserFile handler error", error);
|
||||
client.RPC.Send(new StorageWriteUserFileResultMessage
|
||||
{
|
||||
MessageId = msg.MessageId,
|
||||
Result = 2,
|
||||
FileName = msg.FileName
|
||||
});
|
||||
}
|
||||
});
|
||||
// TODO: RPC message handling for MessagingSendData
|
||||
|
||||
#endregion
|
||||
|
||||
#region RPC server session message handler
|
||||
|
||||
#endregion
|
||||
|
||||
_clients.Add(client);
|
||||
#if !DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
_log.Debug("Client connected");
|
||||
OnClientConnected(client);
|
||||
#if !DEBUG
|
||||
try
|
||||
#endif
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var msg = client.RPC.Read();
|
||||
if (msg == null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if !DEBUG
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Error("Error in RPC read loop", error);
|
||||
}
|
||||
#endif
|
||||
_log.Debug("Client disconnected");
|
||||
OnClientDisconnected(client);
|
||||
#if !DEBUG
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_log.Error("Error in client handling loop", error);
|
||||
client.RPC.Send(new CloseAppMessage {Reason = "Server-side error occurred, try again later."});
|
||||
client.RPC.Close();
|
||||
}
|
||||
#endif
|
||||
_clients.Remove(client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a client has connected but is not authenticating yet.
|
||||
/// </summary>
|
||||
public event ClientEventHandler ClientConnected;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ClientConnected" /> event.
|
||||
/// </summary>
|
||||
/// <param name="client">The client</param>
|
||||
protected virtual void OnClientConnected(NPServerClient client)
|
||||
{
|
||||
var handler = ClientConnected;
|
||||
var args = new ClientEventArgs(client);
|
||||
if (handler != null) handler(this, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a client has disconnected.
|
||||
/// </summary>
|
||||
public event ClientEventHandler ClientDisconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ClientDisconnected" /> event.
|
||||
/// </summary>
|
||||
/// <param name="client">The client</param>
|
||||
protected virtual void OnClientDisconnected(NPServerClient client)
|
||||
{
|
||||
var handler = ClientDisconnected;
|
||||
var args = new ClientEventArgs(client);
|
||||
if (handler != null) handler(this, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a client has authenticated successfully.
|
||||
/// </summary>
|
||||
public event ClientEventHandler ClientAuthenticated;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ClientAuthenticated" /> event.
|
||||
/// </summary>
|
||||
/// <param name="client">The client</param>
|
||||
protected virtual void OnClientAuthenticated(NPServerClient client)
|
||||
{
|
||||
var handler = ClientAuthenticated;
|
||||
var args = new ClientEventArgs(client);
|
||||
if (handler != null) handler(this, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPSharp.RPC;
|
||||
using NPSharp.RPC.Messages.Data;
|
||||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp.NP
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a remote client connection to an NP server.
|
||||
/// </summary>
|
||||
public class NPServerClient
|
||||
{
|
||||
internal readonly NPServer NP;
|
||||
internal readonly RPCServerStream RPC;
|
||||
private readonly Dictionary<string, string> _presence = new Dictionary<string, string>();
|
||||
internal NPServerClient DedicatedServer;
|
||||
|
||||
internal NPServerClient(NPServer np, RPCServerStream rpcclient)
|
||||
{
|
||||
NP = np;
|
||||
RPC = rpcclient;
|
||||
}
|
||||
|
||||
public CSteamID UserID { get; internal set; }
|
||||
|
||||
public IEnumerable<FriendDetails> Friends
|
||||
{
|
||||
get
|
||||
{
|
||||
return NP.FriendsHandler == null
|
||||
? new FriendDetails[0]
|
||||
: NP.FriendsHandler.GetFriends(this).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<NPServerClient> FriendConnections
|
||||
{
|
||||
get { return NP.Clients.Where(c => Friends.Any(f => f.NPID == c.UserID)); }
|
||||
}
|
||||
|
||||
public FriendsPresence[] PresenceData
|
||||
{
|
||||
get { return _presence.Select(i => new FriendsPresence {Key = i.Key, Value = i.Value}).ToArray(); }
|
||||
}
|
||||
|
||||
public bool IsServer { get; set; }
|
||||
public bool IsDirty { get; set; }
|
||||
public int GroupID { get; set; }
|
||||
|
||||
internal void SetPresence(string key, string value)
|
||||
{
|
||||
if (!_presence.ContainsKey(key))
|
||||
_presence.Add(key, value);
|
||||
else
|
||||
_presence[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NPSharp</RootNamespace>
|
||||
<AssemblyName>npsharp_server</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<ProductVersion>12.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>TRACE;DEBUG;COMPILE_RPC,COMPILE_NP,COMPILE_AUTH</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>TRACE;COMPILE_RPC,COMPILE_NP,COMPILE_AUTH</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)\bin\$(Configuration)\$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\</IntDir>
|
||||
<IntermediateOutputPath>$(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\</IntermediateOutputPath>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\</BaseIntermediateOutputPath>
|
||||
<OutputPath>$(SolutionDir)\bin\$(Configuration)\$(Platform)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="uhttpsharp, Version=0.1.5307.27114, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(SolutionDir)\packages\uHttpSharp.0.1.5.4\lib\net40\uhttpsharp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Master\MasterServer.cs" />
|
||||
<Compile Include="Authentication\SessionAuthenticationServer.cs" />
|
||||
<Compile Include="Events\ClientEventArgs.cs" />
|
||||
<Compile Include="Events\ClientEventHandler.cs" />
|
||||
<Compile Include="Handlers\IAuthenticationHandler.cs" />
|
||||
<Compile Include="Handlers\IFileServingHandler.cs" />
|
||||
<Compile Include="Handlers\IFriendsHandler.cs" />
|
||||
<Compile Include="Handlers\IUserAvatarHandler.cs" />
|
||||
<Compile Include="NP\NPServer.cs" />
|
||||
<Compile Include="NP\NPServerClient.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RPC\RPCServerStream.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Master\README.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\client\NPSharp.Client.csproj">
|
||||
<Project>{c6f941a5-82af-456a-9b3a-752e5b001035}</Project>
|
||||
<Name>NPSharp.Client</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
</Project>
|
|
@ -1,39 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die mit einer Assembly verknüpft sind.
|
||||
|
||||
[assembly: AssemblyTitle("NPSharp server library")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Carl Kittelberger")]
|
||||
[assembly: AssemblyProduct("NPSharp")]
|
||||
[assembly: AssemblyCopyright("© 2014 Carl Kittelberger")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
||||
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
|
||||
[assembly: Guid("0efdd0b5-fd69-48fd-b714-f4f0e032f417")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||
// übernehmen, indem Sie "*" eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyFileVersion("1.0")]
|
|
@ -1,19 +0,0 @@
|
|||
#if COMPILE_RPC||COMPILE_NP
|
||||
|
||||
using System.Net.Sockets;
|
||||
using NPSharp.RPC.Messages;
|
||||
|
||||
namespace NPSharp.RPC
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a low-level stream which communicates with an NP client using RPC messages.
|
||||
/// </summary>
|
||||
public class RPCServerStream : RPCStream<RPCServerMessage, RPCClientMessage>
|
||||
{
|
||||
public RPCServerStream(Socket sock) : base(sock)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="6.0.3" targetFramework="net45" />
|
||||
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
|
||||
<package id="uHttpSharp" version="0.1.5.4" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Reference in New Issue