-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApianMessages.cs
142 lines (118 loc) · 6.28 KB
/
ApianMessages.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Apian
{
// ReSharper disable UnusedType.Global,NotAccessedFIeld.Global,FieldCanBeMadeReadOnly.Global,UnusedMember.Global
// (can;t be readonly because of NewtonSoft.JSON)
public class ApianCoreMessage
{
// ReSharper disable MemberCanBePrivate.Global
// Client game or app messages derive from this
public string MsgType;
public long TimeStamp;
public ApianCoreMessage(string t, long ts) {MsgType = t; TimeStamp = ts;}
public ApianCoreMessage() {}
// ReSharper enable MemberCanBePrivate.Global
}
public class ApianMessage
{
// ReSharper disable MemberCanBeProtected.Global
public const string CliRequest = "APapRq";
public const string CliObservation = "APapObs";
public const string CliCommand = "APapCmd";
public const string ApianClockOffset = "APclk";
public const string GroupMessage = "APGrp";
public const string CheckpointMsg = "APchk";
public string DestGroupId; // Can be empty
public string MsgType;
// ReSharper enable MemberCanBeProtected.Global
protected ApianMessage(string gid, string typ) { DestGroupId = gid; MsgType = typ; }
protected ApianMessage() {}
}
public class ApianWrappedClientMessage : ApianMessage
{
public string CliMsgType; // TODO: This is a hack and is a copy of the ApianClientMessage MsgType
// It's related to deserializing from JSON into an ApianWrappedClientMessage
// and me not wanting to include the full derived class names in the data stream.
[JsonIgnore]
public virtual ApianCoreMessage ClientMsg {get;}
public ApianWrappedClientMessage(string gid, string apianMsgType, string clientMsgType) : base(gid, apianMsgType)
{
CliMsgType=clientMsgType;
}
public ApianWrappedClientMessage() : base() {}
}
public class ApianRequest : ApianWrappedClientMessage
{
public ApianRequest(string gid, ApianCoreMessage clientMsg) : base(gid, CliRequest, clientMsg.MsgType) {}
public ApianRequest() : base() {}
public virtual ApianCommand ToCommand(long seqNum) {return null;}
}
public class ApianObservation : ApianWrappedClientMessage
{
public ApianObservation(string gid,ApianCoreMessage clientMsg) : base(gid, CliObservation, clientMsg.MsgType) {}
public ApianObservation() : base() {}
public virtual ApianCommand ToCommand(long seqNum) {return null;}
}
public class ApianCommand : ApianWrappedClientMessage {
public long SequenceNum;
public ApianCommand(long seqNum, string gid, ApianCoreMessage clientMsg) : base(gid, CliCommand, clientMsg.MsgType) {SequenceNum=seqNum;}
public ApianCommand() : base() {}
}
public class ApianClockOffsetMsg : ApianMessage // Send on main channel
{
public string PeerId;
public long ClockOffset;
public ApianClockOffsetMsg(string gid, string pid, long offset) : base(gid, ApianClockOffset) {PeerId=pid; ClockOffset=offset;}
public ApianClockOffsetMsg() : base() {}
}
public class ApianCheckpointMsg : ApianCoreMessage
{
// This is a "mock client command" for an ApianCheckpointCommand to "wrap"
public ApianCheckpointMsg( long timeStamp) : base(ApianMessage.CheckpointMsg, timeStamp) {}
}
public class ApianCheckpointCommand : ApianCommand
{
// A checkpoint request is implemented as an ApianCommand so it can:
// - Explicitly specify an "epoch" for the checkpoint (its sequence number)
// - Be part of the serial command stream. Client commands are strictly evaluated
// and applied in order. By being a command the request can guarantee that it is processed
// by all peers on an app state that has the identical commands applied - and will take advantage
// of the ordering mechanism
public override ApianCoreMessage ClientMsg {get => checkpointMsg;}
public ApianCheckpointMsg checkpointMsg;
public ApianCheckpointCommand(long seqNum, string gid, ApianCheckpointMsg _checkpointMsg) : base(seqNum, gid, _checkpointMsg) {checkpointMsg=_checkpointMsg;}
public ApianCheckpointCommand() : base() {}
}
static public class ApianMessageDeserializer
{
// IMPORTANT: this only deserialized to the Apian[Foo]Msg level. In many cases all that gets you is
// an app-specific "subType" and you then have to do it again at the App message level.
// TODO: This is super fugly. Make it not.
public static Dictionary<string, Func<string, ApianMessage>> deserializers = new Dictionary<string, Func<string, ApianMessage>>()
{
{ApianMessage.CliRequest, (s) => JsonConvert.DeserializeObject<ApianRequest>(s) },
{ApianMessage.CliObservation, (s) => JsonConvert.DeserializeObject<ApianObservation>(s) },
{ApianMessage.CliCommand, (s) => JsonConvert.DeserializeObject<ApianCommand>(s) },
{ApianMessage.GroupMessage, (s) => JsonConvert.DeserializeObject<ApianGroupMessage>(s) },
{ApianMessage.ApianClockOffset, (s) => JsonConvert.DeserializeObject<ApianClockOffsetMsg>(s) },
};
public static Dictionary<string, Func<ApianMessage, string>> subTypeExtractor = new Dictionary<string, Func<ApianMessage, string>>()
{
{ApianMessage.CliRequest, (msg) => (msg as ApianRequest).CliMsgType }, // Need to use App-level message deserializer to fully decode
{ApianMessage.CliObservation, (msg) => (msg as ApianObservation).CliMsgType },
{ApianMessage.CliCommand, (msg) => (msg as ApianCommand).CliMsgType },
{ApianMessage.GroupMessage, (msg) => (msg as ApianGroupMessage).GroupMsgType }, // Need to use ApianGroupMessageDeserializer to fully decode
{ApianMessage.ApianClockOffset, (msg) => null },
};
public static ApianMessage FromJSON(string msgType, string json)
{
return deserializers[msgType](json) as ApianMessage;
}
public static string GetSubType(ApianMessage msg)
{
return subTypeExtractor[msg.MsgType](msg);
}
}
}