diff --git a/src/DrawTogether.Actors/DrawTogether.Actors.csproj b/src/DrawTogether.Actors/DrawTogether.Actors.csproj index 01a77b1..54ebfa9 100644 --- a/src/DrawTogether.Actors/DrawTogether.Actors.csproj +++ b/src/DrawTogether.Actors/DrawTogether.Actors.csproj @@ -20,8 +20,7 @@ - - + diff --git a/src/DrawTogether.Actors/Serialization/DrawingProtocolSerializer.cs b/src/DrawTogether.Actors/Serialization/DrawingProtocolSerializer.cs index 41325d8..6a6d62d 100644 --- a/src/DrawTogether.Actors/Serialization/DrawingProtocolSerializer.cs +++ b/src/DrawTogether.Actors/Serialization/DrawingProtocolSerializer.cs @@ -1,7 +1,12 @@ -using Akka.Actor; +using System.Collections.Immutable; +using Akka.Actor; using Akka.Serialization; +using DrawTogether.Entities; using DrawTogether.Entities.Drawings; using DrawTogether.Entities.Drawings.Messages; +using DrawTogether.Entities.Users; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; namespace DrawTogether.Actors.Serialization; @@ -27,16 +32,220 @@ public sealed class DrawingProtocolSerializer : SerializerWithStringManifest private const string RemoveUserManifest = "ru"; private const string DrawingActivityUpdateManifest = "da"; private const string DrawingSessionStateManifest = "ds"; - + public DrawingProtocolSerializer(ExtendedActorSystem system) : base(system) { } public override byte[] ToBinary(object obj) { - throw new NotImplementedException(); + switch (obj) + { + case IDrawingSessionEvent e: + return e switch + { + DrawingSessionEvents.StrokeAdded sa => ToProto(sa).ToByteArray(), + DrawingSessionEvents.StrokeRemoved sr => ToProto(sr).ToByteArray(), + DrawingSessionEvents.StrokesCleared sc => ToProto(sc).ToByteArray(), + DrawingSessionEvents.UserAdded ua => ToProto(ua).ToByteArray(), + DrawingSessionEvents.UserRemoved ur => ToProto(ur).ToByteArray(), + DrawingSessionEvents.DrawingSessionClosed dc => ToProto(dc).ToByteArray(), + _ => throw new ArgumentException($"Can't serialize object of type {e.GetType()}") + }; + case IDrawingSessionCommand c: + return c switch + { + DrawingSessionCommands.AddStroke asCmd => ToProto(asCmd).ToByteArray(), + DrawingSessionCommands.RemoveStroke rsCmd => ToProto(rsCmd).ToByteArray(), + DrawingSessionCommands.ClearStrokes csCmd => ToProto(csCmd).ToByteArray(), + DrawingSessionCommands.AddUser auCmd => ToProto(auCmd).ToByteArray(), + DrawingSessionCommands.RemoveUser ruCmd => ToProto(ruCmd).ToByteArray(), + _ => throw new ArgumentException($"Can't serialize object of type {c.GetType()}") + }; + case IDrawingSessionQuery q: + return q switch + { + DrawingSessionQueries.GetDrawingSessionState gs => ToProto(gs).ToByteArray(), + DrawingSessionQueries.SubscribeToDrawingSession su => ToProto(su).ToByteArray(), + DrawingSessionQueries.SubscribeAcknowledged sak => ToProto(sak).ToByteArray(), + DrawingSessionQueries.UnsubscribeFromDrawingSession uu => ToProto(uu).ToByteArray(), + DrawingSessionQueries.UnsubscribeAcknowledged uak => ToProto(uak).ToByteArray(), + _ => throw new ArgumentException($"Can't serialize object of type {q.GetType()}") + }; + case DrawingActivityUpdate da: + return ToProto(da).ToByteArray(); + case DrawingSessionState state: + return ToProto(state).ToByteArray(); + default: + throw new ArgumentException($"Can't serialize object of type {obj.GetType()}"); + } + } + + private Proto.DrawingActivityUpdated ToProto(DrawingActivityUpdate asCmd) + { + var protoDrawingActivityUpdated = new Proto.DrawingActivityUpdated() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId, + LastUpdated = asCmd.LastUpdate.ToTimestamp(), + ActiveUsers = asCmd.ActiveUsers, + IsRemoved = asCmd.IsRemoved + }; + return protoDrawingActivityUpdated; + } + + private Proto.SubscribeToDrawingSessionState ToProto(DrawingSessionQueries.SubscribeToDrawingSession asCmd) + { + var protoSubscribeToDrawingSessionState = new Proto.SubscribeToDrawingSessionState() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId + }; + return protoSubscribeToDrawingSessionState; + } + + private Proto.SubscribeAcknowledged ToProto(DrawingSessionQueries.SubscribeAcknowledged asCmd) + { + var protoSubscribeAcknowledged = new Proto.SubscribeAcknowledged() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId + }; + return protoSubscribeAcknowledged; + } + + private Proto.UnsubscribeFromDrawingSessionState ToProto(DrawingSessionQueries.UnsubscribeFromDrawingSession asCmd) + { + var protoUnsubscribeFromDrawingSessionState = new Proto.UnsubscribeFromDrawingSessionState() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId + }; + return protoUnsubscribeFromDrawingSessionState; + } + + private Proto.UnsubscribeAcknowledged ToProto(DrawingSessionQueries.UnsubscribeAcknowledged asCmd) + { + var protoUnsubscribeAcknowledged = new Proto.UnsubscribeAcknowledged() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId + }; + return protoUnsubscribeAcknowledged; + } + + + private Proto.AddStroke ToProto(DrawingSessionCommands.AddStroke asCmd) + { + var protoAddStroke = new Proto.AddStroke() + { + DrawingSessionId = asCmd.DrawingSessionId.SessionId, + ConnectedStroke = ToProto(asCmd.Stroke) + }; + return protoAddStroke; + } + + private Proto.RemoveStroke ToProto(DrawingSessionCommands.RemoveStroke rsCmd) + { + var protoRemoveStroke = new Proto.RemoveStroke() + { + DrawingSessionId = rsCmd.DrawingSessionId.SessionId, + StrokeId = rsCmd.StrokeId.Id + }; + return protoRemoveStroke; + } + + private Proto.ClearStrokes ToProto(DrawingSessionCommands.ClearStrokes csCmd) + { + var protoClearStrokes = new Proto.ClearStrokes() + { + DrawingSessionId = csCmd.DrawingSessionId.SessionId + }; + return protoClearStrokes; + } + + private Proto.AddUser ToProto(DrawingSessionCommands.AddUser auCmd) + { + var protoAddUser = new Proto.AddUser() + { + DrawingSessionId = auCmd.DrawingSessionId.SessionId, + UserId = auCmd.UserId.IdentityName + }; + return protoAddUser; + } + + private Proto.RemoveUser ToProto(DrawingSessionCommands.RemoveUser ruCmd) + { + var protoRemoveUser = new Proto.RemoveUser() + { + DrawingSessionId = ruCmd.DrawingSessionId.SessionId, + UserId = ruCmd.UserId.IdentityName + }; + return protoRemoveUser; + } + + private Proto.GetDrawingSessionState ToProto(DrawingSessionQueries.GetDrawingSessionState gs) + { + var protoGetDrawingSessionState = new Proto.GetDrawingSessionState() + { + DrawingSessionId = gs.DrawingSessionId.SessionId + }; + return protoGetDrawingSessionState; + } + + + private Proto.AddStroke ToProto(DrawingSessionEvents.StrokeAdded stroke) + { + var protoStrokeAdded = new Proto.AddStroke() + { + DrawingSessionId = stroke.DrawingSessionId.SessionId, + ConnectedStroke = ToProto(stroke.Stroke) + }; + return protoStrokeAdded; } + private Proto.RemoveStroke ToProto(DrawingSessionEvents.StrokeRemoved stroke) + { + var protoStrokeRemoved = new Proto.RemoveStroke() + { + DrawingSessionId = stroke.DrawingSessionId.SessionId, + StrokeId = stroke.StrokeId.Id + }; + return protoStrokeRemoved; + } + + private Proto.ClearStrokes ToProto(DrawingSessionEvents.StrokesCleared strokes) + { + var protoStrokesCleared = new Proto.ClearStrokes() + { + DrawingSessionId = strokes.DrawingSessionId.SessionId + }; + return protoStrokesCleared; + } + + private Proto.AddUser ToProto(DrawingSessionEvents.UserAdded user) + { + var protoUserAdded = new Proto.AddUser() + { + DrawingSessionId = user.DrawingSessionId.SessionId, + UserId = user.UserId.IdentityName + }; + return protoUserAdded; + } + + private Proto.RemoveUser ToProto(DrawingSessionEvents.UserRemoved user) + { + var protoUserRemoved = new Proto.RemoveUser() + { + DrawingSessionId = user.DrawingSessionId.SessionId, + UserId = user.UserId.IdentityName + }; + return protoUserRemoved; + } + + private Proto.SessionClosed ToProto(DrawingSessionEvents.DrawingSessionClosed closed) + { + var protoDrawingSessionClosed = new Proto.SessionClosed() + { + DrawingSessionId = closed.DrawingSessionId.SessionId + }; + return protoDrawingSessionClosed; + } public override object FromBinary(byte[] bytes, string manifest) { @@ -55,15 +264,12 @@ public override string Manifest(object o) _ => throw new ArgumentException($"Can't serialize object of type {o.GetType()}") }; } - - private static string GetManifestForQuery(object o) { return o switch { DrawingSessionQueries.GetDrawingSessionState => GetDrawingSessionStateManifest, - DrawingSessionQueries.GetDrawingSessionUsers => GetDrawingSessionUsersManifest, DrawingSessionQueries.SubscribeToDrawingSession => SubscribeToDrawingSessionManifest, DrawingSessionQueries.SubscribeAcknowledged => SubscribeAcknowledgedManifest, DrawingSessionQueries.UnsubscribeFromDrawingSession => UnsubscribeFromDrawingSessionManifest, @@ -99,4 +305,50 @@ private static string GetManifestForEvent(object o) _ => throw new ArgumentException($"Can't serialize object of type {o.GetType()}") }; } + + Proto.ConnectedStroke ToProto(ConnectedStroke stroke) + { + var protoStroke = new Proto.ConnectedStroke + { + Id = stroke.Id.Id, + Points = { stroke.Points.Select(p => new Proto.Point { X = p.X, Y = p.Y }) }, + StrokeWidth = stroke.StrokeWidth.Value, + StrokeColor = stroke.StrokeColor.HexCodeOrColorName + }; + return protoStroke; + } + + ConnectedStroke FromProto(Proto.ConnectedStroke protoStroke) + { + var stroke = new ConnectedStroke(new StrokeId(protoStroke.Id)) + { + Points = protoStroke.Points.Select(p => new Point(p.X, p.Y)).ToList(), + StrokeWidth = new GreaterThanZeroInteger(protoStroke.StrokeWidth), + StrokeColor = new Color(protoStroke.StrokeColor) + }; + return stroke; + } + + Proto.DrawingSessionState ToProto(DrawingSessionState state) + { + var protoState = new Proto.DrawingSessionState + { + DrawingSessionId = state.DrawingSessionId.SessionId, + ConnectedStrokes = { state.Strokes.Select(v => ToProto(v.Value)) }, + ConnectedUsers = { state.ConnectedUsers.Select(u => u.IdentityName) }, + LastUpdated = state.LastUpdate.ToTimestamp() + }; + return protoState; + } + + DrawingSessionState FromProto(Proto.DrawingSessionState protoState) + { + var state = new DrawingSessionState(new DrawingSessionId(protoState.DrawingSessionId)) + { + Strokes = protoState.ConnectedStrokes.ToImmutableDictionary(s => new StrokeId(s.Id), s => FromProto(s)), + ConnectedUsers = protoState.ConnectedUsers.Select(u => new UserId(u)).ToImmutableHashSet(), + LastUpdate = protoState.LastUpdated.ToDateTime() + }; + return state; + } } \ No newline at end of file diff --git a/src/DrawTogether.Actors/Serialization/Proto/DrawingProtocol.proto b/src/DrawTogether.Actors/Serialization/Proto/DrawingProtocol.proto index f6648fb..2cfd974 100644 --- a/src/DrawTogether.Actors/Serialization/Proto/DrawingProtocol.proto +++ b/src/DrawTogether.Actors/Serialization/Proto/DrawingProtocol.proto @@ -5,25 +5,13 @@ package DrawTogether.Actors.Serialization.Proto; import "google/protobuf/timestamp.proto"; /* Primitives */ -message UserId { - string id = 1; -} - -message DrawingSessionId { - string id = 1; -} - -message StrokeId { - int32 id = 1; -} - message Point { double x = 1; double y = 2; } message ConnectedStroke { - StrokeId id = 1; + int32 id = 1; repeated Point points = 2; int32 strokeWidth = 3; string strokeColor = 4; @@ -31,67 +19,67 @@ message ConnectedStroke { /* Commands and events */ message AddUser { - DrawingSessionId drawingSessionId = 1; - UserId userId = 2; + string drawingSessionId = 1; + string userId = 2; } message RemoveUser { - DrawingSessionId drawingSessionId = 1; - UserId userId = 2; + string drawingSessionId = 1; + string userId = 2; } message AddStroke { - DrawingSessionId drawingSessionId = 2; + string drawingSessionId = 2; ConnectedStroke connectedStroke = 3; } message RemoveStroke { - DrawingSessionId drawingSessionId = 1; - StrokeId strokeId = 2; + string drawingSessionId = 1; + int32 strokeId = 2; } message ClearStrokes { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; } message SessionClosed { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; } /* State */ message DrawingSessionState{ - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; repeated ConnectedStroke connectedStrokes = 2; - repeated UserId connectedUsers = 3; + repeated string connectedUsers = 3; google.protobuf.Timestamp lastUpdated = 4; } /* Queries */ message GetDrawingSessionState { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; } message SubscribeToDrawingSessionState { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; } message SubscribeAcknowledged { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; } -message UnsuscribeFromDrawingSessionState { - DrawingSessionId drawingSessionId = 1; +message UnsubscribeFromDrawingSessionState { + string drawingSessionId = 1; } -message UnsuscribeAcknowledged { - DrawingSessionId drawingSessionId = 1; +message UnsubscribeAcknowledged { + string drawingSessionId = 1; } message DrawingActivityUpdated { - DrawingSessionId drawingSessionId = 1; + string drawingSessionId = 1; google.protobuf.Timestamp lastUpdated = 2; int32 activeUsers = 3; bool isRemoved = 4; diff --git a/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs index 2016a1e..aa512d5 100644 --- a/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs +++ b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs @@ -6,8 +6,6 @@ public static class DrawingSessionQueries { public sealed record GetDrawingSessionState(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; - public sealed record GetDrawingSessionUsers(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; - public sealed record SubscribeToDrawingSession(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; public sealed record SubscribeAcknowledged(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery;