Fixed issue with Horizontal list not refreshing. Added clientId to joinPartyResponse. Crashing on multiple user joins.

This commit is contained in:
watsonb8 2019-07-12 11:34:06 -04:00
parent 11a585ecc0
commit d78dce44f0
11 changed files with 173 additions and 54 deletions

View File

@ -25,10 +25,10 @@ namespace Aurora.Design.Components.HorizontalList
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource",
typeof(ObservableCollection<object>),
typeof(HorizontalList),
default(ObservableCollection<object>),
BindingMode.TwoWay,
returnType: typeof(ObservableCollection<object>),
declaringType: typeof(HorizontalList),
defaultValue: default(ObservableCollection<object>),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: ItemsSourceChanged);
public static readonly BindableProperty SelectedItemProperty =

View File

@ -14,7 +14,7 @@
<DataTemplate>
<Frame>
<Label
Text="{Binding Username}"/>
Text="{Binding UserName}"/>
</Frame>
</DataTemplate>
</hl:HorizontalList.ItemTemplate>

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Xamarin.Forms;
using Aurora.Proto.Party;
@ -9,10 +10,10 @@ namespace Aurora.Design.Components.MemberList
public partial class MemberList : ContentView
{
private static ObservableCollection<PartyMember> _newSource;
private static NotifyCollectionChangedEventHandler _collectionChangedHandler;
public MemberList()
{
InitializeComponent();
}
/// <summary>
@ -44,6 +45,7 @@ namespace Aurora.Design.Components.MemberList
}
}
/// <summary>
/// Memberes changed event handler. Assign member list source.
/// </summary>
@ -57,7 +59,56 @@ namespace Aurora.Design.Components.MemberList
if (membersList != null)
{
_newSource = newValue as ObservableCollection<PartyMember>;
membersList.ItemsSource = newValue as ObservableCollection<object>;
membersList.ItemsSource = new ObservableCollection<object>(_newSource);
//Setup collection changed listeners
//TODO evaluate for memory leak
_newSource.CollectionChanged += (sender, e) => HandleCollectionChanged(sender, e, bindable);
}
}
private static void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, BindableObject bindable)
{
MemberList self = bindable as MemberList;
var membersList = self.FindByName("MembersHorizontalList") as HorizontalList.HorizontalList;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
foreach (PartyMember member in e.NewItems)
{
membersList.ItemsSource.Add(member);
}
break;
}
case NotifyCollectionChangedAction.Remove:
{
foreach (PartyMember member in e.NewItems)
{
//Find all matches
var sourceMembers = membersList.ItemsSource.Where((object obj) =>
{
bool match = false;
if (obj is PartyMember)
{
PartyMember tmp = obj as PartyMember;
match = tmp.Id == member.Id;
}
return match;
});
//Remove found matches
foreach (object obj in sourceMembers)
{
membersList.ItemsSource.Remove(obj);
}
}
break;
}
}
}
}

View File

@ -3,7 +3,7 @@
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:hs="clr-namespace:Aurora.Design.Components.HostSelector"
xmlns:hl="clr-namespace:Aurora.Design.Components.HorizontalList"
xmlns:ml="clr-namespace:Aurora.Design.Components.MemberList"
xmlns:renderedViews="clr-namespace:Sharpnado.Presentation.Forms.RenderedViews;assembly=Sharpnado.Presentation.Forms"
xmlns:qu="clr-namespace:Aurora.Design.Components.Queue"
x:Class="Aurora.Design.Views.Party.PartyView">
@ -18,20 +18,9 @@
IsVisible="{Binding IsNotSelectingHost}">
<Label
Text="Party Members"/>
<hl:HorizontalList
x:Name="MembersHorizontalList"
ListOrientation="Horizontal"
VerticalOptions="Start"
ItemsSource="{Binding Members}">
<hl:HorizontalList.ItemTemplate>
<DataTemplate>
<Frame>
<Label
Text="{Binding UserName}"/>
</Frame>
</DataTemplate>
</hl:HorizontalList.ItemTemplate>
</hl:HorizontalList>
<ml:MemberList
VerticalOptions="FillAndExpand"
Members="{Binding Members}"/>
<Label
Text="Queue"/>
<qu:Queue/>

View File

@ -39,11 +39,8 @@ namespace Aurora.Design.Views.Party
{
this.JoinCommand = new Command(OnJoinExecute, CanJoinExecute);
this.HostCommand = new Command(OnHostExecute, CanHostExecute);
_members = new ObservableCollection<PartyMember>
{
new PartyMember{UserName = "asdf"}
};
_members = new ObservableCollection<PartyMember>();
SetState(PartyState.SelectingHost);
}
@ -142,17 +139,24 @@ namespace Aurora.Design.Views.Party
/// <returns></returns>
private async void JoinParty()
{
await _remotePartyClient.JoinPartyAsync(new JoinPartyRequest
JoinPartyResponse resp = await _remotePartyClient.JoinPartyAsync(new JoinPartyRequest
{
UserName = SettingsService.Instance.Username,
});
SettingsService.Instance.ClientId = resp.ClientId;
RefreshMembers();
//Subscribe to events
SubscribeRequest req = new SubscribeRequest();
req.EventTypes.Add(EventType.PartyMemberJoined);
req.EventTypes.Add(EventType.PartyMemberLeft);
if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId))
{
req.ClientId = SettingsService.Instance.ClientId;
}
try
{
_remoteEventsClient.SubscribeToEvents(req);
@ -202,11 +206,13 @@ namespace Aurora.Design.Views.Party
/// <returns></returns>
private async void GetEvents()
{
using (AsyncServerStreamingCall<BaseEvent> eventStream = _remoteEventsClient.GetEvents(new Empty()))
using (AsyncServerStreamingCall<BaseEvent> eventStream = _remoteEventsClient
.GetEvents(new EventsRequest { ClientId = SettingsService.Instance.ClientId }))
{
while (!_eventCancellationTokenSource.Token.IsCancellationRequested)
while (!_eventCancellationTokenSource.Token.IsCancellationRequested &&
await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token))
{
if (await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token))
try
{
//Convert derived event type
BaseEvent e = eventStream.ResponseStream.Current;
@ -233,6 +239,11 @@ namespace Aurora.Design.Views.Party
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine(string.Format("EXCEPTION --- " + ex.Message));
}
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace Aurora.Proto.Party
{
/// <summary>
/// Partial PartyMember class with a constructor that generates a new id
/// </summary>
public partial class PartyMember
{
public PartyMember(string id)
{
if (!string.IsNullOrWhiteSpace(id))
{
Id = id;
}
else
{
Id = Guid.NewGuid().ToString();
}
}
}
}

View File

@ -7,22 +7,29 @@ import "Proto/party.proto";
service RemoteEventService {
//Party Service
rpc GetEvents(Aurora.Proto.General.Empty) returns (stream BaseEvent) {};
rpc GetEvents(EventsRequest) returns (stream BaseEvent) {};
rpc SubscribeToEvents(SubscribeRequest) returns(SubscriptionResponse);
rpc UnsubscribeFromEvents(UnsubscribeRequest) returns (SubscriptionResponse);
rpc UnsubscribeFromAll(UnsubscribeAllRequest) returns (SubscriptionResponse);
}
message EventsRequest {
string clientId = 1;
}
/* Subscription messages */
message SubscribeRequest {
repeated EventType eventTypes = 1;
string clientId = 2;
}
message UnsubscribeRequest {
repeated EventType eventTypes = 1;
string clientId = 2;
}
message UnsubscribeAllRequest {
string clientId = 1;
}
message SubscriptionResponse {
@ -36,16 +43,18 @@ enum EventType {
}
message BaseEvent {
EventType eventType = 1;
string clientKey = 2;
oneof derivedEvent {
PartyMemberJoinedEvent partyMemberJoinedEvent = 2;
PartyMemberLeftEvent partyMemberLeftEvent = 3;
PartyMemberJoinedEvent partyMemberJoinedEvent = 3;
PartyMemberLeftEvent partyMemberLeftEvent = 4;
}
}
message PartyMemberJoinedEvent {
Aurora.Proto.Party.PartyMember member = 2;
Aurora.Proto.Party.PartyMember member = 3;
}
message PartyMemberLeftEvent {
Aurora.Proto.Party.PartyMember member = 2;
Aurora.Proto.Party.PartyMember member = 3;
}

View File

@ -17,6 +17,7 @@ message JoinPartyRequest {
message JoinPartyResponse {
PartyJoinedStatusEnum status = 1;
string clientId = 2;
}
message LeavePartyRequest {

View File

@ -22,17 +22,27 @@ namespace Aurora.RemoteImpl
/// <param name="responseStream">The response stream</param>
/// <param name="context">gRPC client context</param>
/// <returns></returns>
public async override Task GetEvents(Empty request, Grpc.Core.IServerStreamWriter<BaseEvent> responseStream, Grpc.Core.ServerCallContext context)
public async override Task GetEvents(EventsRequest request, Grpc.Core.IServerStreamWriter<BaseEvent> responseStream, Grpc.Core.ServerCallContext context)
{
while (EventManager.Instance.GetSubscriptionCount(context.Peer) > 0)
try
{
List<BaseEvent> events = EventManager.Instance.GetSessionEvents(context.Peer);
while (EventManager.Instance.GetSubscriptionCount(Combine(new string[] { context.Peer, request.ClientId })) > 0)
{
Console.WriteLine("Peer " + context.Peer);
//TODO this causes crashes when two or more members are connected
//TODO Change this to events based stream instead of a poll based...
List<BaseEvent> events = EventManager.Instance.GetSessionEvents(Combine(new string[] { context.Peer, request.ClientId }));
foreach (BaseEvent currentEvent in events)
{
await responseStream.WriteAsync(currentEvent);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught:" + ex.Message);
}
}
/// <summary>
/// RPC for subscribing to remote events
@ -42,7 +52,7 @@ namespace Aurora.RemoteImpl
/// <returns></returns>
public override Task<SubscriptionResponse> SubscribeToEvents(SubscribeRequest request, Grpc.Core.ServerCallContext context)
{
EventManager.Instance.AddSubscriptionList(context.Peer, request.EventTypes.ToList());
EventManager.Instance.AddSubscriptionList(Combine(new string[] { context.Peer, request.ClientId }), request.EventTypes.ToList());
return Task.FromResult(new SubscriptionResponse { Successful = true });
}
@ -58,7 +68,7 @@ namespace Aurora.RemoteImpl
EventType[] eventTypes = null;
request.EventTypes.CopyTo(eventTypes, 0);
EventManager.Instance.RemoveSubscriptionList(context.Peer, eventTypes.ToList());
EventManager.Instance.RemoveSubscriptionList(Combine(new string[] { context.Peer, request.ClientId }), eventTypes.ToList());
return Task.FromResult(new SubscriptionResponse { Successful = true });
}
@ -71,10 +81,28 @@ namespace Aurora.RemoteImpl
/// <returns></returns>
public override Task<SubscriptionResponse> UnsubscribeFromAll(UnsubscribeAllRequest request, Grpc.Core.ServerCallContext context)
{
EventManager.Instance.RemoveAllSubscriptions(context.Peer);
EventManager.Instance.RemoveAllSubscriptions(Combine(new string[] { context.Peer, request.ClientId }));
return Task.FromResult(new SubscriptionResponse { Successful = true });
}
private string Combine(string[] args)
{
string outString = "";
foreach (string arg in args)
{
if (arg == args.Last())
{
outString += arg;
}
else
{
outString += arg + ":";
}
}
return outString;
}
}
}

View File

@ -21,15 +21,6 @@ namespace Aurora.RemoteImpl
public RemotePartyServiceImpl()
{
_partyMembers = new ObservableCollection<PartyMember>();
//Add self to members list
_partyMembers.Add(new PartyMember
{
UserName = SettingsService.Instance.Username,
Id = "asdf",
IpAddress = ServerService.Instance.Hostname,
Port = ServerService.Instance.Port
});
}
public ObservableCollection<PartyMember> PartyMembers
@ -42,7 +33,7 @@ namespace Aurora.RemoteImpl
public override Task<JoinPartyResponse> JoinParty(JoinPartyRequest request, Grpc.Core.ServerCallContext context)
{
PartyMember partyMember = new PartyMember()
PartyMember partyMember = new PartyMember("")
{
UserName = request.UserName,
IpAddress = context.Host,
@ -61,7 +52,7 @@ namespace Aurora.RemoteImpl
EventManager.Instance.PushEvent(e);
JoinPartyResponse response = new JoinPartyResponse() { Status = PartyJoinedStatusEnum.Connected };
JoinPartyResponse response = new JoinPartyResponse() { Status = PartyJoinedStatusEnum.Connected, ClientId = partyMember.Id };
return Task.FromResult(response);
}

View File

@ -32,6 +32,10 @@ namespace Aurora.Services
}
}
/// <summary>
/// The user's username. This is persisted.
/// </summary>
/// <value></value>
public string Username
{
get { return AppSettings.GetValueOrDefault(_usernameKey, ""); }
@ -41,11 +45,24 @@ namespace Aurora.Services
}
}
/// <summary>
/// The default port to use. This is persisted.
/// </summary>
/// <value></value>
public int DefaultPort
{
get { return AppSettings.GetValueOrDefault(_defaultPortKey, 4005); }
set { AppSettings.AddOrUpdateValue(_defaultPortKey, value); }
}
/// <summary>
/// The current sessions clientId. This is assigned by the server. This is not persisted.
/// </summary>
/// <value></value>
public string ClientId
{
get; set;
}
}
}