mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Make disabled prediction work again.
Simulation input and Update() does not happen when prediction is disabled. Both of these can be re-opted in on a per-handler/system basis with a bool flag. Stuff like physics opts out of this now.
This commit is contained in:
@@ -438,7 +438,7 @@ namespace Robust.Client
|
||||
// In singleplayer, however, we're in full control instead.
|
||||
else if (_client.RunLevel == ClientRunLevel.SinglePlayerGame)
|
||||
{
|
||||
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds);
|
||||
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, noPredictions: false);
|
||||
_lookup.Update();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Client.GameObjects
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public override void TickUpdate(float frameTime, Histogram? histogram)
|
||||
public override void TickUpdate(float frameTime, bool noPredictions, Histogram? histogram)
|
||||
{
|
||||
using (histogram?.WithLabels("EntityNet").NewTimer())
|
||||
{
|
||||
@@ -79,7 +79,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
base.TickUpdate(frameTime, histogram);
|
||||
base.TickUpdate(frameTime, noPredictions, histogram);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -62,6 +64,9 @@ namespace Robust.Client.GameObjects
|
||||
// handle local binds before sending off
|
||||
foreach (var handler in BindRegistry.GetHandlers(function))
|
||||
{
|
||||
if (!_stateManager.IsPredictionEnabled && !handler.FireOutsidePrediction)
|
||||
continue;
|
||||
|
||||
// local handlers can block sending over the network.
|
||||
if (handler.HandleCmdMessage(session, message))
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Robust.Client.GameStates
|
||||
/// <inheritdoc />
|
||||
public int CurrentBufferSize => _processor.CalculateBufferSize(CurServerTick);
|
||||
|
||||
public bool Predicting { get; private set; }
|
||||
public bool IsPredictionEnabled { get; private set; }
|
||||
|
||||
public int PredictTickBias { get; private set; }
|
||||
public float PredictLagBias { get; private set; }
|
||||
@@ -96,7 +96,7 @@ namespace Robust.Client.GameStates
|
||||
_config.OnValueChanged(CVars.NetInterp, b => _processor.Interpolation = b, true);
|
||||
_config.OnValueChanged(CVars.NetInterpRatio, i => _processor.InterpRatio = i, true);
|
||||
_config.OnValueChanged(CVars.NetLogging, b => _processor.Logging = b, true);
|
||||
_config.OnValueChanged(CVars.NetPredict, b => Predicting = b, true);
|
||||
_config.OnValueChanged(CVars.NetPredict, b => IsPredictionEnabled = b, true);
|
||||
_config.OnValueChanged(CVars.NetPredictTickBias, i => PredictTickBias = i, true);
|
||||
_config.OnValueChanged(CVars.NetPredictLagBias, i => PredictLagBias = i, true);
|
||||
_config.OnValueChanged(CVars.NetStateBufMergeThreshold, i => StateBufferMergeThreshold = i, true);
|
||||
@@ -104,7 +104,7 @@ namespace Robust.Client.GameStates
|
||||
_processor.Interpolation = _config.GetCVar(CVars.NetInterp);
|
||||
_processor.InterpRatio = _config.GetCVar(CVars.NetInterpRatio);
|
||||
_processor.Logging = _config.GetCVar(CVars.NetLogging);
|
||||
Predicting = _config.GetCVar(CVars.NetPredict);
|
||||
IsPredictionEnabled = _config.GetCVar(CVars.NetPredict);
|
||||
PredictTickBias = _config.GetCVar(CVars.NetPredictTickBias);
|
||||
PredictLagBias = _config.GetCVar(CVars.NetPredictLagBias);
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public void InputCommandDispatched(FullInputCmdMessage message)
|
||||
{
|
||||
if (!Predicting)
|
||||
if (!IsPredictionEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs
|
||||
{
|
||||
if (!Predicting)
|
||||
if (!IsPredictionEnabled)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
@@ -214,7 +214,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// TODO: If Predicting gets disabled *while* the world state is dirty from a prediction,
|
||||
// this won't run meaning it could potentially get stuck dirty.
|
||||
if (Predicting && i == 0)
|
||||
if (IsPredictionEnabled && i == 0)
|
||||
{
|
||||
// Disable IsFirstTimePredicted while re-running HandleComponentState here.
|
||||
// Helps with debugging.
|
||||
@@ -268,7 +268,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
DebugTools.Assert(_timing.InSimulation);
|
||||
|
||||
if (Predicting)
|
||||
if (IsPredictionEnabled)
|
||||
{
|
||||
using var _ = _timing.StartPastPredictionArea();
|
||||
|
||||
@@ -324,13 +324,13 @@ namespace Robust.Client.GameStates
|
||||
// Don't run EntitySystemManager.TickUpdate if this is the target tick,
|
||||
// because the rest of the main loop will call into it with the target tick later,
|
||||
// and it won't be a past prediction.
|
||||
_entitySystemManager.TickUpdate((float) _timing.TickPeriod.TotalSeconds);
|
||||
_entitySystemManager.TickUpdate((float) _timing.TickPeriod.TotalSeconds, noPredictions: false);
|
||||
((IBroadcastEventBusInternal) _entities.EventBus).ProcessEventQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds);
|
||||
_entities.TickUpdate((float) _timing.TickPeriod.TotalSeconds, noPredictions: !IsPredictionEnabled);
|
||||
|
||||
_lookup.Update();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -44,6 +45,13 @@ namespace Robust.Client.GameStates
|
||||
/// </summary>
|
||||
int StateBufferMergeThreshold { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether prediction is currently enabled on the client entirely.
|
||||
/// This is NOT equal to <see cref="IGameTiming.InPrediction"/> or <see cref="IGameTiming.IsFirstTimePredicted"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This is effectively an alias of <see cref="CVars.NetPredict"/>.</remarks>
|
||||
bool IsPredictionEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is called after the game state has been applied for the current tick.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
@@ -11,6 +12,7 @@ namespace Robust.Client.Physics
|
||||
public class PhysicsSystem : SharedPhysicsSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameState = default!;
|
||||
|
||||
private TimeSpan _lastRem;
|
||||
|
||||
@@ -22,6 +24,9 @@ namespace Robust.Client.Physics
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
if (!_gameState.IsPredictionEnabled)
|
||||
return;
|
||||
|
||||
if (_lastRem > _gameTiming.TickRemainder)
|
||||
{
|
||||
_lastRem = TimeSpan.Zero;
|
||||
|
||||
@@ -615,7 +615,7 @@ namespace Robust.Server
|
||||
}
|
||||
|
||||
// Pass Histogram into the IEntityManager.Update so it can do more granular measuring.
|
||||
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, TickUsage);
|
||||
_entityManager.TickUpdate(frameEventArgs.DeltaSeconds, noPredictions: false, TickUsage);
|
||||
|
||||
_lookup.Update();
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void TickUpdate(float frameTime, Histogram? histogram)
|
||||
public override void TickUpdate(float frameTime, bool noPredictions, Histogram? histogram)
|
||||
{
|
||||
using (histogram?.WithLabels("EntityNet").NewTimer())
|
||||
{
|
||||
@@ -138,7 +138,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
base.TickUpdate(frameTime, histogram);
|
||||
base.TickUpdate(frameTime, noPredictions, histogram);
|
||||
|
||||
EntitiesCount.Set(Entities.Count);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,12 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<bool> NetLogging =
|
||||
CVarDef.Create("net.logging", false, CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// Whether prediction is enabled on the client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If off, simulation input commands will not fire and most entity methods will not run update.
|
||||
/// </remarks>
|
||||
public static readonly CVarDef<bool> NetPredict =
|
||||
CVarDef.Create("net.predict", true, CVar.CLIENTONLY);
|
||||
|
||||
|
||||
@@ -109,11 +109,11 @@ namespace Robust.Shared.GameObjects
|
||||
Started = false;
|
||||
}
|
||||
|
||||
public virtual void TickUpdate(float frameTime, Histogram? histogram)
|
||||
public virtual void TickUpdate(float frameTime, bool noPredictions, Histogram? histogram)
|
||||
{
|
||||
using (histogram?.WithLabels("EntitySystems").NewTimer())
|
||||
{
|
||||
EntitySystemManager.TickUpdate(frameTime);
|
||||
EntitySystemManager.TickUpdate(frameTime, noPredictions);
|
||||
}
|
||||
|
||||
using (histogram?.WithLabels("EntityEventBus").NewTimer())
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace Robust.Shared.GameObjects
|
||||
protected internal List<Type> UpdatesAfter { get; } = new();
|
||||
protected internal List<Type> UpdatesBefore { get; } = new();
|
||||
|
||||
|
||||
public bool UpdatesOutsidePrediction { get; protected internal set; }
|
||||
|
||||
IEnumerable<Type> IEntitySystem.UpdatesAfter => UpdatesAfter;
|
||||
IEnumerable<Type> IEntitySystem.UpdatesBefore => UpdatesBefore;
|
||||
|
||||
@@ -40,6 +43,10 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual void Initialize() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Not ran on the client if prediction is disabled and
|
||||
/// <see cref="UpdatesOutsidePrediction"/> is false (the default).
|
||||
/// </remarks>
|
||||
public virtual void Update(float frameTime) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -259,10 +259,13 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TickUpdate(float frameTime)
|
||||
public void TickUpdate(float frameTime, bool noPredictions)
|
||||
{
|
||||
foreach (var updReg in _updateOrder)
|
||||
{
|
||||
if (noPredictions && !updReg.System.UpdatesOutsidePrediction)
|
||||
continue;
|
||||
|
||||
if (MetricsEnabled)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
@@ -27,7 +27,11 @@ namespace Robust.Shared.GameObjects
|
||||
/// Drops every entity, component and entity system.
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
void TickUpdate(float frameTime, Histogram? histogram=null);
|
||||
|
||||
/// <param name="noPredictions">
|
||||
/// Only run systems with <see cref="EntitySystem.UpdatesOutsidePrediction"/> set true.
|
||||
/// </param>
|
||||
void TickUpdate(float frameTime, bool noPredictions, Histogram? histogram=null);
|
||||
|
||||
/// <summary>
|
||||
/// Client-specific per-render frame updating.
|
||||
|
||||
@@ -16,6 +16,11 @@ namespace Robust.Shared.GameObjects
|
||||
IEnumerable<Type> UpdatesAfter { get; }
|
||||
IEnumerable<Type> UpdatesBefore { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If prediction is disabled on the client, <see cref="Update"/> will not be ran unless this flag is set.
|
||||
/// </summary>
|
||||
bool UpdatesOutsidePrediction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called once when the system is created to initialize its state.
|
||||
/// </summary>
|
||||
|
||||
@@ -104,8 +104,11 @@ namespace Robust.Shared.GameObjects
|
||||
/// Update all systems.
|
||||
/// </summary>
|
||||
/// <param name="frameTime">Time since the last frame was rendered.</param>
|
||||
/// <param name="noPredictions">
|
||||
/// Only run systems with <see cref="EntitySystem.UpdatesOutsidePrediction"/> set true.
|
||||
/// </param>
|
||||
/// <seealso cref="IEntitySystem.Update(float)"/>
|
||||
void TickUpdate(float frameTime);
|
||||
void TickUpdate(float frameTime, bool noPredictions);
|
||||
void FrameUpdate(float frameTime);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
_mapManager.MapCreated += OnMapCreated;
|
||||
|
||||
SubscribeLocalEvent<GridInitializeEvent>(HandleGridInit);
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
// Need to queue because otherwise calling HandleMove during FrameUpdate will lead to prediction issues.
|
||||
// TODO: Need to check if that's even still relevant since transform lerping fix?
|
||||
ProcessChanges();
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace Robust.Shared.GameObjects
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
_mapManager.TileChanged += MapManagerOnTileChanged;
|
||||
SubscribeLocalEvent<TransformComponent, EntityDirtyEvent>(OnTransformDirty);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Robust.Shared.Input.Binding
|
||||
|
||||
public abstract class InputCmdHandler
|
||||
{
|
||||
public virtual bool FireOutsidePrediction => false;
|
||||
|
||||
public virtual void Enabled(ICommonSession? session)
|
||||
{
|
||||
}
|
||||
@@ -25,13 +27,14 @@ namespace Robust.Shared.Input.Binding
|
||||
/// <param name="disabled">The delegate to be ran when this command is disabled.</param>
|
||||
/// <returns>The new input command.</returns>
|
||||
public static InputCmdHandler FromDelegate(StateInputCmdDelegate? enabled = null,
|
||||
StateInputCmdDelegate? disabled = null, bool handle=true)
|
||||
StateInputCmdDelegate? disabled = null, bool handle=true, bool outsidePrediction=false)
|
||||
{
|
||||
return new StateInputCmdHandler
|
||||
{
|
||||
EnabledDelegate = enabled,
|
||||
DisabledDelegate = disabled,
|
||||
Handle = handle,
|
||||
OutsidePrediction = outsidePrediction
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,6 +43,8 @@ namespace Robust.Shared.Input.Binding
|
||||
public StateInputCmdDelegate? EnabledDelegate;
|
||||
public StateInputCmdDelegate? DisabledDelegate;
|
||||
public bool Handle { get; set; }
|
||||
public bool OutsidePrediction;
|
||||
public override bool FireOutsidePrediction => OutsidePrediction;
|
||||
|
||||
public override void Enabled(ICommonSession? session)
|
||||
{
|
||||
@@ -81,15 +86,7 @@ namespace Robust.Shared.Input.Binding
|
||||
private PointerInputCmdDelegate2 _callback;
|
||||
private bool _ignoreUp;
|
||||
|
||||
/// <summary>
|
||||
/// Handler which will handle the command using the indicated callback
|
||||
/// </summary>
|
||||
/// <param name="callback">callback to handle the command</param>
|
||||
/// <param name="ignoreUp">whether keyup actions will be ignored by this handler (like lifting a key or releasing
|
||||
/// mouse button)</param>
|
||||
public PointerInputCmdHandler(PointerInputCmdDelegate callback, bool ignoreUp = true)
|
||||
: this((in PointerInputCmdArgs args) =>
|
||||
callback(args.Session, args.Coordinates, args.EntityUid), ignoreUp) { }
|
||||
public override bool FireOutsidePrediction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handler which will handle the command using the indicated callback
|
||||
@@ -97,11 +94,21 @@ namespace Robust.Shared.Input.Binding
|
||||
/// <param name="callback">callback to handle the command</param>
|
||||
/// <param name="ignoreUp">whether keyup actions will be ignored by this handler (like lifting a key or releasing
|
||||
/// mouse button)</param>
|
||||
public PointerInputCmdHandler(PointerInputCmdDelegate2 callback, bool ignoreUp = true)
|
||||
public PointerInputCmdHandler(PointerInputCmdDelegate callback, bool ignoreUp = true, bool outsidePrediction = false)
|
||||
: this((in PointerInputCmdArgs args) =>
|
||||
callback(args.Session, args.Coordinates, args.EntityUid), ignoreUp, outsidePrediction) { }
|
||||
|
||||
/// <summary>
|
||||
/// Handler which will handle the command using the indicated callback
|
||||
/// </summary>
|
||||
/// <param name="callback">callback to handle the command</param>
|
||||
/// <param name="ignoreUp">whether keyup actions will be ignored by this handler (like lifting a key or releasing
|
||||
/// mouse button)</param>
|
||||
public PointerInputCmdHandler(PointerInputCmdDelegate2 callback, bool ignoreUp = true, bool outsidePrediction = false)
|
||||
{
|
||||
_callback = callback;
|
||||
_ignoreUp = ignoreUp;
|
||||
|
||||
FireOutsidePrediction = outsidePrediction;
|
||||
}
|
||||
|
||||
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
|
||||
@@ -141,11 +148,13 @@ namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
private PointerInputCmdDelegate _enabled;
|
||||
private PointerInputCmdDelegate _disabled;
|
||||
public override bool FireOutsidePrediction { get; }
|
||||
|
||||
public PointerStateInputCmdHandler(PointerInputCmdDelegate enabled, PointerInputCmdDelegate disabled)
|
||||
public PointerStateInputCmdHandler(PointerInputCmdDelegate enabled, PointerInputCmdDelegate disabled, bool outsidePrediction = false)
|
||||
{
|
||||
_enabled = enabled;
|
||||
_disabled = disabled;
|
||||
FireOutsidePrediction = outsidePrediction;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -57,6 +57,9 @@ namespace Robust.Shared.Physics
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
UpdatesAfter.Add(typeof(SharedTransformSystem));
|
||||
|
||||
SubscribeLocalEvent<BroadphaseComponent, ComponentAdd>(OnBroadphaseAdd);
|
||||
|
||||
@@ -51,6 +51,9 @@ namespace Robust.Shared.Physics
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
|
||||
SubscribeLocalEvent<JointComponent, ComponentShutdown>(HandleShutdown);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
|
||||
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
|
||||
public bool UpdatesOutsidePrediction => true;
|
||||
public void Initialize() { }
|
||||
public void Shutdown() { }
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
systems.GetEntitySystem<TestSystemC>().Counter = counter;
|
||||
systems.GetEntitySystem<TestSystemD>().Counter = counter;
|
||||
|
||||
systems.TickUpdate(1);
|
||||
systems.TickUpdate(1, noPredictions: false);
|
||||
|
||||
Assert.That(counter.X, Is.EqualTo(4));
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
|
||||
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
|
||||
public bool UpdatesOutsidePrediction => true;
|
||||
public void Initialize() { }
|
||||
public void Shutdown() { }
|
||||
public void Update(float frameTime) { }
|
||||
|
||||
Reference in New Issue
Block a user