You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RizzeProjectile/Assets/Scripts/Utilities/NetworkDataBuffer.cs

189 lines
4.6 KiB
C#

using System.Collections.Generic;
using Fusion;
namespace Projectiles
{
public interface IBufferView<T> where T : INetworkStruct
{
public bool IsFinished { get; }
public void Render(ref T data, ref T fromData, float alpha);
}
public abstract class NetworkDataBuffer<TData> : NetworkDataBuffer<TData, IBufferView<TData>>
where TData : INetworkStruct
{}
/// <summary>
/// Generic data ring buffer that pairs networked data TData with visual representation TView.
/// </summary>
/// <typeparam name="TData">Data structure representing single entry in the buffer</typeparam>
/// <typeparam name="TView">View component representing buffer entry in the game scene</typeparam>
public abstract class NetworkDataBuffer<TData, TView> : ContextBehaviour
where TData : INetworkStruct
where TView : IBufferView<TData>
{
// PROTECTED MEMBERS
[Networked]
protected int _dataCount { get; set; }
// PRIVATE MEMBERS
private Dictionary<int, ViewEntry> _views = new(256);
private List<int> _finishedViews = new(128);
private int _viewCount;
private ArrayReader<TData> _dataBufferReader;
private PropertyReader<int> _dataCountReader;
// PUBLIC METHODS
public void AddData(TData data)
{
int dataIndex = _dataCount % DataBuffer.Length;
DataBuffer.Set(dataIndex, data);
_dataCount++;
}
// NetworkBehaviour INTERFACE
public override void Spawned()
{
_viewCount = _dataCount;
_dataBufferReader = GetArrayReader<TData>(nameof(DataBuffer));
_dataCountReader = GetPropertyReader<int>(nameof(_dataCount));
}
public override void Despawned(NetworkRunner runner, bool hasState)
{
foreach (var pair in _views)
{
ReturnEntry(pair.Value, false);
}
_views.Clear();
}
public override void FixedUpdateNetwork()
{
for (int i = 0; i < DataBuffer.Length; i++)
{
TData bufferData = DataBuffer[i];
UpdateData(ref bufferData);
DataBuffer.Set(i, bufferData);
}
}
public override void Render()
{
// Visuals are not processed on dedicated server at all
if (Runner.Mode == SimulationModes.Server)
return;
if (TryGetSnapshotsBuffers(out var fromNetworkBuffer, out var toNetworkBuffer, out float bufferAlpha) == false)
return;
var fromDataBuffer = _dataBufferReader.Read(fromNetworkBuffer);
var toDataBuffer = _dataBufferReader.Read(toNetworkBuffer);
int fromDataCount = _dataCountReader.Read(fromNetworkBuffer);
int toDataCount = _dataCountReader.Read(toNetworkBuffer);
int bufferLength = DataBuffer.Length;
// If our predicted views were not confirmed by the server, discard them
for (int i = fromDataCount; i < _viewCount; i++)
{
if (_views.TryGetValue(i, out var viewEntry) == false)
continue;
ReturnEntry(viewEntry, true);
_views.Remove(i);
}
// Let's spawn missing views
for (int i = _viewCount; i < fromDataCount; i++)
{
int bufferIndex = i % bufferLength;
var data = fromDataBuffer[bufferIndex];
var view = GetView(data);
if (view == null)
continue;
var viewEntry = Pool.Get<ViewEntry>();
viewEntry.View = view;
_views.Add(i, viewEntry);
}
// At some point the buffer will be overriden
// by new data (new buffer cycle) so we need to calculate
// last valid data key in the buffer.
int minDataKey = toDataCount - bufferLength;
// Update all visible views
foreach (var pair in _views)
{
var view = pair.Value.View;
if (pair.Key >= minDataKey)
{
int bufferIndex = pair.Key % bufferLength;
var data = toDataBuffer[bufferIndex];
var fromData = fromDataBuffer[bufferIndex];
view.Render(ref data, ref fromData, bufferAlpha);
pair.Value.LastData = data;
}
else
{
// Use last data to Render when there are no data available in the buffer
view.Render(ref pair.Value.LastData, ref pair.Value.LastData, 0f);
}
if (view.IsFinished == true)
{
ReturnEntry(pair.Value, false);
_finishedViews.Add(pair.Key);
}
}
for (int i = 0; i < _finishedViews.Count; i++)
{
_views.Remove(_finishedViews[i]);
}
_finishedViews.Clear();
_viewCount = fromDataCount;
}
// PROTECTED MEMBERS
protected abstract NetworkArray<TData> DataBuffer { get; }
protected virtual void UpdateData(ref TData data) { }
protected abstract TView GetView(TData data);
protected abstract void ReturnView(TView bufferView, bool misprediction);
// PRIVATE METHODS
private void ReturnEntry(ViewEntry entry, bool misprediction)
{
ReturnView(entry.View, misprediction);
Pool.Return(entry);
}
// DATA STRUCTURES
private class ViewEntry
{
public TView View;
public TData LastData;
}
}
}