mirror of
https://github.com/sbrl/Nibriboard.git
synced 2018-01-10 21:33:49 +00:00
[server] Start handling line part and line complete udpates correctly. Also add more logic to the chunk/plane system ready for later.
This commit is contained in:
parent
b6f50a48ba
commit
5dc1a2d9e5
6 changed files with 145 additions and 10 deletions
|
@ -21,7 +21,7 @@ namespace Nibriboard.Client
|
|||
public int LinesCompleted { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The number of liens that are still incubating and haven't been completed yet.
|
||||
/// The number of lines that are still incubating and haven't been completed yet.
|
||||
/// </summary>
|
||||
public int IncompletedLines {
|
||||
get {
|
||||
|
|
|
@ -55,7 +55,8 @@ namespace Nibriboard.Client
|
|||
["CursorPosition"] = typeof(CursorPositionMessage),
|
||||
["PlaneChange"] = typeof(PlaneChangeMessage),
|
||||
["ChunkUpdateRequest"] = typeof(ChunkUpdateRequestMessage),
|
||||
["LinePartMessage"] = typeof(LinePartMessage)
|
||||
["LinePartMessage"] = typeof(LinePartMessage),
|
||||
["LineCompleteMessage"] = typeof(LineCompleteMessage)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -327,16 +328,37 @@ namespace Nibriboard.Client
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles messages containing a fragment of a line from the client.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to process.</param>
|
||||
protected Task handleLinePartMessage(LinePartMessage message)
|
||||
{
|
||||
// Forward the line part to everyone on this plane
|
||||
manager.BroadcastPlane(this, message);
|
||||
|
||||
throw new NotImplementedException();
|
||||
List<LocationReference> linePoints = new List<LocationReference>(message.Points.Count);
|
||||
foreach(Vector2 point in message.Points)
|
||||
linePoints.Add(new LocationReference(CurrentPlane, point.X, point.Y));
|
||||
|
||||
manager.LineIncubator.AddBit(message.LineId, linePoints);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles notifications from clients telling us that they've finished drawing a line.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to handle.</param>
|
||||
protected async Task handleLineCompleteMessage(LineCompleteMessage message)
|
||||
{
|
||||
DrawnLine line = manager.LineIncubator.CompleteLine(message.LineId);
|
||||
line.LineWidth = message.LineWidth;
|
||||
line.Colour = message.LineColour;
|
||||
|
||||
await CurrentPlane.AddLine(line);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace Nibriboard.Client
|
|||
private ClientSettings clientSettings;
|
||||
public List<NibriClient> Clients = new List<NibriClient>();
|
||||
|
||||
public LineIncubator LineIncubator = new LineIncubator();
|
||||
|
||||
/// <summary>
|
||||
/// The cancellation token that's used by the main server to tell us when we should shut down.
|
||||
/// </summary>
|
||||
|
|
|
@ -7,6 +7,32 @@ using System.Runtime.Serialization;
|
|||
|
||||
namespace Nibriboard.RippleSpace
|
||||
{
|
||||
public enum ChunkUpdateType
|
||||
{
|
||||
/// <summary>
|
||||
/// Something was added to the chunk.
|
||||
/// </summary>
|
||||
Addition,
|
||||
/// <summary>
|
||||
/// Something was deleted form the chunk.
|
||||
/// </summary>
|
||||
Deletion,
|
||||
/// <summary>
|
||||
/// A combination of additions and deletions were made to the chunk's contents.
|
||||
/// </summary>
|
||||
Combination
|
||||
}
|
||||
|
||||
public class ChunkUpdateEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of update made to the chunk
|
||||
/// </summary>
|
||||
public ChunkUpdateType UpdateType { get; set; }
|
||||
}
|
||||
|
||||
public delegate void ChunkUpdateEvent(object sender, ChunkUpdateEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single chunk of an infinite <see cref="NibriboardServer.RippleSpace.Plane" />.
|
||||
/// </summary>
|
||||
|
@ -33,7 +59,10 @@ namespace Nibriboard.RippleSpace
|
|||
/// </summary>
|
||||
public readonly ChunkReference Location;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fired when this chunk is updated.
|
||||
/// </summary>
|
||||
public event ChunkUpdateEvent ChunkUpdateEvent;
|
||||
/// <summary>
|
||||
/// The time at which this chunk was loaded.
|
||||
/// </summary>
|
||||
|
@ -83,9 +112,8 @@ namespace Nibriboard.RippleSpace
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this chunk could, theorectically, be unloaded. Of course,
|
||||
/// the server may decide it doesn't need to unload us even if we're
|
||||
/// inactive.
|
||||
/// Whether this chunk could, theorectically, be unloaded.
|
||||
/// This method takes into account whether this is a primary chunk or not.
|
||||
/// </summary>
|
||||
public bool CouldUnload
|
||||
{
|
||||
|
@ -148,6 +176,8 @@ namespace Nibriboard.RippleSpace
|
|||
lines.Add(newLine);
|
||||
i++;
|
||||
}
|
||||
|
||||
ChunkUpdateEvent(this, new ChunkUpdateEventArgs() { UpdateType = ChunkUpdateType.Addition });
|
||||
}
|
||||
|
||||
public IEnumerator<DrawnLine> GetEnumerator()
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Nibriboard.RippleSpace
|
||||
{
|
||||
|
@ -37,16 +39,60 @@ namespace Nibriboard.RippleSpace
|
|||
/// </summary>
|
||||
public int PrimaryChunkAreaSize = 10;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum number of potentially unloadable chunks that we should have
|
||||
/// before considering unloading some chunks
|
||||
/// </summary>
|
||||
public int MinUnloadeableChunks = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The soft limit on the number of chunks we can have loaded before we start
|
||||
/// bothering to try and unload any chunks
|
||||
/// </summary>
|
||||
public int SoftLoadedChunkLimit;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when one of the chunks on this plane updates.
|
||||
/// </summary>
|
||||
public event ChunkUpdateEvent OnChunkUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// The chunkspace that holds the currently loaded and active chunks.
|
||||
/// </summary>
|
||||
protected Dictionary<ChunkReference, Chunk> loadedChunkspace = new Dictionary<ChunkReference, Chunk>();
|
||||
|
||||
/// <summary>
|
||||
/// The number of chunks that this plane currently has laoded into active memory.
|
||||
/// </summary>
|
||||
public int LoadedChunks {
|
||||
get {
|
||||
return loadedChunkspace.Count;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The number of potentially unloadable chunks this plane currently has.
|
||||
/// </summary>
|
||||
public int UnloadableChunks {
|
||||
get {
|
||||
int result = 0;
|
||||
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace) {
|
||||
if(chunkEntry.Value.CouldUnload)
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Plane(string inName, int inChunkSize)
|
||||
{
|
||||
Name = inName;
|
||||
ChunkSize = inChunkSize;
|
||||
|
||||
// Set the soft loaded chunk limit to double the number of chunks in the
|
||||
// primary chunks area
|
||||
// Note that the primary chunk area is a radius around (0, 0) - not the diameter
|
||||
SoftLoadedChunkLimit = PrimaryChunkAreaSize * PrimaryChunkAreaSize * 4;
|
||||
|
||||
StorageDirectory = $"./Planes/{Name}";
|
||||
}
|
||||
/// <summary>
|
||||
|
@ -74,6 +120,7 @@ namespace Nibriboard.RippleSpace
|
|||
// return it fast.
|
||||
string chunkFilePath = Path.Combine(StorageDirectory, chunkLocation.AsFilename());
|
||||
Chunk loadedChunk = await Chunk.FromFile(this, chunkFilePath);
|
||||
loadedChunk.OnChunkUpdate += HandleChunkUpdate;
|
||||
loadedChunkspace.Add(chunkLocation, loadedChunk);
|
||||
|
||||
return loadedChunk;
|
||||
|
@ -96,9 +143,43 @@ namespace Nibriboard.RippleSpace
|
|||
}
|
||||
}
|
||||
|
||||
public async Task PerformMaintenance()
|
||||
public void PerformMaintenance()
|
||||
{
|
||||
// TODO: Perform maintenance here
|
||||
// Be lazy and don't bother to perform maintenance if it's not needed
|
||||
if(LoadedChunks < SoftLoadedChunkLimit ||
|
||||
UnloadableChunks < MinUnloadeableChunks)
|
||||
return;
|
||||
|
||||
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace)
|
||||
{
|
||||
if(!chunkEntry.Value.CouldUnload)
|
||||
continue;
|
||||
|
||||
// This chunk has been inactive for a while - let's serialise it and save it to disk
|
||||
Stream chunkSerializationSink = new FileStream(
|
||||
Path.Combine(StorageDirectory, chunkEntry.Key.AsFilename()),
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.None
|
||||
);
|
||||
IFormatter binaryFormatter = new BinaryFormatter();
|
||||
binaryFormatter.Serialize(chunkSerializationSink, chunkEntry.Value);
|
||||
|
||||
// Remove the chunk from the loaded chunkspace
|
||||
loadedChunkspace.Remove(chunkEntry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles chunk updates from the individual loaded chunks on this plane.
|
||||
/// Re-emits chunk updates it catches wind of at plane-level.
|
||||
/// </summary>
|
||||
/// <param name="sender">The chunk responsible for the update.</param>
|
||||
/// <param name="eventArgs">The event arguments associated with the chunk update.</param>
|
||||
protected void HandleChunkUpdate(object sender, ChunkUpdateEventArgs eventArgs)
|
||||
{
|
||||
// Make the chunk update bubble up to plane-level
|
||||
OnChunkUpdate(sender, eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace Nibriboard.RippleSpace
|
|||
Stopwatch maintenanceStopwatch = Stopwatch.StartNew();
|
||||
|
||||
foreach (Plane plane in Planes)
|
||||
await plane.PerformMaintenance();
|
||||
plane.PerformMaintenance();
|
||||
|
||||
LastMaintenanceDuration = maintenanceStopwatch.ElapsedMilliseconds;
|
||||
|
||||
|
|
Loading…
Reference in a new issue