1
0
Fork 0

[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:
Starbeamrainbowlabs 2017-03-25 19:49:44 +00:00
parent 22a03d9ab9
commit 77ce5b4d71
8 changed files with 244 additions and 13 deletions

View 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;
}
}
}

View 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()
{
}
}
}

View File

@ -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.

View File

@ -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" />

View File

@ -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();
}

View File

@ -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)
{

View 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;
}
}
}

View File

@ -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;
}
}
}