-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added capture of server console input for hotkeys
- Loading branch information
Showing
3 changed files
with
421 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
using FluentAssertions; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
namespace NitroxModel.DataStructures; | ||
|
||
[TestClass] | ||
public class CircularBufferTest | ||
{ | ||
[TestMethod] | ||
public void ShouldLimitSizeToMaxSize() | ||
{ | ||
CircularBuffer<string> buffer = new(1); | ||
buffer.Count.Should().Be(0); | ||
buffer.Add("1"); | ||
buffer.Count.Should().Be(1); | ||
buffer.Add("2"); | ||
buffer.Count.Should().Be(1); | ||
|
||
buffer = new CircularBuffer<string>(5); | ||
buffer.Count.Should().Be(0); | ||
buffer.Add("1"); | ||
buffer.Count.Should().Be(1); | ||
buffer.Add("2"); | ||
buffer.Count.Should().Be(2); | ||
buffer.Add("3"); | ||
buffer.Count.Should().Be(3); | ||
buffer.Add("4"); | ||
buffer.Count.Should().Be(4); | ||
buffer.Add("5"); | ||
buffer.Count.Should().Be(5); | ||
buffer.Add("6"); | ||
buffer.Count.Should().Be(5); | ||
} | ||
|
||
[TestMethod] | ||
public void ShouldOverwriteOldestItemInBufferWhenCapped() | ||
{ | ||
CircularBuffer<string> buffer = new(3); | ||
buffer.Add("1"); | ||
buffer[0].Should().Be("1"); | ||
buffer.Add("2"); | ||
buffer[1].Should().Be("2"); | ||
buffer.Add("3"); | ||
buffer[2].Should().Be("3"); | ||
buffer.Add("4"); | ||
buffer[0].Should().Be("4"); | ||
buffer.Add("5"); | ||
buffer[1].Should().Be("5"); | ||
buffer[2].Should().Be("3"); | ||
buffer.Add("6"); | ||
buffer[2].Should().Be("6"); | ||
} | ||
|
||
[TestMethod] | ||
public void ShouldDiscardAddIfCapacityReached() | ||
{ | ||
CircularBuffer<string> buffer = new(0); | ||
buffer.Count.Should().Be(0); | ||
buffer.Add("1"); | ||
buffer.Count.Should().Be(0); | ||
} | ||
|
||
[TestMethod] | ||
public void ShouldBeEmptyWhenCleared() | ||
{ | ||
CircularBuffer<string> buffer = new(10); | ||
buffer.Count.Should().Be(0); | ||
buffer.Add("1"); | ||
buffer.Add("1"); | ||
buffer.Add("1"); | ||
buffer.Count.Should().Be(3); | ||
buffer.Clear(); | ||
buffer.Count.Should().Be(0); | ||
} | ||
|
||
[TestMethod] | ||
public void ShouldGiveLastChanged() | ||
{ | ||
CircularBuffer<int> buffer = new(3); | ||
buffer.LastChangedIndex.Should().Be(-1); | ||
buffer.Add(1); | ||
buffer.LastChangedIndex.Should().Be(0); | ||
buffer.Add(2); | ||
buffer.LastChangedIndex.Should().Be(1); | ||
buffer.Add(3); | ||
buffer.LastChangedIndex.Should().Be(2); | ||
buffer.Add(4); | ||
buffer.LastChangedIndex.Should().Be(0); | ||
buffer.Add(5); | ||
buffer.LastChangedIndex.Should().Be(1); | ||
buffer.Add(6); | ||
buffer.LastChangedIndex.Should().Be(2); | ||
buffer.Add(7); | ||
buffer.Should().ContainInOrder(7, 5, 6); | ||
} | ||
|
||
[TestMethod] | ||
public void ShouldReverseOrderWithNegativeIndex() | ||
{ | ||
CircularBuffer<int> buffer = new(6); | ||
buffer.AddRange(1, 2, 3, 4, 5, 6); | ||
buffer[-1].Should().Be(6); | ||
buffer[-2].Should().Be(5); | ||
buffer[-3].Should().Be(4); | ||
buffer[-4].Should().Be(3); | ||
buffer[-5].Should().Be(2); | ||
buffer[-6].Should().Be(1); | ||
buffer[-7].Should().Be(6); | ||
buffer[-8].Should().Be(5); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
|
||
namespace NitroxModel.DataStructures; | ||
|
||
/// <summary> | ||
/// Given a fixed size, fills to capacity and then overwrites earliest item. | ||
/// </summary> | ||
public class CircularBuffer<T> : IList<T> | ||
{ | ||
private readonly List<T> data; | ||
private readonly int maxSize; | ||
|
||
/// <summary> | ||
/// Returns the index last changed. If <see cref="CircularBuffer{T}" /> is empty, returns -1. | ||
/// </summary> | ||
public int LastChangedIndex { get; protected set; } = -1; | ||
|
||
/// <summary> | ||
/// Gets the item at the index, wrapping around the buffer if out-of-range. | ||
/// </summary> | ||
public T this[int index] | ||
{ | ||
// Proper modulus operator which C# doesn't have. % = remainder operator and doesn't work in reverse for negative numbers. | ||
get => data[Math.Abs((index % data.Count + data.Count) % data.Count)]; | ||
set => throw new NotSupportedException(); | ||
} | ||
|
||
public int Count => data.Count; | ||
public bool IsReadOnly => false; | ||
|
||
public CircularBuffer(int maxSize, int initialCapacity = 0) | ||
{ | ||
if (maxSize < 0) throw new ArgumentOutOfRangeException(nameof(maxSize), "Max size must be larger than -1"); | ||
|
||
this.maxSize = maxSize; | ||
data = new List<T>(Math.Max(0, Math.Min(initialCapacity, maxSize))); | ||
} | ||
|
||
public int IndexOf(T item) | ||
{ | ||
return data.IndexOf(item); | ||
} | ||
|
||
public void Insert(int index, T item) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public void RemoveAt(int index) | ||
{ | ||
data.RemoveAt(index); | ||
} | ||
|
||
public bool Remove(T item) | ||
{ | ||
return data.Remove(item); | ||
} | ||
|
||
public void Add(T item) | ||
{ | ||
if (maxSize == 0) return; | ||
if (data.Count < maxSize) | ||
{ | ||
data.Add(item); | ||
LastChangedIndex++; | ||
return; | ||
} | ||
|
||
LastChangedIndex = (LastChangedIndex + 1) % maxSize; | ||
data[LastChangedIndex] = item; | ||
} | ||
|
||
public void AddRange(IEnumerable<T> items) | ||
{ | ||
foreach (T item in items) Add(item); | ||
} | ||
|
||
public void AddRange(params T[] items) | ||
{ | ||
foreach (T item in items) Add(item); | ||
} | ||
|
||
public void Clear() | ||
{ | ||
data.Clear(); | ||
LastChangedIndex = 0; | ||
} | ||
|
||
public bool Contains(T item) | ||
{ | ||
return data.Contains(item); | ||
} | ||
|
||
public void CopyTo(T[] array, int arrayIndex) | ||
{ | ||
data.CopyTo(array, arrayIndex); | ||
} | ||
|
||
public IEnumerator<T> GetEnumerator() | ||
{ | ||
return data.GetEnumerator(); | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
return GetEnumerator(); | ||
} | ||
} |
Oops, something went wrong.