mirror of
https://github.com/sbrl/Nibriboard.git
synced 2018-01-10 21:33:49 +00:00
[server] Continue wiring the ripplespace to nibri client, mainly by implementing the ChunkUpdateRequest message handler and all it's reuqired backend methods & classes.
This commit is contained in:
parent
22a03d9ab9
commit
77ce5b4d71
8 changed files with 244 additions and 13 deletions
93
Nibriboard/Client/ChunkCache.cs
Normal file
93
Nibriboard/Client/ChunkCache.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nibriboard.RippleSpace;
|
||||
|
||||
namespace Nibriboard.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache of chunk references. Useful for keeping track which chunks
|
||||
/// a remote party is currently keeping in memory.
|
||||
/// </summary>
|
||||
public class ChunkCache
|
||||
{
|
||||
List<ChunkReference> cache;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new empty chunk cache.
|
||||
/// </summary>
|
||||
public ChunkCache()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a chunk reference to the cache.
|
||||
/// If the chunk is already in the cache, then it won't be added again.
|
||||
/// </summary>
|
||||
/// <param name="chunkRef">The chunk reference to add.</param>
|
||||
public void Add(ChunkReference chunkRef)
|
||||
{
|
||||
// If this cache already contains the specified chunk reference, then we
|
||||
// probably shouldn't add it to the cache twice
|
||||
if(cache.Contains(chunkRef))
|
||||
return;
|
||||
|
||||
cache.Add(chunkRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given chunk references to the cache.
|
||||
/// Quietly skips over duplicate chunk references.
|
||||
/// </summary>
|
||||
/// <param name="chunkRefs">The chunk references to add.</param>
|
||||
public void Add(IEnumerable<ChunkReference> chunkRefs)
|
||||
{
|
||||
foreach(ChunkReference chunkRef in chunkRefs)
|
||||
Add(chunkRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remvoes a chunk reference from the cache.
|
||||
/// </summary>
|
||||
/// <param name="chunkRef">The chunk reference to remove.</param>
|
||||
public void Remove(ChunkReference chunkRef)
|
||||
{
|
||||
cache.Remove(chunkRef);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a list of chunk references from the cache.
|
||||
/// </summary>
|
||||
/// <param name="chunkRefs">The chunk references to remove.</param>
|
||||
public void Remove(IEnumerable<ChunkReference> chunkRefs)
|
||||
{
|
||||
foreach(ChunkReference chunkRef in chunkRefs)
|
||||
Remove(chunkRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this cache contains the specified chunk reference.
|
||||
/// </summary>
|
||||
/// <param name="chunkRef">The chunk reference to check for.</param>
|
||||
/// <returns>Whether this cache contaisn the specified chunk reference..</returns>
|
||||
public bool Contains(ChunkReference chunkRef)
|
||||
{
|
||||
return cache.Contains(chunkRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a list of chunk references, return another list of chunk references
|
||||
/// that aren't in this chunk cache.
|
||||
/// </summary>
|
||||
/// <param name="sourceChunkRefs">The list of chunk references to check against this chunk cache.</param>
|
||||
/// <returns>The chunk references missing from this chunk cache.</returns>
|
||||
public List<ChunkReference> FindMissing(IEnumerable<ChunkReference> sourceChunkRefs)
|
||||
{
|
||||
List<ChunkReference> result = new List<ChunkReference>();
|
||||
foreach(ChunkReference sourceChunkRef in sourceChunkRefs)
|
||||
{
|
||||
if(!Contains(sourceChunkRef))
|
||||
result.Add(sourceChunkRef);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
19
Nibriboard/Client/Messages/ChunkUpdateRequestMessage.cs
Normal file
19
Nibriboard/Client/Messages/ChunkUpdateRequestMessage.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nibriboard.RippleSpace;
|
||||
|
||||
namespace Nibriboard.Client.Messages
|
||||
{
|
||||
public class ChunkUpdateRequestMessage : Message
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of chunks that the client has intentionally forgotten about, and will need
|
||||
/// to be resent to the client.
|
||||
/// </summary>
|
||||
public List<ChunkReference> ForgottenChunks = new List<ChunkReference>();
|
||||
|
||||
public ChunkUpdateRequestMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,11 +50,11 @@ namespace Nibriboard.Client
|
|||
/// </summary>
|
||||
private readonly WebSocket client;
|
||||
|
||||
private static readonly Dictionary<string, Type> messageEventTypes = new Dictionary<string, Type>()
|
||||
{
|
||||
private static readonly Dictionary<string, Type> messageEventTypes = new Dictionary<string, Type>() {
|
||||
["HandshakeRequest"] = typeof(HandshakeRequestMessage),
|
||||
["CursorPosition"] = typeof(CursorPositionMessage),
|
||||
["PlaneChange"] = typeof(PlaneChangeMessage)
|
||||
["PlaneChange"] = typeof(PlaneChangeMessage),
|
||||
["ChunkUpdateRequest"] = typeof(ChunkUpdateRequestMessage)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -105,6 +105,11 @@ namespace Nibriboard.Client
|
|||
/// This client's colour. Used to tell multiple clients apart visually.
|
||||
/// </summary>
|
||||
public readonly ColourHSL Colour = ColourHSL.RandomSaturated();
|
||||
/// <summary>
|
||||
/// The chunk cache. Keeps track of which chunks this client currently has.
|
||||
/// </summary>
|
||||
protected ChunkCache chunkCache = new ChunkCache();
|
||||
|
||||
|
||||
#region Core Setup & Message Routing Logic
|
||||
|
||||
|
@ -167,6 +172,7 @@ namespace Nibriboard.Client
|
|||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Message Sending
|
||||
|
||||
/// <summary>
|
||||
|
@ -239,12 +245,13 @@ namespace Nibriboard.Client
|
|||
{
|
||||
if(chunkRef.Plane != CurrentPlane)
|
||||
return false;
|
||||
|
||||
|
||||
Rectangle chunkArea = chunkRef.InPlanespaceRectangle();
|
||||
|
||||
return chunkArea.Overlap(CurrentViewPort);
|
||||
}
|
||||
|
||||
|
||||
#region Message Handlers
|
||||
/// <summary>
|
||||
/// Handles an incoming handshake request. We should only receive one of these!
|
||||
|
@ -289,6 +296,21 @@ namespace Nibriboard.Client
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected async Task handleChunkUpdateRequestMessage(ChunkUpdateRequestMessage message)
|
||||
{
|
||||
chunkCache.Remove(message.ForgottenChunks);
|
||||
|
||||
ChunkUpdateMessage response = new ChunkUpdateMessage();
|
||||
List<ChunkReference> missingChunks = ChunkTools.GetContainingChunkReferences(CurrentPlane, CurrentViewPort);
|
||||
missingChunks = chunkCache.FindMissing(missingChunks);
|
||||
|
||||
response.Chunks = await CurrentPlane.FetchChunks(missingChunks);
|
||||
|
||||
Send(response);
|
||||
|
||||
chunkCache.Add(missingChunks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles an incoming cursor position message from the client..
|
||||
/// </summary>
|
||||
|
@ -303,8 +325,10 @@ namespace Nibriboard.Client
|
|||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generates an update message that contains information about the locations and states of all connected clients.
|
||||
/// Automatically omits information about the current client, and clients on other planes.
|
||||
|
|
|
@ -92,6 +92,9 @@
|
|||
<Compile Include="Client\Messages\ChunkUpdateMessage.cs" />
|
||||
<Compile Include="Client\Messages\PlaneChangeMessage.cs" />
|
||||
<Compile Include="Client\Messages\ExceptionMessage.cs" />
|
||||
<Compile Include="Utilities\ChunkTools.cs" />
|
||||
<Compile Include="Client\Messages\ChunkUpdateRequestMessage.cs" />
|
||||
<Compile Include="Client\ChunkCache.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ClientFiles\index.html" />
|
||||
|
|
|
@ -9,6 +9,9 @@ namespace Nibriboard.RippleSpace
|
|||
/// </summary>
|
||||
public class LocationReference : Reference
|
||||
{
|
||||
/// <summary>
|
||||
/// The chunk that this location reference fall inside.
|
||||
/// </summary>
|
||||
public ChunkReference ContainingChunk {
|
||||
get {
|
||||
return new ChunkReference(
|
||||
|
@ -36,7 +39,7 @@ namespace Nibriboard.RippleSpace
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override int GetHashCode ()
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return $"({Plane.Name})+{X}+{Y}".GetHashCode();
|
||||
}
|
||||
|
|
|
@ -49,6 +49,18 @@ namespace Nibriboard.RippleSpace
|
|||
|
||||
StorageDirectory = $"./Planes/{Name}";
|
||||
}
|
||||
/// <summary>
|
||||
/// Fetches a list of chunks by a list of chunk refererences.
|
||||
/// </summary>
|
||||
/// <param name="chunkRefs">The chunk references to fetch the attached chunks for.</param>
|
||||
/// <returns>The chunks attached to the specified chunk references.</returns>
|
||||
public async Task<List<Chunk>> FetchChunks(List<ChunkReference> chunkRefs)
|
||||
{
|
||||
List<Chunk> chunks = new List<Chunk>();
|
||||
foreach(ChunkReference chunkRef in chunkRefs)
|
||||
chunks.Add(await FetchChunk(chunkRef));
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public async Task<Chunk> FetchChunk(ChunkReference chunkLocation)
|
||||
{
|
||||
|
|
45
Nibriboard/Utilities/ChunkTools.cs
Normal file
45
Nibriboard/Utilities/ChunkTools.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nibriboard.RippleSpace;
|
||||
using SBRL.Utilities;
|
||||
|
||||
namespace SBRL.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of tools aid in the manipulation of chunks.
|
||||
/// </summary>
|
||||
public static class ChunkTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of chunk references that cross inside the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="plane">The plane to operate on.</param>
|
||||
/// <param name="area">The rectangle to find the containing chunks for.</param>
|
||||
/// <returns>All the chunk references that fall inside the specified area.</returns>
|
||||
public static List<ChunkReference> GetContainingChunkReferences(Plane plane, Rectangle area)
|
||||
{
|
||||
List<ChunkReference> result = new List<ChunkReference>();
|
||||
|
||||
Vector2 currentLocation = area.TopLeft;
|
||||
while(currentLocation.X < area.BottomRight.X &&
|
||||
currentLocation.Y < area.BottomRight.Y)
|
||||
{
|
||||
result.Add(new ChunkReference(
|
||||
plane,
|
||||
currentLocation.X,
|
||||
currentLocation.Y
|
||||
));
|
||||
|
||||
currentLocation.X += plane.ChunkSize;
|
||||
|
||||
if(currentLocation.X > area.Right)
|
||||
{
|
||||
currentLocation.X = area.Left;
|
||||
currentLocation.Y += plane.ChunkSize;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,36 +39,36 @@ namespace SBRL.Utilities
|
|||
/// The top-left corner of the rectangle.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Point TopLeft {
|
||||
public Vector2 TopLeft {
|
||||
get {
|
||||
return new Point(X, Y);
|
||||
return new Vector2(X, Y);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The top-right corner of the rectangle.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Point TopRight {
|
||||
public Vector2 TopRight {
|
||||
get {
|
||||
return new Point(X + Width, Y);
|
||||
return new Vector2(X + Width, Y);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The bottom-left corner of the rectangle.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Point BottomLeft {
|
||||
public Vector2 BottomLeft {
|
||||
get {
|
||||
return new Point(X, Y + Height);
|
||||
return new Vector2(X, Y + Height);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The bottom-right corner of the rectangle.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Point BottomRight {
|
||||
public Vector2 BottomRight {
|
||||
get {
|
||||
return new Point(X + Width, Y + Height);
|
||||
return new Vector2(X + Width, Y + Height);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
@ -82,6 +82,9 @@ namespace SBRL.Utilities
|
|||
get {
|
||||
return Y;
|
||||
}
|
||||
set {
|
||||
Y = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Y coordinate of the bottom of the rectangle.
|
||||
|
@ -91,6 +94,9 @@ namespace SBRL.Utilities
|
|||
get {
|
||||
return Y + Height;
|
||||
}
|
||||
set {
|
||||
Height = value - Y;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The X coordinate of the left side of the rectangle.
|
||||
|
@ -100,6 +106,9 @@ namespace SBRL.Utilities
|
|||
get {
|
||||
return X;
|
||||
}
|
||||
set {
|
||||
X = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The X coordinate of the right side of the rectangle.
|
||||
|
@ -109,6 +118,9 @@ namespace SBRL.Utilities
|
|||
get {
|
||||
return X + Width;
|
||||
}
|
||||
set {
|
||||
Width = value - X;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -135,6 +147,26 @@ namespace SBRL.Utilities
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Rectangle representing the area that this rectangle overlaps with another.
|
||||
/// Returns an empty rectangle if the two don't overlap at all.
|
||||
/// </summary>
|
||||
/// <param name="otherRectangle">The other rectangle that overlaps this one.</param>
|
||||
/// <returns>The area that this rectanagle overlaps with another.</returns>
|
||||
public Rectangle OverlappingArea(Rectangle otherRectangle)
|
||||
{
|
||||
if(!Overlap(otherRectangle))
|
||||
return Rectangle.Zero;
|
||||
|
||||
Rectangle result = new Rectangle();
|
||||
result.Top = Math.Max(Top, otherRectangle.Top);
|
||||
result.Left = Math.Max(Left, otherRectangle.Left);
|
||||
result.Bottom = Math.Max(Bottom, otherRectangle.Bottom);
|
||||
result.Right = Math.Max(Right, otherRectangle.Right);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue