From 347f549ac1224525dfbaebac3212234b4537258f Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 11 Apr 2017 00:41:12 +0300 Subject: [PATCH] Reactors, sonars, nav terminals, pumps and batteries use similar delayed correction logic as doors and inventories. I.e. the clients delay correcting the state of the item until the local player stops manipulating the state (atm the delay is 1 sec). Prevents pumping speeds, steering directions and whatnot from switching to an old state and back - now the corrections should not be visible to the players unless the client predicts the state wrong. --- Subsurface/Source/Items/Components/Door.cs | 3 +- .../Source/Items/Components/ItemComponent.cs | 48 ++++++++++++++----- .../Source/Items/Components/Machines/Pump.cs | 16 ++++++- .../Source/Items/Components/Machines/Radar.cs | 7 +++ .../Items/Components/Machines/Reactor.cs | 18 +++++-- .../Items/Components/Machines/Steering.cs | 11 +++++ .../Items/Components/Power/PowerContainer.cs | 19 ++++++-- Subsurface/Source/Utils/ToolBox.cs | 17 ++++++- 8 files changed, 114 insertions(+), 25 deletions(-) diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index 7dc28b9ee..d47418319 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -514,7 +514,7 @@ namespace Barotrauma.Items.Components PlaySound(ActionType.OnUse, item.WorldPosition); predictedState = open; - resetPredictionTimer = 1.0f; + resetPredictionTimer = CorrectionDelay; } else { @@ -523,7 +523,6 @@ namespace Barotrauma.Items.Components isOpen = open; } - //opening a partially stuck door makes it less stuck if (isOpen) stuck = MathHelper.Clamp(stuck - 30.0f, 0.0f, 100.0f); diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index 5564b0809..ed5dd2841 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -59,9 +59,7 @@ namespace Barotrauma.Items.Components public bool WasUsed; public readonly Dictionary> statusEffectLists; - - protected bool updated; - + public List requiredItems; public List requiredSkills; @@ -72,6 +70,10 @@ namespace Barotrauma.Items.Components public ItemComponent Parent; + protected const float CorrectionDelay = 1.0f; + protected CoroutineHandle delayedCorrectionCoroutine; + protected float correctionTimer; + private string msg; [HasDefaultValue(0.0f, false)] @@ -86,15 +88,7 @@ namespace Barotrauma.Items.Components { get { return properties; } } - //has the component already been updated this frame - public bool Updated - { - get { return updated; } - set { updated = value; } - } - - public bool NetworkUpdateSent; - + public virtual bool IsActive { get { return isActive; } @@ -535,6 +529,12 @@ namespace Barotrauma.Items.Components Sounds.SoundManager.Stop(loopingSoundIndex); } + if (delayedCorrectionCoroutine != null) + { + CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); + delayedCorrectionCoroutine = null; + } + RemoveComponentSpecific(); } @@ -680,6 +680,30 @@ namespace Barotrauma.Items.Components } } + //Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero. + protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime) + { + if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); + + delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime)); + } + + private IEnumerable DoDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime) + { + while (correctionTimer > 0.0f) + { + correctionTimer -= CoroutineManager.DeltaTime; + yield return CoroutineStatus.Running; + } + + ((IServerSerializable)this).ClientRead(type, buffer, sendingTime); + + correctionTimer = 0.0f; + delayedCorrectionCoroutine = null; + + yield return CoroutineStatus.Success; + } + public virtual XElement Save(XElement parentElement) { XElement componentElement = new XElement(name); diff --git a/Subsurface/Source/Items/Components/Machines/Pump.cs b/Subsurface/Source/Items/Components/Machines/Pump.cs index 3cae88913..dba76a360 100644 --- a/Subsurface/Source/Items/Components/Machines/Pump.cs +++ b/Subsurface/Source/Items/Components/Machines/Pump.cs @@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components } else if (GameMain.Client != null) { + correctionTimer = CorrectionDelay; item.CreateClientEvent(this); } @@ -97,6 +98,7 @@ namespace Barotrauma.Items.Components } else if (GameMain.Client != null) { + correctionTimer = CorrectionDelay; item.CreateClientEvent(this); } @@ -114,6 +116,7 @@ namespace Barotrauma.Items.Components } else if (GameMain.Client != null) { + correctionTimer = CorrectionDelay; item.CreateClientEvent(this); } @@ -234,7 +237,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) + public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Client c) { float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; bool isActive = msg.ReadBoolean(); @@ -243,9 +246,12 @@ namespace Barotrauma.Items.Components FlowPercentage = flowPercentage; IsActive = isActive; + + //notify all clients of the changed state + item.CreateServerEvent(this); } - public void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c, object[] extraData = null) + public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null) { //flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f)); @@ -254,6 +260,12 @@ namespace Barotrauma.Items.Components public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { + if (correctionTimer > 0.0f) + { + StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime); + return; + } + FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; IsActive = msg.ReadBoolean(); } diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index 8da8d9e75..1564d6bef 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -64,6 +64,7 @@ namespace Barotrauma.Items.Components else if (GameMain.Client != null) { item.CreateClientEvent(this); + correctionTimer = CorrectionDelay; } IsActive = box.Selected; @@ -450,6 +451,12 @@ namespace Barotrauma.Items.Components public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { + if (correctionTimer > 0.0f) + { + StartDelayedCorrection(type, msg.ExtractBits(1), sendingTime); + return; + } + IsActive = msg.ReadBoolean(); isActiveTickBox.Selected = IsActive; } diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index a73289e56..d01c9de65 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components { class Reactor : Powered, IDrawableComponent, IServerSerializable, IClientSerializable { - const float NetworkUpdateInterval = 3.0f; + const float NetworkUpdateInterval = 0.5f; //the rate at which the reactor is being run un //higher rates generate more power (and heat) @@ -553,16 +553,18 @@ namespace Barotrauma.Items.Components public void ClientWrite(NetBuffer msg, object[] extraData = null) { msg.Write(autoTemp); - msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 8); + msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 15); msg.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8); msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8); + + correctionTimer = CorrectionDelay; } public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { bool autoTemp = msg.ReadBoolean(); - float shutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 8); + float shutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 15); float coolingRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); @@ -583,7 +585,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedSingle(temperature, 0.0f, 10000.0f, 16); msg.Write(autoTemp); - msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 8); + msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 15); msg.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8); msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8); @@ -591,10 +593,16 @@ namespace Barotrauma.Items.Components public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { + if (correctionTimer > 0.0f) + { + StartDelayedCorrection(type, msg.ExtractBits(16 + 1 + 15 + 8 + 8), sendingTime); + return; + } + Temperature = msg.ReadRangedSingle(0.0f, 10000.0f, 16); AutoTemp = msg.ReadBoolean(); - ShutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 8); + ShutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 15); CoolingRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); FissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index 9f941a761..53be9de36 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -146,6 +146,7 @@ namespace Barotrauma.Items.Components if (GameMain.Client != null) { item.CreateClientEvent(this); + correctionTimer = CorrectionDelay; } else if (GameMain.Server != null) { @@ -597,6 +598,8 @@ namespace Barotrauma.Items.Components public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { + long msgStartPos = msg.Position; + bool autoPilot = msg.ReadBoolean(); Vector2 newTargetVelocity = targetVelocity; bool maintainPos = false; @@ -622,6 +625,14 @@ namespace Barotrauma.Items.Components newTargetVelocity = new Vector2(msg.ReadFloat(), msg.ReadFloat()); } + if (correctionTimer > 0.0f) + { + int msgLength = (int)(msg.Position - msgStartPos); + msg.Position = msgStartPos; + StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime); + return; + } + AutoPilot = autoPilot; if (!AutoPilot) diff --git a/Subsurface/Source/Items/Components/Power/PowerContainer.cs b/Subsurface/Source/Items/Components/Power/PowerContainer.cs index 55d587769..3334b2c7c 100644 --- a/Subsurface/Source/Items/Components/Power/PowerContainer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerContainer.cs @@ -100,9 +100,14 @@ namespace Barotrauma.Items.Components RechargeSpeed = rechargeSpeed - maxRechargeSpeed * 0.1f; if (GameMain.Server != null) + { item.CreateServerEvent(this); + } else if (GameMain.Client != null) + { item.CreateClientEvent(this); + correctionTimer = CorrectionDelay; + } return true; }; @@ -113,9 +118,14 @@ namespace Barotrauma.Items.Components RechargeSpeed = rechargeSpeed + maxRechargeSpeed * 0.1f; if (GameMain.Server != null) + { item.CreateServerEvent(this); + } else if (GameMain.Client != null) + { item.CreateClientEvent(this); + correctionTimer = CorrectionDelay; + } return true; }; @@ -269,7 +279,6 @@ namespace Barotrauma.Items.Components public void ClientWrite(NetBuffer msg, object[] extraData) { - DebugConsole.NewMessage("writing recharge speed " + (rechargeSpeed / MaxRechargeSpeed * 10), Color.White); msg.WriteRangedInteger(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10)); } @@ -277,8 +286,6 @@ namespace Barotrauma.Items.Components { float newRechargeSpeed = msg.ReadRangedInteger(0,10) / 10.0f * maxRechargeSpeed; - DebugConsole.NewMessage("received recharge speed " + newRechargeSpeed, Color.White); - if (item.CanClientAccess(c)) { RechargeSpeed = newRechargeSpeed; @@ -297,6 +304,12 @@ namespace Barotrauma.Items.Components public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { + if (correctionTimer > 0.0f) + { + StartDelayedCorrection(type, msg.ExtractBits(4 + 8), sendingTime); + return; + } + RechargeSpeed = msg.ReadRangedInteger(0, 10) / 10.0f * maxRechargeSpeed; Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity; } diff --git a/Subsurface/Source/Utils/ToolBox.cs b/Subsurface/Source/Utils/ToolBox.cs index 2e60c0b6a..bbf2aa6c5 100644 --- a/Subsurface/Source/Utils/ToolBox.cs +++ b/Subsurface/Source/Utils/ToolBox.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework; +using Lidgren.Network; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Globalization; @@ -593,5 +594,19 @@ namespace Barotrauma return ""; } } + + /// + /// Reads a number of bits from the buffer and inserts them to a new NetBuffer instance + /// + public static NetBuffer ExtractBits(this NetBuffer originalBuffer, int numberOfBits) + { + var buffer = new NetBuffer(); + byte[] data = new byte[(int)Math.Ceiling(numberOfBits / (double)8)]; + + originalBuffer.ReadBits(data, 0, numberOfBits); + buffer.Write(data); + + return buffer; + } } }