diff --git a/Nibriboard/Client/Messages/HandshakeRequestMessage.cs b/Nibriboard/Client/Messages/HandshakeRequestMessage.cs
index 6c0eb6e..26c9641 100644
--- a/Nibriboard/Client/Messages/HandshakeRequestMessage.cs
+++ b/Nibriboard/Client/Messages/HandshakeRequestMessage.cs
@@ -9,10 +9,11 @@ namespace Nibriboard.Client.Messages
/// The initial visible area on the client's screen.
/// Very useful for determining which chunks we should send a client when they first connect.
///
- public Rectangle InitialViewport = new Rectangle(
- 0, 0,
- 1366, 768
- );
+ public Rectangle InitialViewport = Rectangle.Empty;
+ ///
+ /// The initial position of the user's cursor.
+ ///
+ public Point InitialAbsCursorPosition = Point.Empty;
public HandshakeRequestMessage()
{
diff --git a/Nibriboard/Client/NibriClient.cs b/Nibriboard/Client/NibriClient.cs
index 191ed2d..c573b23 100644
--- a/Nibriboard/Client/NibriClient.cs
+++ b/Nibriboard/Client/NibriClient.cs
@@ -2,22 +2,63 @@
using System.Threading.Tasks;
using System.Text;
using System.Collections.Generic;
+using System.Drawing;
using IotWeb.Common.Http;
using SBRL.Utilities;
+using Nibriboard.Client.Messages;
+using Newtonsoft.Json;
+using System.Reflection;
+using RippleSpace;
namespace Nibriboard.Client
{
public class NibriClient
{
+ private static int nextId = 1;
+ private static int getNextId() { return nextId++; }
+
+ ///
+ /// This client's unique id.
+ ///
+ public readonly int Id = getNextId();
+ ///
+ /// The nibri client manager
+ ///
private readonly NibriClientManager manager;
+ ///
+ /// The underlying websocket connection to the client.
+ /// Please try not to call the send method on here - use the NibriClient Send() method instead.
+ ///
private readonly WebSocket client;
- private Dictionary messageEventTypes = new Dictionary()
+ private static readonly Dictionary messageEventTypes = new Dictionary()
{
- ["handshake"] =
+ ["handshakeRequest"] = typeof(HandshakeRequestMessage)
};
+ ///
+ /// Whether this client has completed the handshake yet or not.
+ ///
+ public bool HandshakeCompleted = false;
+ ///
+ /// The name this client has assignedto themselves.
+ ///
+ /// The name.
+ public string Name { get; private set; }
+ ///
+ /// The current area that this client is looking at.
+ ///
+ /// The current view port.
+ public Rectangle CurrentViewPort { get; private set; } = Rectangle.Empty;
+ ///
+ /// The absolute position in plane-space of this client's cursor.
+ ///
+ /// The absolute cursor position.
+ public Point AbsoluteCursorPosition { get; private set; } = Point.Empty;
+
+ #region Core Setup & Message Routing Logic
+
public NibriClient(NibriClientManager inManager, WebSocket inClient)
{
manager = inManager;
@@ -40,16 +81,101 @@ namespace Nibriboard.Client
}
- public void Send(string message)
+ private async Task handleMessage(string frame)
+ {
+ string eventName = JsonUtilities.DeserializeProperty(frame, "event");
+
+ if (!messageEventTypes.ContainsKey(eventName))
+ Log.WriteLine("Received message with invalid event {1} from Client #{0}", Id, eventName);
+
+ Type messageType = messageEventTypes[eventName];
+ Type jsonNet = typeof(JsonConvert);
+ MethodInfo deserialiserInfo = jsonNet.GetMethod("DeserailizeObject");
+ MethodInfo genericInfo = deserialiserInfo.MakeGenericMethod(messageType);
+ var decodedMessage = genericInfo.Invoke(null, new object[] { frame });
+
+ string handlerMethodName = "handle" + decodedMessage.GetType().Name;
+ Type clientType = this.GetType();
+ MethodInfo handlerInfo = clientType.GetMethod(handlerMethodName);
+ await (Task)handlerInfo.Invoke(this, new object[] { decodedMessage });
+ }
+
+ #endregion
+
+ ///
+ /// Sends a to the client.
+ /// If you *really* need to send a raw message to the client, you can do so with the SendRawa() method.
+ ///
+ /// The message to send.
+ public void Send(Message message)
+ {
+ SendRaw(JsonConvert.SerializeObject(message));
+ }
+ ///
+ /// Sends a raw string to the client. Don't use unnless you know what you're doing!
+ /// Use the regular Send() method if you can possibly help it.
+ ///
+ /// The message to send.
+ public void SendRaw(string message)
{
client.Send(Encoding.UTF8.GetBytes(message));
}
- private async Task handleMessage(string frame)
+ ///
+ /// Generates a new ClientState object representing this client's state at the current time.
+ ///
+ public ClientState GenerateStateSnapshot()
{
- string eventName = JsonUtilities.DeserializeProperty(frame, "event");
+ ClientState result = new ClientState();
+ result.Id = Id;
+ result.Name = Name;
+ result.AbsCursorPosition = AbsoluteCursorPosition;
+ result.Viewport = CurrentViewPort;
+ return result;
+ }
+ #region Message Handlers
+ ///
+ /// Handles an incoming handshake request. We should only receive one of these!
+ ///
+ protected Task handleHandshakeRequestMessage(HandshakeRequestMessage message)
+ {
+ CurrentViewPort = message.InitialViewport;
+ AbsoluteCursorPosition = message.InitialAbsCursorPosition;
+
+ Send(GenerateClientStateUpdate());
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Handles an incoming cursor position message from the client..
+ ///
+ protected Task handleCursorPositionMessage(CursorPositionMessage message) {
+ AbsoluteCursorPosition = message.AbsCursorPosition;
+
+ return Task.CompletedTask;
+ }
+ #endregion
+
+ ///
+ /// Generates an update message that contains information about the locations and states of all connected clients.
+ /// Automatically omits information about the current client.
+ ///
+ /// The client state update message.
+ protected ClientStateMessage GenerateClientStateUpdate()
+ {
+ ClientStateMessage result = new ClientStateMessage();
+ foreach (NibriClient client in manager.Clients)
+ {
+ // Don't include ourselves in the update message!
+ if (client == this)
+ continue;
+
+ result.ClientStates.Add(client.GenerateStateSnapshot());
+ }
+ return result;
}
}
}
diff --git a/Nibriboard/Client/NibriClientManager.cs b/Nibriboard/Client/NibriClientManager.cs
index a7ee868..82dafae 100644
--- a/Nibriboard/Client/NibriClientManager.cs
+++ b/Nibriboard/Client/NibriClientManager.cs
@@ -9,14 +9,14 @@ namespace Nibriboard.Client
public class NibriClientManager : IWebSocketRequestHandler
{
private ClientSettings clientSettings;
- private List clients = new List();
+ public List Clients = new List();
///
/// The number of clients currently connected to this Nibriboard.
///
public int ClientCount {
get {
- return clients.Count;
+ return Clients.Count;
}
}
@@ -45,18 +45,18 @@ namespace Nibriboard.Client
public void Connected(WebSocket newSocket)
{
NibriClient client = new NibriClient(this, newSocket);
- clients.Add(client);
+ Clients.Add(client);
}
public void Broadcast(NibriClient sendingClient, string message)
{
- foreach(NibriClient client in clients)
+ foreach(NibriClient client in Clients)
{
// Don't send the message to the sender
if (client == sendingClient)
continue;
- client.Send(message);
+ client.SendRaw(message);
}
}
}
diff --git a/Nibriboard/Nibriboard.csproj b/Nibriboard/Nibriboard.csproj
index 5157dad..033eff5 100644
--- a/Nibriboard/Nibriboard.csproj
+++ b/Nibriboard/Nibriboard.csproj
@@ -7,7 +7,7 @@
Exe
Nibriboard
Nibriboard
- v4.5
+ v4.6.1
true
@@ -74,6 +74,9 @@
+
+
+
@@ -97,7 +100,7 @@
-
+