* csharp-sdk.sln:
* LCIMClient.cs: * Realtime.csproj: * Utils.cs: * Protobuf.cs: * LCIMMessage.cs: * Program.cs: * LCConnection.cs: * Conversation.cs: * LCIMFileMessage.cs: * Messages2Proto.cs: * LCIMTextMessage.cs: * LCIMTypedMessage.cs: * LCIMAudioMessage.cs: * LCIMImageMessage.cs: * LCApplicationRealtimeExt.cs: * LCIMChatRoom.cs: * messages2.proto.orig: * Realtime.Test.csproj: * LCIMLocationMessage.cs: * LCRTMServer.cs: * LCRTMRouter.cs: * LCIMRecalledMessage.cs: * compile-client-proto.sh: * LCIMConversation.cs: * RealtimeConsole.csproj: * LCIMConversationQuery.cs: * AssemblyInfo.cs: * LCWebSocketClient.cs: * LCIMTemporaryConversation.cs: * LCIMConversationMemberInfo.cs: * packages.config: chore: protobuf, websocket, converstion
parent
57b1a59cd0
commit
bf2af41565
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMChatRoom : LCIMConversation {
|
||||||
|
public LCIMChatRoom() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMConversation {
|
||||||
|
public string Id {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreatorId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> MemberIdList {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime CreatedAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime UpdatedAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMute => false;
|
||||||
|
|
||||||
|
public virtual bool IsSystem => false;
|
||||||
|
|
||||||
|
public virtual bool IsTransient => false;
|
||||||
|
|
||||||
|
public LCIMConversation() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(string key, object value) {
|
||||||
|
// 自定义属性
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> Count() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> Save() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Add(List<string> clientIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Remove(List<string> removeIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> Join() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> Quit() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMMessage> Send(LCIMMessage message) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMRecalledMessage> Recall(LCIMMessage message) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> Mute() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> Unmute() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MuteMemberList(List<string> clientIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnmuteMemberList(List<string> clientIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task BlockMemberList(List<string> clientIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnblockMemberList(List<string> clientIdList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMMessage> Update(LCIMMessage oldMessage, LCIMMessage newMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> UpdateMemberRole(string memberId, string role) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversationMemberInfo> GetMemberInfo(string memberId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<LCIMConversationMemberInfo>> GetAllMemberInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMConversationMemberInfo {
|
||||||
|
public string ConversationId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string MemberId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOwner {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Role {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMConversationQuery {
|
||||||
|
public LCIMConversationQuery() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMTemporaryConversation : LCIMConversation {
|
||||||
|
public LCIMTemporaryConversation() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using LeanCloud.Realtime.Protocol;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime.Internal {
|
||||||
|
internal class LCConnection {
|
||||||
|
private const int KEEP_ALIVE_INTERVAL = 10;
|
||||||
|
private const int RECV_BUFFER_SIZE = 1024;
|
||||||
|
|
||||||
|
private ClientWebSocket ws;
|
||||||
|
|
||||||
|
private volatile int requestI = 1;
|
||||||
|
|
||||||
|
private readonly object requestILock = new object();
|
||||||
|
|
||||||
|
private readonly Dictionary<int, TaskCompletionSource<GenericCommand>> responses;
|
||||||
|
|
||||||
|
internal LCConnection() {
|
||||||
|
responses = new Dictionary<int, TaskCompletionSource<GenericCommand>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task Connect() {
|
||||||
|
ws = new ClientWebSocket();
|
||||||
|
ws.Options.AddSubProtocol("lc.protobuf2.3");
|
||||||
|
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL);
|
||||||
|
await ws.ConnectAsync(new Uri(""), default);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task SendRequest(GenericCommand request) {
|
||||||
|
request.I = RequestI;
|
||||||
|
ArraySegment<byte> bytes = new ArraySegment<byte>(request.ToByteArray());
|
||||||
|
try {
|
||||||
|
await ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO 发送消息异常
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task Close() {
|
||||||
|
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "1", default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartReceive() {
|
||||||
|
byte[] buffer = new byte[RECV_BUFFER_SIZE];
|
||||||
|
try {
|
||||||
|
while (ws.State == WebSocketState.Open) {
|
||||||
|
byte[] data = new byte[0];
|
||||||
|
WebSocketReceiveResult result;
|
||||||
|
do {
|
||||||
|
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), default);
|
||||||
|
if (result.MessageType == WebSocketMessageType.Close) {
|
||||||
|
// TODO 区分主动断开和被动断开
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 拼合 WebSocket Frame
|
||||||
|
byte[] oldData = data;
|
||||||
|
data = new byte[data.Length + result.Count];
|
||||||
|
Array.Copy(oldData, data, oldData.Length);
|
||||||
|
Array.Copy(buffer, 0, data, oldData.Length, result.Count);
|
||||||
|
} while (!result.EndOfMessage);
|
||||||
|
try {
|
||||||
|
GenericCommand command = GenericCommand.Parser.ParseFrom(data);
|
||||||
|
HandleCommand(command);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析消息错误
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO 连接断开
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleCommand(GenericCommand command) {
|
||||||
|
if (command.HasI) {
|
||||||
|
// 应答
|
||||||
|
if (responses.TryGetValue(command.I, out TaskCompletionSource<GenericCommand> tcs)) {
|
||||||
|
if (command.HasErrorMessage) {
|
||||||
|
// 错误
|
||||||
|
ErrorCommand error = command.ErrorMessage;
|
||||||
|
int code = error.Code;
|
||||||
|
string detail = error.Detail;
|
||||||
|
// TODO 包装成异常抛出
|
||||||
|
LCException exception = new LCException(code, detail);
|
||||||
|
tcs.SetException(exception);
|
||||||
|
} else {
|
||||||
|
tcs.SetResult(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 通知
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int RequestI {
|
||||||
|
get {
|
||||||
|
lock (requestILock) {
|
||||||
|
return requestI++;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net.Http;
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime.Internal.Router {
|
||||||
|
internal class LCRTMRouter {
|
||||||
|
private LCRTMServer rtmServer;
|
||||||
|
|
||||||
|
internal LCRTMRouter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<string> GetServer() {
|
||||||
|
if (rtmServer == null || !rtmServer.IsValid) {
|
||||||
|
await Fetch();
|
||||||
|
}
|
||||||
|
return rtmServer.Server;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<LCRTMServer> Fetch() {
|
||||||
|
string server = await LCApplication.AppRouter.GetRealtimeServer();
|
||||||
|
string url = $"{server}/v1/route?appId={LCApplication.AppId}&secure=1";
|
||||||
|
|
||||||
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
|
RequestUri = new Uri(url),
|
||||||
|
Method = HttpMethod.Get
|
||||||
|
};
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
LCHttpUtils.PrintRequest(client, request);
|
||||||
|
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
request.Dispose();
|
||||||
|
|
||||||
|
string resultString = await response.Content.ReadAsStringAsync();
|
||||||
|
response.Dispose();
|
||||||
|
LCHttpUtils.PrintResponse(response, resultString);
|
||||||
|
|
||||||
|
rtmServer = JsonConvert.DeserializeObject<LCRTMServer>(resultString);
|
||||||
|
|
||||||
|
return rtmServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime.Internal.Router {
|
||||||
|
internal class LCRTMServer {
|
||||||
|
[JsonProperty("groupId")]
|
||||||
|
internal string GroupId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("groupUrl")]
|
||||||
|
internal string GroupUrl {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("server")]
|
||||||
|
internal string Server {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("secondary")]
|
||||||
|
internal string Secondary {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("ttl")]
|
||||||
|
internal int Ttl {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset createdAt;
|
||||||
|
|
||||||
|
internal LCRTMServer() {
|
||||||
|
createdAt = DateTimeOffset.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsValid => DateTimeOffset.Now < createdAt + TimeSpan.FromSeconds(Ttl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using LeanCloud.Realtime.Protocol;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
using LeanCloud.Realtime.Internal.Router;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
|
internal class LCWebSocketClient {
|
||||||
|
private const int KEEP_ALIVE_INTERVAL = 10;
|
||||||
|
private const int RECV_BUFFER_SIZE = 1024;
|
||||||
|
|
||||||
|
private ClientWebSocket ws;
|
||||||
|
|
||||||
|
private volatile int requestI = 1;
|
||||||
|
|
||||||
|
private readonly object requestILock = new object();
|
||||||
|
|
||||||
|
private Dictionary<int, TaskCompletionSource<GenericCommand>> responses;
|
||||||
|
|
||||||
|
internal Action<GenericCommand> OnNotification {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal LCWebSocketClient() {
|
||||||
|
responses = new Dictionary<int, TaskCompletionSource<GenericCommand>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task Connect() {
|
||||||
|
LCRTMRouter rtmRouter = new LCRTMRouter();
|
||||||
|
string rtmServer = await rtmRouter.GetServer();
|
||||||
|
|
||||||
|
ws = new ClientWebSocket();
|
||||||
|
ws.Options.AddSubProtocol("lc.protobuf2.3");
|
||||||
|
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL);
|
||||||
|
await ws.ConnectAsync(new Uri(rtmServer), default);
|
||||||
|
_ = StartReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Task<GenericCommand> SendRequest(GenericCommand request) {
|
||||||
|
TaskCompletionSource<GenericCommand> tcs = new TaskCompletionSource<GenericCommand>();
|
||||||
|
request.I = RequestI;
|
||||||
|
responses.Add(request.I, tcs);
|
||||||
|
LCLogger.Debug($"=> {request.Cmd}/{request.Op}: {request.ToString()}");
|
||||||
|
ArraySegment<byte> bytes = new ArraySegment<byte>(request.ToByteArray());
|
||||||
|
try {
|
||||||
|
ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO 发送消息异常
|
||||||
|
|
||||||
|
}
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task Close() {
|
||||||
|
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "1", default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartReceive() {
|
||||||
|
byte[] buffer = new byte[RECV_BUFFER_SIZE];
|
||||||
|
try {
|
||||||
|
while (ws.State == WebSocketState.Open) {
|
||||||
|
byte[] data = new byte[0];
|
||||||
|
WebSocketReceiveResult result;
|
||||||
|
do {
|
||||||
|
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), default);
|
||||||
|
if (result.MessageType == WebSocketMessageType.Close) {
|
||||||
|
// TODO 区分主动断开和被动断开
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 拼合 WebSocket Frame
|
||||||
|
byte[] oldData = data;
|
||||||
|
data = new byte[data.Length + result.Count];
|
||||||
|
Array.Copy(oldData, data, oldData.Length);
|
||||||
|
Array.Copy(buffer, 0, data, oldData.Length, result.Count);
|
||||||
|
} while (!result.EndOfMessage);
|
||||||
|
try {
|
||||||
|
GenericCommand command = GenericCommand.Parser.ParseFrom(data);
|
||||||
|
LCLogger.Debug($"<= {command.Cmd}/{command.Op}: {command.ToString()}");
|
||||||
|
HandleCommand(command);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析消息错误
|
||||||
|
LCLogger.Error(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO 连接断开
|
||||||
|
LCLogger.Error(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleCommand(GenericCommand command) {
|
||||||
|
if (command.HasI) {
|
||||||
|
// 应答
|
||||||
|
if (responses.TryGetValue(command.I, out TaskCompletionSource<GenericCommand> tcs)) {
|
||||||
|
if (command.HasErrorMessage) {
|
||||||
|
// 错误
|
||||||
|
ErrorCommand error = command.ErrorMessage;
|
||||||
|
int code = error.Code;
|
||||||
|
string detail = error.Detail;
|
||||||
|
// TODO 包装成异常抛出
|
||||||
|
LCException exception = new LCException(code, detail);
|
||||||
|
tcs.SetException(exception);
|
||||||
|
} else {
|
||||||
|
tcs.SetResult(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 通知
|
||||||
|
OnNotification?.Invoke(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int RequestI {
|
||||||
|
get {
|
||||||
|
lock (requestILock) {
|
||||||
|
return requestI++;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud.Realtime;
|
||||||
|
using LeanCloud.Realtime.Internal;
|
||||||
|
|
||||||
|
namespace LeanCloud {
|
||||||
|
public static class LCApplicationRealtimeExt {
|
||||||
|
static LCConnection connection;
|
||||||
|
|
||||||
|
public static async Task<LCIMClient> CreateIMClient(this LCApplication application, string clientId) {
|
||||||
|
if (string.IsNullOrEmpty(clientId)) {
|
||||||
|
throw new ArgumentNullException(nameof(clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
LCIMClient client = new LCIMClient(clientId);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
using LeanCloud.Realtime.Internal.WebSocket;
|
||||||
|
using LeanCloud.Realtime.Protocol;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMClient {
|
||||||
|
private string clientId;
|
||||||
|
|
||||||
|
private LCWebSocketClient client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前用户被加入某个对话的黑名单
|
||||||
|
/// </summary>
|
||||||
|
public Action OnBlocked {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前客户端被服务端强行下线
|
||||||
|
/// </summary>
|
||||||
|
public Action OnClosed {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户端连接断开
|
||||||
|
/// </summary>
|
||||||
|
public Action OnDisconnected {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户端连接恢复正常
|
||||||
|
/// </summary>
|
||||||
|
public Action OnReconnect {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前用户被添加至某个对话
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, string> OnInvited {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前用户被从某个对话中移除
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, string> OnKicked {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 有用户被添加至某个对话
|
||||||
|
/// </summary>
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersJoined {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<LCIMConversation, List<string>, string> OnMembersLeft {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCIMClient(string clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Open() {
|
||||||
|
client = new LCWebSocketClient {
|
||||||
|
OnNotification = OnNotification
|
||||||
|
};
|
||||||
|
await client.Connect();
|
||||||
|
// Open Session
|
||||||
|
GenericCommand command = NewCommand(CommandType.Session, OpType.Open);
|
||||||
|
command.SessionMessage = new SessionCommand();
|
||||||
|
await client.SendRequest(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMChatRoom> CreateChatRoom(
|
||||||
|
string name,
|
||||||
|
Dictionary<string, object> properties = null) {
|
||||||
|
LCIMChatRoom chatRoom = await CreateConv(name: name, transient: true, properties: properties) as LCIMChatRoom;
|
||||||
|
return chatRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> CreateConversation(
|
||||||
|
IEnumerable<string> members,
|
||||||
|
string name = null,
|
||||||
|
bool unique = true,
|
||||||
|
Dictionary<string, object> properties = null) {
|
||||||
|
return await CreateConv(members: members, name: name, unique: unique, properties: properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMTemporaryConversation> CreateTemporaryConversation(
|
||||||
|
IEnumerable<string> members,
|
||||||
|
int ttl = 86400,
|
||||||
|
Dictionary<string, object> properties = null) {
|
||||||
|
LCIMTemporaryConversation tempConversation = await CreateConv(members: members, temporary: true, temporaryTtl: ttl, properties: properties) as LCIMTemporaryConversation;
|
||||||
|
return tempConversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<LCIMConversation> CreateConv(
|
||||||
|
IEnumerable<string> members = null,
|
||||||
|
string name = null,
|
||||||
|
bool transient = false,
|
||||||
|
bool unique = true,
|
||||||
|
bool temporary = false,
|
||||||
|
int temporaryTtl = 86400,
|
||||||
|
Dictionary<string, object> properties = null) {
|
||||||
|
GenericCommand command = NewCommand(CommandType.Conv, OpType.Start);
|
||||||
|
ConvCommand conv = new ConvCommand {
|
||||||
|
Transient = transient,
|
||||||
|
Unique = unique,
|
||||||
|
TempConv = temporary,
|
||||||
|
TempConvTTL = temporaryTtl
|
||||||
|
};
|
||||||
|
if (members != null) {
|
||||||
|
conv.M.AddRange(members);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(name)) {
|
||||||
|
conv.N = name;
|
||||||
|
}
|
||||||
|
if (properties != null) {
|
||||||
|
conv.Attr = new JsonObjectMessage {
|
||||||
|
Data = JsonConvert.SerializeObject(properties)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
command.ConvMessage = conv;
|
||||||
|
GenericCommand response = await client.SendRequest(command);
|
||||||
|
// TODO 实例化对话对象
|
||||||
|
|
||||||
|
LCIMConversation conversation = new LCIMConversation();
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversation> GetConversation(string id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<LCIMConversation>> GetConversationList(List<string> idList) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCIMConversationQuery> GetConversationQuery() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNotification(GenericCommand notification) {
|
||||||
|
switch (notification.Cmd) {
|
||||||
|
case CommandType.Conv:
|
||||||
|
OnConversationNotification(notification);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConversationNotification(GenericCommand notification) {
|
||||||
|
switch (notification.Op) {
|
||||||
|
case OpType.Joined:
|
||||||
|
OnConversationJoined(notification.ConvMessage);
|
||||||
|
break;
|
||||||
|
case OpType.MembersJoined:
|
||||||
|
OnConversationMembersJoined(notification.ConvMessage);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConversationJoined(ConvCommand conv) {
|
||||||
|
OnInvited?.Invoke(null, conv.InitBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConversationMembersJoined(ConvCommand conv) {
|
||||||
|
OnMembersJoined?.Invoke(null, conv.M.ToList(), conv.InitBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenericCommand NewCommand(CommandType cmd, OpType op) {
|
||||||
|
return new GenericCommand {
|
||||||
|
Cmd = cmd,
|
||||||
|
Op = op,
|
||||||
|
AppId = LCApplication.AppId,
|
||||||
|
PeerId = clientId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime.Message {
|
||||||
|
public class LCIMAudioMessage {
|
||||||
|
public LCIMAudioMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMFileMessage {
|
||||||
|
public LCIMFileMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime.Message {
|
||||||
|
public class LCIMImageMessage {
|
||||||
|
public LCIMImageMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime.Message {
|
||||||
|
public class LCIMLocationMessage {
|
||||||
|
public LCIMLocationMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMMessage {
|
||||||
|
public string ConversationId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FromClientId {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SentTimestamp {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime SentAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DeliveredTimestamp {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime DeliveredAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadTimestamp {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime ReadAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PatchedTimestamp {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime PatchedAt {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> MentionList {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCIMMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMRecalledMessage {
|
||||||
|
public LCIMRecalledMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LeanCloud.Realtime {
|
||||||
|
public class LCIMTextMessage {
|
||||||
|
public LCIMTextMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System;
|
||||||
|
namespace LeanCloud.Realtime.Message {
|
||||||
|
public class LCIMTypedMessage {
|
||||||
|
public LCIMTypedMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<ReleaseVersion>0.1.0</ReleaseVersion>
|
||||||
|
<RootNamespace>LeanCloud.Realtime</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Storage\Storage.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Google.Protobuf" Version="3.11.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Internal\" />
|
||||||
|
<Folder Include="Internal\Router\" />
|
||||||
|
<Folder Include="Conversation\" />
|
||||||
|
<Folder Include="Message\" />
|
||||||
|
<Folder Include="Internal\WebSocket\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
||||||
|
protoc --proto_path=. --csharp_out=. messages2.proto.orig
|
|
@ -0,0 +1,484 @@
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package push_server.messages2;
|
||||||
|
option csharp_namespace = "LeanCloud.Realtime.Protocol";
|
||||||
|
|
||||||
|
// note that this line will be removed by out build script until we
|
||||||
|
// finally upgraded to protobuffer 3
|
||||||
|
option objc_class_prefix = "AVIM";
|
||||||
|
|
||||||
|
enum CommandType {
|
||||||
|
session = 0;
|
||||||
|
conv = 1;
|
||||||
|
direct = 2;
|
||||||
|
ack = 3;
|
||||||
|
rcp = 4;
|
||||||
|
unread = 5;
|
||||||
|
logs = 6;
|
||||||
|
error = 7;
|
||||||
|
login = 8;
|
||||||
|
data = 9;
|
||||||
|
room = 10;
|
||||||
|
read = 11;
|
||||||
|
presence = 12;
|
||||||
|
report = 13;
|
||||||
|
echo = 14;
|
||||||
|
loggedin = 15;
|
||||||
|
logout = 16;
|
||||||
|
loggedout = 17;
|
||||||
|
patch = 18;
|
||||||
|
pubsub = 19;
|
||||||
|
blacklist = 20;
|
||||||
|
goaway = 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OpType {
|
||||||
|
// session
|
||||||
|
open = 1;
|
||||||
|
add = 2;
|
||||||
|
remove = 3;
|
||||||
|
close = 4;
|
||||||
|
opened = 5;
|
||||||
|
closed = 6;
|
||||||
|
query = 7;
|
||||||
|
query_result = 8;
|
||||||
|
conflict = 9;
|
||||||
|
added = 10;
|
||||||
|
removed = 11;
|
||||||
|
refresh = 12;
|
||||||
|
refreshed = 13;
|
||||||
|
|
||||||
|
// conv
|
||||||
|
start = 30;
|
||||||
|
started = 31;
|
||||||
|
joined = 32;
|
||||||
|
members_joined = 33;
|
||||||
|
// add = 34; reuse session.add
|
||||||
|
// added = 35; reuse session.added
|
||||||
|
// remove = 37; reuse session.remove
|
||||||
|
// removed = 38; reuse session.removed
|
||||||
|
left = 39;
|
||||||
|
members_left = 40;
|
||||||
|
// query = 41; reuse session.query
|
||||||
|
results = 42;
|
||||||
|
count = 43;
|
||||||
|
result = 44;
|
||||||
|
update = 45;
|
||||||
|
updated = 46;
|
||||||
|
mute = 47;
|
||||||
|
unmute = 48;
|
||||||
|
status = 49;
|
||||||
|
members = 50;
|
||||||
|
max_read = 51;
|
||||||
|
is_member = 52;
|
||||||
|
member_info_update = 53;
|
||||||
|
member_info_updated = 54;
|
||||||
|
member_info_changed = 55;
|
||||||
|
|
||||||
|
// room
|
||||||
|
join = 80;
|
||||||
|
invite = 81;
|
||||||
|
leave = 82;
|
||||||
|
kick = 83;
|
||||||
|
reject = 84;
|
||||||
|
invited = 85;
|
||||||
|
// joined = 32; reuse the value in conv section
|
||||||
|
// left = 39; reuse the value in conv section
|
||||||
|
kicked = 86;
|
||||||
|
// members-joined = 33; reuse the value in conv section
|
||||||
|
// members-left = 40; reuse the value in conv section
|
||||||
|
|
||||||
|
// report
|
||||||
|
upload = 100;
|
||||||
|
uploaded = 101;
|
||||||
|
|
||||||
|
// pubsub
|
||||||
|
subscribe = 120;
|
||||||
|
subscribed = 121;
|
||||||
|
unsubscribe = 122;
|
||||||
|
unsubscribed = 123;
|
||||||
|
is_subscribed = 124;
|
||||||
|
|
||||||
|
// patch
|
||||||
|
modify = 150;
|
||||||
|
modified = 151;
|
||||||
|
|
||||||
|
// blacklist, query, query_result defined with 7, 8
|
||||||
|
block = 170;
|
||||||
|
unblock = 171;
|
||||||
|
blocked = 172;
|
||||||
|
unblocked = 173;
|
||||||
|
members_blocked = 174;
|
||||||
|
members_unblocked = 175;
|
||||||
|
check_block = 176;
|
||||||
|
check_result = 177;
|
||||||
|
|
||||||
|
add_shutup = 180;
|
||||||
|
remove_shutup = 181;
|
||||||
|
query_shutup = 182;
|
||||||
|
shutup_added = 183;
|
||||||
|
shutup_removed = 184;
|
||||||
|
shutup_result = 185;
|
||||||
|
shutuped = 186;
|
||||||
|
unshutuped = 187;
|
||||||
|
members_shutuped = 188;
|
||||||
|
members_unshutuped = 189;
|
||||||
|
check_shutup = 190; // check_result define in 177
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatusType {
|
||||||
|
on = 1;
|
||||||
|
off = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DeviceType {
|
||||||
|
unknown = 0;
|
||||||
|
android = 1;
|
||||||
|
ios = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SemanticVersion {
|
||||||
|
optional int32 major = 1;
|
||||||
|
optional int32 minor = 2;
|
||||||
|
optional int32 patch = 3;
|
||||||
|
optional string preRelease = 4;
|
||||||
|
optional string build = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AndroidVersion {
|
||||||
|
optional string codename = 1;
|
||||||
|
optional string apiLevel = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SystemInfo {
|
||||||
|
optional DeviceType deviceType = 1;
|
||||||
|
optional SemanticVersion osVersion = 2;
|
||||||
|
optional AndroidVersion androidVersion = 3;
|
||||||
|
optional bool isEmulator = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JsonObjectMessage {
|
||||||
|
required string data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnreadTuple {
|
||||||
|
required string cid = 1;
|
||||||
|
required int32 unread = 2;
|
||||||
|
optional string mid = 3;
|
||||||
|
optional int64 timestamp = 4;
|
||||||
|
optional string from = 5;
|
||||||
|
optional string data = 6;
|
||||||
|
optional int64 patchTimestamp = 7;
|
||||||
|
optional bool mentioned = 8;
|
||||||
|
optional bytes binaryMsg = 9;
|
||||||
|
optional int32 convType = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogItem {
|
||||||
|
optional string from = 1;
|
||||||
|
optional string data = 2;
|
||||||
|
optional int64 timestamp = 3;
|
||||||
|
optional string msgId = 4;
|
||||||
|
optional int64 ackAt = 5;
|
||||||
|
optional int64 readAt = 6;
|
||||||
|
optional int64 patchTimestamp = 7;
|
||||||
|
optional bool mentionAll = 8;
|
||||||
|
repeated string mentionPids = 9;
|
||||||
|
optional bool bin = 10;
|
||||||
|
optional int32 convType = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConvMemberInfo {
|
||||||
|
optional string pid = 1;
|
||||||
|
optional string role = 2;
|
||||||
|
optional string infoId = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoginCommand {
|
||||||
|
optional SystemInfo systemInfo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoggedinCommand {
|
||||||
|
optional bool pushDisabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DataCommand {
|
||||||
|
repeated string ids = 1;
|
||||||
|
repeated JsonObjectMessage msg = 2;
|
||||||
|
optional bool offline = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SessionCommand {
|
||||||
|
optional int64 t = 1;
|
||||||
|
optional string n = 2;
|
||||||
|
optional string s = 3;
|
||||||
|
optional string ua = 4;
|
||||||
|
optional bool r = 5;
|
||||||
|
optional string tag = 6;
|
||||||
|
optional string deviceId = 7;
|
||||||
|
repeated string sessionPeerIds = 8;
|
||||||
|
repeated string onlineSessionPeerIds = 9;
|
||||||
|
optional string st = 10;
|
||||||
|
optional int32 stTtl = 11;
|
||||||
|
optional int32 code = 12;
|
||||||
|
optional string reason = 13;
|
||||||
|
optional string deviceToken = 14;
|
||||||
|
optional bool sp = 15;
|
||||||
|
optional string detail = 16;
|
||||||
|
optional int64 lastUnreadNotifTime = 17;
|
||||||
|
optional int64 lastPatchTime = 18;
|
||||||
|
optional int64 configBitmap = 19;
|
||||||
|
optional SystemInfo systemInfo = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ErrorCommand {
|
||||||
|
required int32 code = 1;
|
||||||
|
required string reason = 2;
|
||||||
|
optional int32 appCode = 3;
|
||||||
|
optional string detail = 4;
|
||||||
|
repeated string pids = 5;
|
||||||
|
optional string appMsg = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DirectCommand {
|
||||||
|
optional string msg = 1;
|
||||||
|
optional string uid = 2;
|
||||||
|
optional string fromPeerId = 3;
|
||||||
|
optional int64 timestamp = 4;
|
||||||
|
optional bool offline = 5;
|
||||||
|
optional bool hasMore = 6;
|
||||||
|
repeated string toPeerIds = 7;
|
||||||
|
optional bool r = 10;
|
||||||
|
optional string cid = 11;
|
||||||
|
optional string id = 12;
|
||||||
|
optional bool transient = 13;
|
||||||
|
optional string dt = 14;
|
||||||
|
optional string roomId = 15;
|
||||||
|
optional string pushData = 16;
|
||||||
|
optional bool will = 17;
|
||||||
|
optional int64 patchTimestamp = 18;
|
||||||
|
optional bytes binaryMsg = 19;
|
||||||
|
repeated string mentionPids = 20;
|
||||||
|
optional bool mentionAll = 21;
|
||||||
|
optional int32 convType = 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AckCommand {
|
||||||
|
optional int32 code = 1;
|
||||||
|
optional string reason = 2;
|
||||||
|
optional string mid = 3;
|
||||||
|
optional string cid = 4;
|
||||||
|
optional int64 t = 5;
|
||||||
|
optional string uid = 6;
|
||||||
|
optional int64 fromts = 7;
|
||||||
|
optional int64 tots = 8;
|
||||||
|
optional string type = 9;
|
||||||
|
repeated string ids = 10;
|
||||||
|
optional int32 appCode = 11;
|
||||||
|
optional string appMsg = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnreadCommand {
|
||||||
|
repeated UnreadTuple convs = 1;
|
||||||
|
optional int64 notifTime = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConvCommand {
|
||||||
|
repeated string m = 1;
|
||||||
|
optional bool transient = 2;
|
||||||
|
optional bool unique = 3;
|
||||||
|
optional string cid = 4;
|
||||||
|
optional string cdate = 5;
|
||||||
|
optional string initBy = 6;
|
||||||
|
optional string sort = 7;
|
||||||
|
optional int32 limit = 8;
|
||||||
|
optional int32 skip = 9;
|
||||||
|
optional int32 flag = 10;
|
||||||
|
optional int32 count = 11;
|
||||||
|
optional string udate = 12;
|
||||||
|
optional int64 t = 13;
|
||||||
|
optional string n = 14;
|
||||||
|
optional string s = 15;
|
||||||
|
|
||||||
|
optional bool statusSub = 16;
|
||||||
|
optional bool statusPub = 17;
|
||||||
|
optional int32 statusTTL = 18;
|
||||||
|
optional string uniqueId = 19;
|
||||||
|
|
||||||
|
optional string targetClientId = 20;
|
||||||
|
optional int64 maxReadTimestamp = 21;
|
||||||
|
optional int64 maxAckTimestamp = 22;
|
||||||
|
optional bool queryAllMembers = 23;
|
||||||
|
repeated MaxReadTuple maxReadTuples = 24;
|
||||||
|
repeated string cids = 25;
|
||||||
|
|
||||||
|
optional ConvMemberInfo info = 26;
|
||||||
|
|
||||||
|
optional bool tempConv = 27;
|
||||||
|
optional int32 tempConvTTL = 28;
|
||||||
|
repeated string tempConvIds = 29;
|
||||||
|
|
||||||
|
repeated string allowedPids = 30;
|
||||||
|
repeated ErrorCommand failedPids = 31;
|
||||||
|
|
||||||
|
// used in shutup query
|
||||||
|
optional string next = 40;
|
||||||
|
|
||||||
|
optional JsonObjectMessage results = 100;
|
||||||
|
optional JsonObjectMessage where = 101;
|
||||||
|
optional JsonObjectMessage attr = 103;
|
||||||
|
optional JsonObjectMessage attrModified = 104;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoomCommand {
|
||||||
|
optional string roomId = 1;
|
||||||
|
optional string s = 2;
|
||||||
|
optional int64 t = 3;
|
||||||
|
optional string n = 4;
|
||||||
|
optional bool transient = 5;
|
||||||
|
repeated string roomPeerIds = 6;
|
||||||
|
optional string byPeerId = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogsCommand {
|
||||||
|
optional string cid = 1;
|
||||||
|
optional int32 l = 2;
|
||||||
|
optional int32 limit = 3;
|
||||||
|
optional int64 t = 4;
|
||||||
|
optional int64 tt = 5;
|
||||||
|
optional string tmid = 6;
|
||||||
|
optional string mid = 7;
|
||||||
|
optional string checksum = 8;
|
||||||
|
optional bool stored = 9;
|
||||||
|
enum QueryDirection {
|
||||||
|
OLD = 1;
|
||||||
|
NEW = 2;
|
||||||
|
}
|
||||||
|
optional QueryDirection direction = 10 [default = OLD];
|
||||||
|
optional bool tIncluded = 11;
|
||||||
|
optional bool ttIncluded = 12;
|
||||||
|
optional int32 lctype = 13;
|
||||||
|
|
||||||
|
repeated LogItem logs = 105;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RcpCommand {
|
||||||
|
optional string id = 1;
|
||||||
|
optional string cid = 2;
|
||||||
|
optional int64 t = 3;
|
||||||
|
optional bool read = 4;
|
||||||
|
optional string from = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReadTuple {
|
||||||
|
required string cid = 1;
|
||||||
|
optional int64 timestamp = 2;
|
||||||
|
optional string mid = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MaxReadTuple {
|
||||||
|
optional string pid = 1;
|
||||||
|
optional int64 maxAckTimestamp = 2;
|
||||||
|
optional int64 maxReadTimestamp = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReadCommand {
|
||||||
|
optional string cid = 1;
|
||||||
|
repeated string cids = 2;
|
||||||
|
repeated ReadTuple convs = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PresenceCommand {
|
||||||
|
optional StatusType status = 1;
|
||||||
|
repeated string sessionPeerIds = 2;
|
||||||
|
optional string cid = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReportCommand {
|
||||||
|
optional bool initiative = 1;
|
||||||
|
optional string type = 2;
|
||||||
|
optional string data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PatchItem {
|
||||||
|
optional string cid = 1;
|
||||||
|
optional string mid = 2;
|
||||||
|
optional int64 timestamp = 3;
|
||||||
|
optional bool recall = 4;
|
||||||
|
optional string data = 5;
|
||||||
|
optional int64 patchTimestamp = 6;
|
||||||
|
optional string from = 7;
|
||||||
|
optional bytes binaryMsg = 8;
|
||||||
|
optional bool mentionAll = 9;
|
||||||
|
repeated string mentionPids = 10;
|
||||||
|
optional int64 patchCode = 11;
|
||||||
|
optional string patchReason = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PatchCommand {
|
||||||
|
repeated PatchItem patches = 1;
|
||||||
|
optional int64 lastPatchTime = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PubsubCommand {
|
||||||
|
optional string cid = 1;
|
||||||
|
repeated string cids = 2;
|
||||||
|
optional string topic = 3;
|
||||||
|
optional string subtopic = 4;
|
||||||
|
repeated string topics = 5;
|
||||||
|
repeated string subtopics = 6;
|
||||||
|
optional JsonObjectMessage results = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BlacklistCommand {
|
||||||
|
optional string srcCid = 1;
|
||||||
|
repeated string toPids = 2;
|
||||||
|
optional string srcPid = 3;
|
||||||
|
repeated string toCids = 4;
|
||||||
|
optional int32 limit = 5;
|
||||||
|
optional string next = 6;
|
||||||
|
|
||||||
|
repeated string blockedPids = 8;
|
||||||
|
repeated string blockedCids = 9;
|
||||||
|
|
||||||
|
repeated string allowedPids = 10;
|
||||||
|
repeated ErrorCommand failedPids = 11;
|
||||||
|
|
||||||
|
optional int64 t = 12;
|
||||||
|
optional string n = 13;
|
||||||
|
optional string s = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GenericCommand {
|
||||||
|
optional CommandType cmd = 1;
|
||||||
|
optional OpType op = 2;
|
||||||
|
|
||||||
|
optional string appId = 3;
|
||||||
|
optional string peerId = 4;
|
||||||
|
optional int32 i = 5;
|
||||||
|
optional string installationId = 6;
|
||||||
|
optional int32 priority = 7;
|
||||||
|
optional int32 service = 8;
|
||||||
|
optional int64 serverTs = 9;
|
||||||
|
optional int64 clientTs = 10;
|
||||||
|
optional int32 notificationType = 11;
|
||||||
|
|
||||||
|
optional LoginCommand loginMessage = 100;
|
||||||
|
optional DataCommand dataMessage = 101;
|
||||||
|
optional SessionCommand sessionMessage = 102;
|
||||||
|
optional ErrorCommand errorMessage = 103;
|
||||||
|
optional DirectCommand directMessage = 104;
|
||||||
|
optional AckCommand ackMessage = 105;
|
||||||
|
optional UnreadCommand unreadMessage = 106;
|
||||||
|
optional ReadCommand readMessage = 107;
|
||||||
|
optional RcpCommand rcpMessage = 108;
|
||||||
|
optional LogsCommand logsMessage = 109;
|
||||||
|
optional ConvCommand convMessage = 110;
|
||||||
|
optional RoomCommand roomMessage = 111;
|
||||||
|
optional PresenceCommand presenceMessage = 112;
|
||||||
|
optional ReportCommand reportMessage = 113;
|
||||||
|
optional PatchCommand patchMessage = 114;
|
||||||
|
optional PubsubCommand pubsubMessage = 115;
|
||||||
|
optional BlacklistCommand blacklistMessage = 116;
|
||||||
|
optional LoggedinCommand loggedinMessage = 117;
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using LeanCloud.Realtime;
|
||||||
|
|
||||||
|
namespace Realtime.Test {
|
||||||
|
public class Conversation {
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() {
|
||||||
|
LCLogger.LogDelegate += Utils.Print;
|
||||||
|
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown() {
|
||||||
|
LCLogger.LogDelegate -= Utils.Print;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task CreateConversation() {
|
||||||
|
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
|
string clientId = Guid.NewGuid().ToString();
|
||||||
|
LCIMClient client = new LCIMClient(clientId);
|
||||||
|
|
||||||
|
await client.Open();
|
||||||
|
|
||||||
|
client.OnInvited = (conv, initBy) => {
|
||||||
|
TestContext.WriteLine($"on invited: {initBy}");
|
||||||
|
};
|
||||||
|
|
||||||
|
client.OnMembersJoined = (conv, memberList, initBy) => {
|
||||||
|
TestContext.WriteLine($"on members joined: {initBy}");
|
||||||
|
tcs.SetResult(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
List<string> memberIdList = new List<string> { "world" };
|
||||||
|
string name = Guid.NewGuid().ToString();
|
||||||
|
await client.CreateConversation(memberIdList, name: name, unique: false);
|
||||||
|
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task CreateChatRoom() {
|
||||||
|
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
|
string clientId = Guid.NewGuid().ToString();
|
||||||
|
LCIMClient client = new LCIMClient(clientId);
|
||||||
|
|
||||||
|
await client.Open();
|
||||||
|
|
||||||
|
client.OnInvited = (conv, initBy) => {
|
||||||
|
TestContext.WriteLine($"on invited: {initBy}");
|
||||||
|
tcs.SetResult(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
string name = Guid.NewGuid().ToString();
|
||||||
|
await client.CreateChatRoom(name);
|
||||||
|
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task CreateTemporaryConversation() {
|
||||||
|
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
|
string clientId = Guid.NewGuid().ToString();
|
||||||
|
LCIMClient client = new LCIMClient(clientId);
|
||||||
|
|
||||||
|
await client.Open();
|
||||||
|
|
||||||
|
client.OnInvited = (conv, initBy) => {
|
||||||
|
TestContext.WriteLine($"on invited: {initBy}");
|
||||||
|
};
|
||||||
|
|
||||||
|
client.OnMembersJoined = (conv, memberList, initBy) => {
|
||||||
|
TestContext.WriteLine($"on members joined: {initBy}");
|
||||||
|
tcs.SetResult(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
List<string> memberIdList = new List<string> { "world" };
|
||||||
|
await client.CreateTemporaryConversation(memberIdList);
|
||||||
|
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using LeanCloud.Realtime.Protocol;
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
namespace Realtime.Test {
|
||||||
|
public class Protobuf {
|
||||||
|
[Test]
|
||||||
|
public void Serialize() {
|
||||||
|
GenericCommand command = new GenericCommand {
|
||||||
|
Cmd = CommandType.Session,
|
||||||
|
Op = OpType.Open,
|
||||||
|
PeerId = "hello"
|
||||||
|
};
|
||||||
|
SessionCommand session = new SessionCommand {
|
||||||
|
Code = 123
|
||||||
|
};
|
||||||
|
command.SessionMessage = session;
|
||||||
|
byte[] bytes = command.ToByteArray();
|
||||||
|
TestContext.WriteLine($"length: {bytes.Length}");
|
||||||
|
|
||||||
|
command = GenericCommand.Parser.ParseFrom(bytes);
|
||||||
|
Assert.AreEqual(command.Cmd, CommandType.Session);
|
||||||
|
Assert.AreEqual(command.Op, OpType.Open);
|
||||||
|
Assert.AreEqual(command.PeerId, "hello");
|
||||||
|
Assert.NotNull(command.SessionMessage);
|
||||||
|
Assert.AreEqual(command.SessionMessage.Code, 123);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<ReleaseVersion>0.1.0</ReleaseVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="nunit" Version="3.12.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Realtime\Realtime.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Storage\Storage.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Realtime.Test {
|
||||||
|
public static class Utils {
|
||||||
|
internal static void Print(LCLogLevel level, string info) {
|
||||||
|
switch (level) {
|
||||||
|
case LCLogLevel.Debug:
|
||||||
|
TestContext.Out.WriteLine($"[DEBUG] {info}");
|
||||||
|
break;
|
||||||
|
case LCLogLevel.Warn:
|
||||||
|
TestContext.Out.WriteLine($"[WARNING] {info}");
|
||||||
|
break;
|
||||||
|
case LCLogLevel.Error:
|
||||||
|
TestContext.Out.WriteLine($"[ERROR] {info}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TestContext.Out.WriteLine(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using LeanCloud.Realtime;
|
||||||
|
|
||||||
|
namespace RealtimeConsole {
|
||||||
|
class MainClass {
|
||||||
|
public static void Main(string[] args) {
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
|
||||||
|
Start();
|
||||||
|
|
||||||
|
Console.ReadKey(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Start() {
|
||||||
|
LCLogger.LogDelegate += (level, info) => {
|
||||||
|
switch (level) {
|
||||||
|
case LCLogLevel.Debug:
|
||||||
|
Console.WriteLine($"[DEBUG] {info}");
|
||||||
|
break;
|
||||||
|
case LCLogLevel.Warn:
|
||||||
|
Console.WriteLine($"[WARNING] {info}");
|
||||||
|
break;
|
||||||
|
case LCLogLevel.Error:
|
||||||
|
Console.WriteLine($"[ERROR] {info}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||||
|
|
||||||
|
LCIMClient client = new LCIMClient("hello123");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.Open();
|
||||||
|
Console.WriteLine($"End {Thread.CurrentThread.ManagedThreadId}");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.OnInvited = (conv, initBy) => {
|
||||||
|
Console.WriteLine($"on invited: {initBy}");
|
||||||
|
};
|
||||||
|
|
||||||
|
client.OnMembersJoined = (conv, memberList, initBy) => {
|
||||||
|
Console.WriteLine($"on members joined: {initBy}");
|
||||||
|
};
|
||||||
|
|
||||||
|
List<string> memberIdList = new List<string> { "world", "code" };
|
||||||
|
string name = Guid.NewGuid().ToString();
|
||||||
|
_ = await client.CreateTemporaryConversation(memberIdList);
|
||||||
|
//_ = await client.CreateChatRoom(name);
|
||||||
|
//_ = await client.CreateConversation(memberIdList, name: name, unique: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes.
|
||||||
|
// Change them to the values specific to your project.
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("RealtimeConsole")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
|
// The following attributes are used to specify the signing key for the assembly,
|
||||||
|
// if desired. See the Mono documentation for more information about signing.
|
||||||
|
|
||||||
|
//[assembly: AssemblyDelaySign(false)]
|
||||||
|
//[assembly: AssemblyKeyFile("")]
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{7C563EE9-D130-4681-88B8-4523A31F6017}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>RealtimeConsole</RootNamespace>
|
||||||
|
<AssemblyName>RealtimeConsole</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ExternalConsole>true</ExternalConsole>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ExternalConsole>true</ExternalConsole>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Buffers">
|
||||||
|
<HintPath>..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics.Vectors">
|
||||||
|
<HintPath>..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="mscorlib" />
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.Unsafe">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Memory">
|
||||||
|
<HintPath>..\..\packages\System.Memory.4.5.2\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Google.Protobuf">
|
||||||
|
<HintPath>..\..\packages\Google.Protobuf.3.11.4\lib\net45\Google.Protobuf.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Common\Common.csproj">
|
||||||
|
<Project>{758DE75D-37D7-4392-B564-9484348B505C}</Project>
|
||||||
|
<Name>Common</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\Realtime\Realtime.csproj">
|
||||||
|
<Project>{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}</Project>
|
||||||
|
<Name>Realtime</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\Storage\Storage.csproj">
|
||||||
|
<Project>{4DCA6CCF-DBD2-4184-9A7E-8775A024D194}</Project>
|
||||||
|
<Name>Storage</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Google.Protobuf" version="3.11.4" targetFramework="net472" />
|
||||||
|
<package id="System.Buffers" version="4.4.0" targetFramework="net472" />
|
||||||
|
<package id="System.Memory" version="4.5.2" targetFramework="net472" />
|
||||||
|
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" />
|
||||||
|
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net472" />
|
||||||
|
</packages>
|
|
@ -9,6 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage", "Storage\Storage.
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Test", "Test\Storage.Test\Storage.Test.csproj", "{531F8181-FFE0-476E-9D0A-93F13CAD1183}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Test", "Test\Storage.Test\Storage.Test.csproj", "{531F8181-FFE0-476E-9D0A-93F13CAD1183}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Realtime", "Realtime\Realtime.csproj", "{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Realtime.Test", "Test\Realtime.Test\Realtime.Test.csproj", "{746B0DE6-C504-4568-BA6D-4A08A91A5E35}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealtimeConsole", "Test\RealtimeConsole\RealtimeConsole.csproj", "{7C563EE9-D130-4681-88B8-4523A31F6017}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -27,9 +33,23 @@ Global
|
||||||
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Release|Any CPU.Build.0 = Release|Any CPU
|
{531F8181-FFE0-476E-9D0A-93F13CAD1183}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7084C9BD-6D26-4803-9E7F-A6D2E55D963A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{746B0DE6-C504-4568-BA6D-4A08A91A5E35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{746B0DE6-C504-4568-BA6D-4A08A91A5E35}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{746B0DE6-C504-4568-BA6D-4A08A91A5E35}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{746B0DE6-C504-4568-BA6D-4A08A91A5E35}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7C563EE9-D130-4681-88B8-4523A31F6017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7C563EE9-D130-4681-88B8-4523A31F6017}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7C563EE9-D130-4681-88B8-4523A31F6017}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7C563EE9-D130-4681-88B8-4523A31F6017}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{531F8181-FFE0-476E-9D0A-93F13CAD1183} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933}
|
{531F8181-FFE0-476E-9D0A-93F13CAD1183} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933}
|
||||||
|
{746B0DE6-C504-4568-BA6D-4A08A91A5E35} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933}
|
||||||
|
{7C563EE9-D130-4681-88B8-4523A31F6017} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(MonoDevelopProperties) = preSolution
|
GlobalSection(MonoDevelopProperties) = preSolution
|
||||||
version = 0.1.0
|
version = 0.1.0
|
||||||
|
|
Loading…
Reference in New Issue