refactoring

This commit is contained in:
Michele Scandura 2020-11-05 20:03:01 +00:00
parent c293490995
commit 4dfbcff460
14 changed files with 240 additions and 82 deletions

View File

@ -1,45 +1,39 @@
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Sledgemapper.Entities;
using Sledgemapper.Shared.Entities;
using Sledgemapper.Clients;
using MediatR;
namespace SignalRChat.Hubs
{
public class ChatHub : Hub
public class SledgemapperHub : Hub<ISledgemapperClient>
{
private readonly IMediator _mediator;
public SledgemapperHub(IMediator mediator) => _mediator = mediator;
private static Dictionary<string, SessionData> _sessions = new Dictionary<string, SessionData>();
public List<string> Colors = new List<string>{"CC0000",
"CC3300",
"FFCC00",
"009900",
"006666",
"0066FF",
"0000CC",
"663399",
"CC0099"};
public async Task SendMessage(string user, string message, string sessionName)
{
await Clients.Group(sessionName).SendAsync("ReceiveMessage", user, message);
}
"CC3300",
"FFCC00",
"009900",
"006666",
"0066FF",
"0000CC",
"663399",
"CC0099"};
public async Task NewTile(string sessionName, Tile tile)
{
var existingTile = _sessions[sessionName].Map.TryGetValue(tile.ToString(), out var t);
if (existingTile)
{
_sessions[sessionName].Map.TryRemove(t.ToString(), out var rtile);
}
_sessions[sessionName].Map.TryAdd(tile.ToString(), tile);
await Clients.Group(sessionName).SendAsync("NewTile", tile);
await Clients.Group(sessionName).NewTile(tile);
}
public async Task NewWall(string sessionName, Wall tile)
@ -48,10 +42,9 @@ namespace SignalRChat.Hubs
if (existingTile)
{
_sessions[sessionName].Walls.TryRemove(t.ToString(), out var rtile);
}
_sessions[sessionName].Walls.TryAdd(tile.ToString(), tile);
await Clients.Group(sessionName).SendAsync("NewWall", tile);
await Clients.Group(sessionName).NewWall(tile);
}
public async Task NewOverlay(string sessionName, Overlay tile)
@ -60,34 +53,27 @@ namespace SignalRChat.Hubs
if (existingTile)
{
_sessions[sessionName].Overlays.TryRemove(t.ToString(), out var rtile);
}
_sessions[sessionName].Overlays.TryAdd(tile.ToString(), tile);
await Clients.Group(sessionName).SendAsync("NewOverlay", tile);
await Clients.Group(sessionName).NewOverlay(tile);
}
public async Task DeleteTile(string sessionName, Tile tile)
{
_sessions[sessionName].Map.TryRemove(tile.ToString(), out var rtile);
await Clients.Group(sessionName).SendAsync("DeleteTile", tile);
await Clients.Group(sessionName).DeleteTile(tile);
}
public async Task DeleteWall(string sessionName, Wall tile)
{
_sessions[sessionName].Walls.TryRemove(tile.ToString(), out var rtile);
await Clients.Group(sessionName).SendAsync("DeleteWall", tile);
await Clients.Group(sessionName).DeleteWall(tile);
}
public async Task DeleteOverlay(string sessionName, Overlay tile)
{
_sessions[sessionName].Overlays.TryRemove(tile.ToString(), out var rtile);
await Clients.Group(sessionName).SendAsync("DeleteOverlay", tile);
await Clients.Group(sessionName).DeleteOverlay(tile);
}
public async Task<SessionData> NewSession(string sessionName, string initials)
@ -110,7 +96,7 @@ namespace SignalRChat.Hubs
var session = _sessions[sessionName];
var player = new Player { Position = new Tile { X = 0, Y = 0 }, ConnectionId = Context.ConnectionId, Color = session.Colors[session.Players.Count], Initials = initials };
session.Players.Add(player);
await Clients.Group(sessionName).SendAsync("NewPlayer", player);
await Clients.Group(sessionName).NewPlayer(player);
await Groups.AddToGroupAsync(Context.ConnectionId, sessionName);
return session;
}
@ -124,8 +110,7 @@ namespace SignalRChat.Hubs
{
var player = _sessions[sessionName].Players.First(m => m.ConnectionId == Context.ConnectionId);
player.Position = tile;
await Clients.Group(sessionName).SendAsync("PlayerUpdate", player);
await Clients.Group(sessionName).PlayerUpdate(player);
}
public async Task<SessionData> Refresh(string sessionName)
@ -139,26 +124,7 @@ namespace SignalRChat.Hubs
_sessions[sessionName].Overlays = map.Overlays;
_sessions[sessionName].Walls = map.Walls;
await Clients.Group(sessionName).SendAsync("UpdateMap", _sessions[sessionName]);
}
}
public static class ExtensionMethods
{
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
await Clients.Group(sessionName).UpdateMap( _sessions[sessionName]);
}
}
}

View File

@ -1,7 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<ProjectReference Include="..\..\Sledgemapper.Entities\Sledgemapper.Entities.csproj" />
<ProjectReference Include="..\..\Sledgemapper.Shared\Sledgemapper.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="mediatr" Version="9.0.0" />
</ItemGroup>
<PropertyGroup>

View File

@ -9,7 +9,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
using MediatR.Pipeline;
namespace SignalRChat
{
public class Startup
@ -26,7 +26,7 @@ namespace SignalRChat
{
services.AddRazorPages();
services.AddSignalR();
services.AddMediatR(typeof(Startup));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -53,7 +53,7 @@ namespace SignalRChat
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");
endpoints.MapHub<SledgemapperHub>("/sledgemapperhub");
});
}

View File

@ -0,0 +1,19 @@
using Sledgemapper.Shared.Entities;
using System;
using System.Threading.Tasks;
namespace Sledgemapper.Clients
{
public interface ISledgemapperClient
{
Task NewTile(Tile tile);
Task NewWall(Wall wall);
Task NewOverlay(Overlay overlay);
Task DeleteTile(Tile tile);
Task DeleteWall(Wall wall);
Task DeleteOverlay(Overlay overlay);
Task NewPlayer(Player player);
Task PlayerUpdate(Player player);
Task UpdateMap(SessionData player);
}
}

View File

@ -1,4 +1,4 @@
namespace Sledgemapper.Entities
namespace Sledgemapper.Shared.Entities
{
public class Overlay
{

View File

@ -1,4 +1,4 @@
namespace Sledgemapper.Entities
namespace Sledgemapper.Shared.Entities
{
public class Player
{

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Collections.Concurrent ;
namespace Sledgemapper.Entities
namespace Sledgemapper.Shared.Entities
{
public class SessionData
@ -13,6 +13,7 @@ namespace Sledgemapper.Entities
Players = new List< Player>();
Colors = new List<string>();
}
public ConcurrentDictionary<string, Tile> Map { get; set; }
public ConcurrentDictionary<string, Wall> Walls { get; set; }
public ConcurrentDictionary<string, Overlay> Overlays { get; set; }

View File

@ -1,4 +1,4 @@
namespace Sledgemapper.Entities
namespace Sledgemapper.Shared.Entities
{

View File

@ -1,4 +1,4 @@
namespace Sledgemapper.Entities
namespace Sledgemapper.Shared.Entities
{

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
namespace SignalRChat.Hubs
{
public static class ExtensionMethods
{
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}

View File

@ -0,0 +1,141 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
//
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
namespace System.Collections.Concurrent
{
/// <summary>
/// Provides a thread-safe dictionary for use with data binding.
/// </summary>
/// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam>
/// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam>
[DebuggerDisplay("Count={Count}")]
public class ObservableConcurrentDictionary<TKey, TValue> :
ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly SynchronizationContext _context;
private readonly ConcurrentDictionary<TKey, TValue> _dictionary;
/// <summary>
/// Initializes an instance of the ObservableConcurrentDictionary class.
/// </summary>
public ObservableConcurrentDictionary()
{
_context = AsyncOperationManager.SynchronizationContext;
_dictionary = new ConcurrentDictionary<TKey, TValue>();
}
/// <summary>Event raised when the collection changes.</summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>Event raised when a property on the collection changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
/// </summary>
private void NotifyObserversOfChange()
{
var collectionHandler = CollectionChanged;
var propertyHandler = PropertyChanged;
_context.Post(s =>
{
collectionHandler?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Count"));
propertyHandler?.Invoke(this, new PropertyChangedEventArgs(nameof(Keys)));
propertyHandler?.Invoke(this, new PropertyChangedEventArgs(nameof(Values)));
}, null);
}
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
/// <param name="item">The item to be added.</param>
/// <returns>Whether the add was successful.</returns>
private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item) => TryAddWithNotification(item.Key, item.Value);
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be added.</param>
/// <param name="value">The value of the item to be added.</param>
/// <returns>Whether the add was successful.</returns>
private bool TryAddWithNotification(TKey key, TValue value)
{
bool result = _dictionary.TryAdd(key, value);
if (result) NotifyObserversOfChange();
return result;
}
/// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be removed.</param>
/// <param name="value">The value of the item removed.</param>
/// <returns>Whether the removal was successful.</returns>
private bool TryRemoveWithNotification(TKey key, out TValue value)
{
bool result = _dictionary.TryRemove(key, out value);
if (result) NotifyObserversOfChange();
return result;
}
/// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be updated.</param>
/// <param name="value">The new value to set for the item.</param>
/// <returns>Whether the update was successful.</returns>
private void UpdateWithNotification(TKey key, TValue value)
{
_dictionary[key] = value;
NotifyObserversOfChange();
}
#region ICollection<KeyValuePair<TKey,TValue>> Members
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => TryAddWithNotification(item);
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Clear();
NotifyObserversOfChange();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
int ICollection<KeyValuePair<TKey, TValue>>.Count => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly;
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => TryRemoveWithNotification(item.Key, out _);
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value) => TryAddWithNotification(key, value);
public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key);
public ICollection<TKey> Keys => _dictionary.Keys;
public bool Remove(TKey key) => TryRemoveWithNotification(key, out _);
public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value);
public ICollection<TValue> Values => _dictionary.Values;
public TValue this[TKey key]
{
get => _dictionary[key];
set => UpdateWithNotification(key, value);
}
#endregion
}
}

View File

@ -58,15 +58,19 @@ namespace Sledgemapper
Window.AllowUserResizing = true;
Players = new List<Player>();
connection = new HubConnectionBuilder()
//.WithUrl("http://localhost:5000/ChatHub")
.WithUrl("http://hub.michelescandura.com:5000/ChatHub")
.Build();
.WithAutomaticReconnect()
#if DEBUG
.WithUrl("http://localhost:5000/ChatHub")
#else
.WithUrl("http://hub.michelescandura.com:5000/ChatHub")
#endif
.Build();
connection.On<SessionData>("UpdateMap", (map) =>
{
_sessionData.Map=map.Map;
_sessionData.Walls=map.Walls;
_sessionData.Overlays=map.Overlays;
_sessionData.Map = map.Map;
_sessionData.Walls = map.Walls;
_sessionData.Overlays = map.Overlays;
});
@ -94,11 +98,11 @@ namespace Sledgemapper
_sessionData.Overlays.Remove(tile.ToString(), out var rtile);
});
connection.On<Tile>("NewTile", (tile) =>
{
_sessionData.Map.Remove(tile.ToString(), out var rtile);
_sessionData.Map.TryAdd(tile.ToString(), tile);
});
connection.On<Tile>("NewTile", (tile) =>
{
_sessionData.Map.Remove(tile.ToString(), out var rtile);
_sessionData.Map.TryAdd(tile.ToString(), tile);
});
connection.On<Wall>("NewWall", (tile) =>
{
@ -147,7 +151,7 @@ namespace Sledgemapper
{
await connection?.InvokeAsync("Sync", _session, _sessionData);
};
menuFileLoad.Selected += (s, e) =>
{
FileDialog dialog = new FileDialog(FileDialogMode.OpenFile)
@ -727,7 +731,7 @@ namespace Sledgemapper
{
var content = Content.Load<Texture2D>($"overlays/{tile.ID}");
// System.Console.WriteLine(tile.Rotation);
// System.Console.WriteLine(tile.Rotation);
if (tile.Intersection)
{
var posX = tile.X * _tileSize;
@ -907,7 +911,7 @@ namespace Sledgemapper
if (tileExist)
{
var get = _sessionData.Map.TryGetValue(_selectedTile.ToString(), out var tile);
_sessionData.Map.TryRemove(tile.ToString(), out var rrtile);
if (tile.ID == tileId)
{

View File

@ -50,6 +50,6 @@
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sledgemapper.Entities\Sledgemapper.Entities.csproj" />
<ProjectReference Include="..\Sledgemapper.Shared\Sledgemapper.Shared.csproj" />
</ItemGroup>
</Project>