mirror of https://github.com/icedream/npsharp.git
Very slowly getting there.
parent
5ce198cbf6
commit
b13d462f0d
|
@ -0,0 +1,35 @@
|
|||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents details about the outcome of an authentication attempt.
|
||||
/// </summary>
|
||||
public class AuthenticationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an authentication result instance.
|
||||
/// </summary>
|
||||
/// <param name="npid">
|
||||
/// Set this to null if authentication should fail, otherwise use an instance of a steam ID which is
|
||||
/// unique to the user.
|
||||
/// </param>
|
||||
public AuthenticationResult(CSteamID npid = null)
|
||||
{
|
||||
UserID = npid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if authentiation succeeded, otherwise false.
|
||||
/// </summary>
|
||||
public bool Result
|
||||
{
|
||||
get { return UserID != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assigned user ID by the authentication provider. Can be null for failed authentication attempts.
|
||||
/// </summary>
|
||||
public CSteamID UserID { get; private set; }
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@ namespace NPSharp
|
|||
{
|
||||
public class ClientEventArgs : EventArgs
|
||||
{
|
||||
internal ClientEventArgs(NPServer.NPServerClient client)
|
||||
internal ClientEventArgs(NPServerClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public NPServer.NPServerClient Client { get; private set; }
|
||||
public NPServerClient Client { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace NPSharp
|
||||
{
|
||||
/// <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);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
namespace NPSharp
|
||||
{
|
||||
/// <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="AuthenticationResult" /></returns>
|
||||
AuthenticationResult 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="AuthenticationResult" /></returns>
|
||||
AuthenticationResult 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="AuthenticationResult" /></returns>
|
||||
AuthenticationResult 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
namespace NPSharp
|
||||
{
|
||||
/// <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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using NPSharp.RPC.Messages;
|
||||
|
||||
namespace NPSharp
|
||||
{
|
||||
/// <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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handler for all user avatar-related requests.
|
||||
/// </summary>
|
||||
public interface IUserAvatarHandler
|
||||
{
|
||||
byte[] GetUserAvatar(CSteamID id);
|
||||
|
||||
byte[] GetDefaultAvatar();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Configuration;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using NPSharp.RPC;
|
||||
using NPSharp.RPC.Messages;
|
||||
|
@ -12,34 +15,118 @@ namespace NPSharp
|
|||
{
|
||||
public class NPServer
|
||||
{
|
||||
public delegate void ClientEventHandler(object sender, ClientEventArgs args);
|
||||
|
||||
public IFileServingHandler FileHandler { get; set; }
|
||||
|
||||
public IUserAvatarHandler UserAvatarHandler { get; set; }
|
||||
|
||||
public interface IUserAvatarHandler
|
||||
{
|
||||
byte[] GetUserAvatar(CSteamID id);
|
||||
|
||||
byte[] GetDefaultAvatar();
|
||||
}
|
||||
|
||||
private readonly List<NPServerClient> _clients;
|
||||
private readonly ILog _log;
|
||||
private readonly Socket _socket;
|
||||
private readonly ushort _port;
|
||||
|
||||
public NPServer()
|
||||
/// <summary>
|
||||
/// Constructs a new NP server.
|
||||
/// </summary>
|
||||
public NPServer(ushort port = 3025)
|
||||
{
|
||||
_log = LogManager.GetLogger("NPServer");
|
||||
_clients = new List<NPServerClient>();
|
||||
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
||||
_port = port;
|
||||
}
|
||||
|
||||
private void _handleClient(NPServerClient client)
|
||||
/// <summary>
|
||||
/// Starts up the NP server.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (_socket.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.Tcp, "", _port);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Error("Socket permission request failed, can't start server.");
|
||||
throw new SocketException(10013 /* Permission denied */);
|
||||
}
|
||||
|
||||
_socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
|
||||
_socket.Listen(4);
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
_log.Debug("Listener loop started");
|
||||
var allDone = new ManualResetEvent(false);
|
||||
while (_socket != null && _socket.IsBound)
|
||||
{
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down all connections and stops the NP server.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The handler to use for file requests to this NP server.
|
||||
/// </summary>
|
||||
public IFileServingHandler FileHandler { 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; }
|
||||
|
||||
internal void _handleClient(NPServerClient client)
|
||||
{
|
||||
_log.Debug("Client now being handled");
|
||||
|
||||
#region RPC authentication message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<AuthenticateWithKeyMessage>(msg =>
|
||||
{
|
||||
var result = new AuthenticationResult();;
|
||||
var result = new AuthenticationResult();
|
||||
if (AuthenticationHandler != null)
|
||||
{
|
||||
try
|
||||
|
@ -109,11 +196,11 @@ namespace NPSharp
|
|||
client.UserID = result.UserID;
|
||||
|
||||
// Send "online" notification to all friends of this player
|
||||
foreach (var fconn in client.FriendConnections)
|
||||
foreach (NPServerClient fconn in client.FriendConnections)
|
||||
{
|
||||
fconn.RPC.Send(new FriendsPresenceMessage
|
||||
{
|
||||
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.NPID,
|
||||
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID,
|
||||
Friend = client.UserID,
|
||||
Presence = client.PresenceData,
|
||||
PresenceState = client.DedicatedServer == null ? 1 : 2
|
||||
|
@ -157,7 +244,7 @@ namespace NPSharp
|
|||
client.UserID = result.UserID;
|
||||
|
||||
// Send "online" notification to all friends of this player
|
||||
foreach (var fconn in client.FriendConnections)
|
||||
foreach (NPServerClient fconn in client.FriendConnections)
|
||||
{
|
||||
fconn.RPC.Send(new FriendsPresenceMessage
|
||||
{
|
||||
|
@ -185,7 +272,6 @@ namespace NPSharp
|
|||
|
||||
_log.DebugFormat("Ticket[Version={0},ServerID={1},Time={2}]", ticketData.Version,
|
||||
ticketData.ServerID, ticketData.Time);
|
||||
|
||||
}
|
||||
catch (ArgumentException error)
|
||||
{
|
||||
|
@ -198,7 +284,9 @@ namespace NPSharp
|
|||
{
|
||||
if (ticketData.ClientID == client.UserID) // NPID enforcement
|
||||
{
|
||||
var s = _clients.Where(c => c.IsServer && !c.IsDirty && c.UserID == ticketData.ServerID).ToArray();
|
||||
var s =
|
||||
_clients.Where(c => c.IsServer && !c.IsDirty && c.UserID == ticketData.ServerID)
|
||||
.ToArray();
|
||||
|
||||
if (s.Any())
|
||||
{
|
||||
|
@ -236,12 +324,14 @@ namespace NPSharp
|
|||
Result = validTicket ? 0 : 1
|
||||
});
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region RPC friend message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<FriendsSetPresenceMessage>(msg =>
|
||||
{
|
||||
foreach (var pdata in msg.Presence)
|
||||
foreach (FriendsPresence pdata in msg.Presence)
|
||||
{
|
||||
client.SetPresence(pdata.Key, pdata.Value);
|
||||
_log.DebugFormat("Client says presence \"{0}\" is \"{1}\"", pdata.Key, pdata.Value);
|
||||
|
@ -252,10 +342,10 @@ namespace NPSharp
|
|||
{
|
||||
// Why so goddamn complicated, NTA. Fuck.
|
||||
// TODO: Not compatible with non-public accounts
|
||||
var npid = new CSteamID((uint) msg.Guid, EUniverse.Public,
|
||||
ulong npid = new CSteamID((uint) msg.Guid, EUniverse.Public,
|
||||
EAccountType.Individual).ConvertToUint64();
|
||||
|
||||
var avatar = UserAvatarHandler.GetUserAvatar(npid) ?? UserAvatarHandler.GetDefaultAvatar();
|
||||
byte[] avatar = UserAvatarHandler.GetUserAvatar(npid) ?? UserAvatarHandler.GetDefaultAvatar();
|
||||
|
||||
client.RPC.Send(new FriendsGetUserAvatarResultMessage
|
||||
{
|
||||
|
@ -269,11 +359,197 @@ namespace NPSharp
|
|||
{
|
||||
// TODO
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: RPC message handling for storage
|
||||
#region RPC storage message handlers
|
||||
|
||||
client.RPC.AttachHandlerForMessageType<StorageGetPublisherFileMessage>(msg =>
|
||||
{
|
||||
if (FileHandler == 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;
|
||||
}
|
||||
|
||||
byte[] data = FileHandler.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
|
||||
});
|
||||
_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 (FileHandler == 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;
|
||||
}
|
||||
|
||||
byte[] data = FileHandler.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 (FileHandler == 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;
|
||||
}
|
||||
|
||||
FileHandler.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
|
||||
// TODO: RPC message handling for server sessions
|
||||
|
||||
#endregion
|
||||
|
||||
#region RPC server session message handler
|
||||
|
||||
#endregion
|
||||
|
||||
_clients.Add(client);
|
||||
try
|
||||
|
@ -298,123 +574,15 @@ namespace NPSharp
|
|||
_clients.Remove(client);
|
||||
}
|
||||
|
||||
public NPServerClient[] Clients
|
||||
{
|
||||
get { return _clients.ToArray(); } // Avoid race condition by IEnum changes
|
||||
}
|
||||
|
||||
public interface IFileServingHandler
|
||||
{
|
||||
Stream ReadUserFile(NPServerClient client, string file);
|
||||
|
||||
Stream ReadPublisherFile(NPServerClient client, string file);
|
||||
}
|
||||
|
||||
public IAuthenticationHandler AuthenticationHandler { get; set; }
|
||||
|
||||
public interface IAuthenticationHandler
|
||||
{
|
||||
AuthenticationResult AuthenticateUser(NPServerClient client, string username, string password);
|
||||
|
||||
AuthenticationResult AuthenticateUser(NPServerClient client, string token);
|
||||
|
||||
AuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey);
|
||||
|
||||
TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server);
|
||||
}
|
||||
|
||||
public class AuthenticationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an authentication result instance.
|
||||
/// Triggered when a client has connected but is not authenticating yet.
|
||||
/// </summary>
|
||||
/// <param name="npid">Set this to null if authentication should fail, otherwise use an instance of a steam ID which is unique to the user.</param>
|
||||
public AuthenticationResult(CSteamID npid = null)
|
||||
{
|
||||
UserID = npid;
|
||||
}
|
||||
|
||||
public bool Result { get { return UserID != null; } }
|
||||
|
||||
public CSteamID UserID { get; private set; }
|
||||
}
|
||||
|
||||
public enum TicketValidationResult
|
||||
{
|
||||
Valid = 0,
|
||||
Invalid = 1
|
||||
}
|
||||
|
||||
public IFriendsHandler FriendsHandler { get; set; }
|
||||
|
||||
public interface IFriendsHandler
|
||||
{
|
||||
IEnumerable<FriendDetails> GetFriends(NPServerClient client);
|
||||
|
||||
/*
|
||||
void SetFriendStatus(NPServerClient client, PresenceState presenceState,
|
||||
Dictionary<string, string> presenceData, ulong serverID)
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public class NPServerClient
|
||||
{
|
||||
internal NPServerClient(NPServer np, RPCServerStream rpcclient)
|
||||
{
|
||||
NP = np;
|
||||
RPC = rpcclient;
|
||||
}
|
||||
|
||||
internal readonly RPCServerStream RPC;
|
||||
|
||||
internal readonly NPServer NP;
|
||||
|
||||
public CSteamID UserID { get; internal set; }
|
||||
|
||||
public IEnumerable<FriendDetails> Friends
|
||||
{
|
||||
get { return NP.FriendsHandler.GetFriends(this).ToArray(); }
|
||||
}
|
||||
|
||||
public IEnumerable<NPServerClient> FriendConnections
|
||||
{
|
||||
get { return NP.Clients.Where(c => Friends.Any(f => f.NPID == c.NPID)); }
|
||||
}
|
||||
|
||||
internal NPServerClient DedicatedServer;
|
||||
|
||||
private readonly Dictionary<string, string> _presence = new Dictionary<string, string>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PresenceState
|
||||
{
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Playing = 2
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -422,8 +590,15 @@ namespace NPSharp
|
|||
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;
|
||||
|
@ -431,8 +606,15 @@ namespace NPSharp
|
|||
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;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPSharp.RPC;
|
||||
using NPSharp.RPC.Messages;
|
||||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp
|
||||
{
|
||||
/// <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.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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace NPSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the friend's current presence activity.
|
||||
/// </summary>
|
||||
public enum PresenceState
|
||||
{
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Playing = 2
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
@ -33,7 +34,9 @@ namespace NPSharp.RPC.Messages
|
|||
{
|
||||
var header = new byte[4*sizeof (uint)];
|
||||
|
||||
while (sock.Connected && !sock.Poll(1000, SelectMode.SelectRead)) { }
|
||||
while (sock.Connected && !sock.Poll(1000, SelectMode.SelectRead))
|
||||
{
|
||||
}
|
||||
|
||||
if (!sock.Connected)
|
||||
{
|
||||
|
@ -41,7 +44,7 @@ namespace NPSharp.RPC.Messages
|
|||
return null;
|
||||
}
|
||||
|
||||
var l = sock.Receive(header);
|
||||
int l = sock.Receive(header);
|
||||
if (l == 0)
|
||||
{
|
||||
Log.Debug("Received 0 bytes");
|
||||
|
@ -53,7 +56,7 @@ namespace NPSharp.RPC.Messages
|
|||
throw new ProtocolViolationException("Received incomplete header");
|
||||
}
|
||||
|
||||
uint signature, length, type, pid;
|
||||
uint signature, length, type, mid;
|
||||
using (var ms = new MemoryStream(header))
|
||||
{
|
||||
using (var br = new BinaryReader(ms))
|
||||
|
@ -61,7 +64,7 @@ namespace NPSharp.RPC.Messages
|
|||
signature = br.ReadUInt32();
|
||||
length = br.ReadUInt32();
|
||||
type = br.ReadUInt32();
|
||||
pid = br.ReadUInt32();
|
||||
mid = br.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,11 +77,11 @@ namespace NPSharp.RPC.Messages
|
|||
throw new ProtocolViolationException("Received packet with invalid signature");
|
||||
}
|
||||
|
||||
T packet;
|
||||
T message;
|
||||
|
||||
using (var ms = new MemoryStream(buffer))
|
||||
{
|
||||
var types = Assembly.GetExecutingAssembly().GetTypes().Where(
|
||||
Type[] types = Assembly.GetExecutingAssembly().GetTypes().Where(
|
||||
t =>
|
||||
t.IsSubclassOf(typeof (T))
|
||||
&&
|
||||
|
@ -96,27 +99,27 @@ namespace NPSharp.RPC.Messages
|
|||
return null;
|
||||
#endif
|
||||
}
|
||||
packet = (T) Serializer.NonGeneric.Deserialize(
|
||||
message = (T) Serializer.NonGeneric.Deserialize(
|
||||
types.Single(),
|
||||
ms
|
||||
);
|
||||
}
|
||||
|
||||
packet.MessageId = pid;
|
||||
message.MessageId = mid;
|
||||
|
||||
#if DEBUG
|
||||
Log.DebugFormat("{3}[ID={0},Type={1},TypeName={2}] {{", pid, packet.GetTypeId(), packet.GetType().Name,
|
||||
Log.DebugFormat("{3}[ID={0},Type={1},TypeName={2}] {{", mid, message.GetTypeId(), message.GetType().Name,
|
||||
typeof (T).Name);
|
||||
foreach (
|
||||
var prop in
|
||||
packet.GetType().GetProperties().Where(p => !(p.DeclaringType == typeof (RPCServerMessage))))
|
||||
PropertyInfo prop in
|
||||
message.GetType().GetProperties().Where(p => !(p.DeclaringType == typeof (RPCServerMessage))))
|
||||
{
|
||||
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(packet));
|
||||
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(message));
|
||||
}
|
||||
Log.DebugFormat("}} // deserialized from {0} bytes", header.Length + buffer.Length);
|
||||
#endif
|
||||
|
||||
return packet;
|
||||
return message;
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
|
@ -149,7 +152,7 @@ namespace NPSharp.RPC.Messages
|
|||
#if DEBUG
|
||||
Log.DebugFormat("{3}[ID={0},Type={1},TypeName={2}] {{", MessageId, GetTypeId(), GetType().Name,
|
||||
GetType().Name);
|
||||
foreach (var prop in GetType().GetProperties())
|
||||
foreach (PropertyInfo prop in GetType().GetProperties())
|
||||
{
|
||||
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(this));
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace NPSharp.RPC
|
|||
TypeCallbacks.Add(
|
||||
new KeyValuePair<uint, Action<TRecv>>(
|
||||
((PacketAttribute) typeof (T).GetCustomAttributes(typeof (PacketAttribute), false).Single()).Type,
|
||||
(Action<TRecv>)callback));
|
||||
msg => callback.Invoke((T)msg)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -131,12 +131,14 @@ namespace NPSharp.RPC
|
|||
if (_sock == null)
|
||||
throw new InvalidOperationException("You need to open the stream first.");
|
||||
|
||||
if (message.MessageId == default(uint))
|
||||
message.MessageId = MessageID;
|
||||
|
||||
var buffer = message.Serialize();
|
||||
byte[] buffer = message.Serialize();
|
||||
|
||||
_sock.Send(buffer);
|
||||
|
||||
if (typeof (TSend) == typeof (RPCClientMessage))
|
||||
IterateMessageID();
|
||||
}
|
||||
|
||||
|
@ -156,6 +158,9 @@ namespace NPSharp.RPC
|
|||
return null;
|
||||
}
|
||||
|
||||
if (typeof (TRecv) == typeof (RPCClientMessage))
|
||||
MessageID = message.MessageId;
|
||||
|
||||
// Callbacks
|
||||
foreach (var cbi in IDCallbacks.Where(p => p.Key == message.MessageId))
|
||||
cbi.Value.Invoke(message);
|
||||
|
|
|
@ -174,7 +174,9 @@ namespace NPSharp.Steam
|
|||
{
|
||||
case EAccountType.Invalid:
|
||||
case EAccountType.Individual:
|
||||
return AccountUniverse <= EUniverse.Public ? String.Format("STEAM_0:{0}:{1}", AccountID & 1, AccountID >> 1) : String.Format("STEAM_{2}:{0}:{1}", AccountID & 1, AccountID >> 1, (int) AccountUniverse);
|
||||
return AccountUniverse <= EUniverse.Public
|
||||
? String.Format("STEAM_0:{0}:{1}", AccountID & 1, AccountID >> 1)
|
||||
: String.Format("STEAM_{2}:{0}:{1}", AccountID & 1, AccountID >> 1, (int) AccountUniverse);
|
||||
default:
|
||||
return Convert.ToString(this);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using log4net.Appender;
|
||||
|
||||
namespace NPSharp
|
||||
{
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
namespace NPSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the outcome of a ticket validation attempt.
|
||||
/// </summary>
|
||||
public enum TicketValidationResult
|
||||
{
|
||||
Valid = 0,
|
||||
Invalid = 1
|
||||
}
|
||||
}
|
|
@ -54,10 +54,18 @@
|
|||
<Reference Include="System.Xml.Serialization" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AuthenticationResult.cs" />
|
||||
<Compile Include="ClientEventArgs.cs" />
|
||||
<Compile Include="ClientEventHandler.cs" />
|
||||
<Compile Include="IAuthenticationHandler.cs" />
|
||||
<Compile Include="IFileServingHandler.cs" />
|
||||
<Compile Include="IFriendsHandler.cs" />
|
||||
<Compile Include="IUserAvatarHandler.cs" />
|
||||
<Compile Include="NPClient.cs" />
|
||||
<Compile Include="NPFileException.cs" />
|
||||
<Compile Include="NPServer.cs" />
|
||||
<Compile Include="NPServerClient.cs" />
|
||||
<Compile Include="PresenceState.cs" />
|
||||
<Compile Include="RPC\Messages\AuthenticateRegisterServerMessage.cs" />
|
||||
<Compile Include="RPC\Messages\AuthenticateRegisterServerResultMessage.cs" />
|
||||
<Compile Include="RPC\Messages\AuthenticateUserGroupMessage.cs" />
|
||||
|
@ -104,6 +112,7 @@
|
|||
<Compile Include="Steam\InteropHelp.cs" />
|
||||
<Compile Include="Steam\SteamID_t.cs" />
|
||||
<Compile Include="Ticket.cs" />
|
||||
<Compile Include="TicketValidationResult.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Steam\README.txt" />
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NPSharp.Steam;
|
||||
|
||||
namespace NPSharp.CommandLine.Server
|
||||
{
|
||||
class DummyAuthenticationHandler : IAuthenticationHandler
|
||||
{
|
||||
private uint _userID = 0;
|
||||
|
||||
public DummyAuthenticationHandler()
|
||||
{
|
||||
// TODO: Listener on port 12003 accepting HTTP token retrievals
|
||||
}
|
||||
|
||||
public AuthenticationResult AuthenticateUser(NPServerClient client, string username, string password)
|
||||
{
|
||||
return new AuthenticationResult(new CSteamID()
|
||||
{
|
||||
AccountID = _userID++
|
||||
});
|
||||
}
|
||||
|
||||
public AuthenticationResult AuthenticateUser(NPServerClient client, string token)
|
||||
{
|
||||
return new AuthenticationResult(new CSteamID()
|
||||
{
|
||||
AccountID = _userID++
|
||||
});
|
||||
}
|
||||
|
||||
public AuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,85 @@
|
|||
namespace NPSharp.CommandLine.Server
|
||||
using System;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using log4net.Appender;
|
||||
using log4net.Config;
|
||||
using log4net.Core;
|
||||
using log4net.Layout;
|
||||
|
||||
namespace NPSharp.CommandLine.Server
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
static void Main()
|
||||
{
|
||||
|
||||
// log4net setup
|
||||
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
|
||||
{
|
||||
var appender = new ConsoleAppender
|
||||
{
|
||||
#if DEBUG
|
||||
Threshold = Level.Debug,
|
||||
#else
|
||||
Threshold = Level.Info,
|
||||
#endif
|
||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||
};
|
||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
||||
}
|
||||
else
|
||||
{
|
||||
var appender = new ColoredConsoleAppender
|
||||
{
|
||||
#if DEBUG
|
||||
Threshold = Level.Debug,
|
||||
#else
|
||||
Threshold = Level.Info,
|
||||
#endif
|
||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||
};
|
||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||
{
|
||||
Level = Level.Debug,
|
||||
ForeColor = ColoredConsoleAppender.Colors.Cyan | ColoredConsoleAppender.Colors.HighIntensity
|
||||
});
|
||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||
{
|
||||
Level = Level.Info,
|
||||
ForeColor = ColoredConsoleAppender.Colors.Green | ColoredConsoleAppender.Colors.HighIntensity
|
||||
});
|
||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||
{
|
||||
Level = Level.Warn,
|
||||
ForeColor = ColoredConsoleAppender.Colors.Purple | ColoredConsoleAppender.Colors.HighIntensity
|
||||
});
|
||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||
{
|
||||
Level = Level.Error,
|
||||
ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity
|
||||
});
|
||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||
{
|
||||
Level = Level.Fatal,
|
||||
ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity,
|
||||
BackColor = ColoredConsoleAppender.Colors.Red
|
||||
});
|
||||
appender.ActivateOptions();
|
||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
||||
}
|
||||
|
||||
var log = LogManager.GetLogger("Main");
|
||||
|
||||
log.Info("Now starting NP server...");
|
||||
var np = new NPServer(3036)
|
||||
{
|
||||
AuthenticationHandler = new DummyAuthenticationHandler(),
|
||||
// TODO: Implement the other handlers
|
||||
};
|
||||
np.Start();
|
||||
log.Info("NP server started up and is now ready.");
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
<AssemblyName>npserv</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -37,22 +39,29 @@
|
|||
<OutputPath>$(SolutionDir)\bin\$(Configuration)\$(Platform)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DummyAuthenticationHandler.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libnpsharp\libnpsharp.csproj">
|
||||
<Project>{1a5ac63a-250e-4bc8-b81a-822ac31f5e37}</Project>
|
||||
<Name>libnpsharp</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.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">
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Reference in New Issue