refactoring
This commit is contained in:
parent
c293490995
commit
4dfbcff460
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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");
|
||||
|
||||
});
|
||||
}
|
||||
|
19
Sledgemapper.Shared/Clients/ISledgemapperClient.cs
Normal file
19
Sledgemapper.Shared/Clients/ISledgemapperClient.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Sledgemapper.Entities
|
||||
namespace Sledgemapper.Shared.Entities
|
||||
{
|
||||
public class Overlay
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
namespace Sledgemapper.Entities
|
||||
namespace Sledgemapper.Shared.Entities
|
||||
{
|
||||
public class Player
|
||||
{
|
@ -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; }
|
@ -1,4 +1,4 @@
|
||||
namespace Sledgemapper.Entities
|
||||
namespace Sledgemapper.Shared.Entities
|
||||
{
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Sledgemapper.Entities
|
||||
namespace Sledgemapper.Shared.Entities
|
||||
{
|
||||
|
||||
|
23
Sledgemapper.Shared/ExtensionMethods.cs
Normal file
23
Sledgemapper.Shared/ExtensionMethods.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
141
Sledgemapper.Shared/ObservableConcurrentDictionary.cs
Normal file
141
Sledgemapper.Shared/ObservableConcurrentDictionary.cs
Normal 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
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -50,6 +50,6 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sledgemapper.Entities\Sledgemapper.Entities.csproj" />
|
||||
<ProjectReference Include="..\Sledgemapper.Shared\Sledgemapper.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user