From ab7c39071c0ba2f8177ef18e61eaa5f35cc2611a Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 14 Nov 2015 00:09:56 +0200 Subject: [PATCH] Input syncing between clients, inventory sync bugfix, structure sync bugfix, settings button highlight bugfix, spectating bugfixes, in-game chatbox not visible when joining a server when the round is running --- Subsurface/Source/Camera.cs | 16 ++- Subsurface/Source/Characters/Character.cs | 102 +++++++----------- Subsurface/Source/DebugConsole.cs | 10 ++ Subsurface/Source/GUI/GUIComponent.cs | 2 +- Subsurface/Source/Items/CharacterInventory.cs | 2 +- Subsurface/Source/Items/Inventory.cs | 2 +- Subsurface/Source/Map/Structure.cs | 31 +++--- Subsurface/Source/Networking/GameServer.cs | 5 +- Subsurface/Source/Networking/NetConfig.cs | 3 +- Subsurface/Source/Networking/NetworkEvent.cs | 2 +- Subsurface/Source/Networking/NetworkMember.cs | 2 +- .../Source/Networking/ReliableSender.cs | 21 ++-- Subsurface/Source/PlayerInput.cs | 6 ++ Subsurface/Source/Screens/NetLobbyScreen.cs | 2 +- Subsurface_Solution.v12.suo | Bin 753152 -> 723968 bytes 15 files changed, 105 insertions(+), 101 deletions(-) diff --git a/Subsurface/Source/Camera.cs b/Subsurface/Source/Camera.cs index a037506bf..d483da785 100644 --- a/Subsurface/Source/Camera.cs +++ b/Subsurface/Source/Camera.cs @@ -163,12 +163,18 @@ namespace Barotrauma Vector2 moveCam = Vector2.Zero; if (targetPos == Vector2.Zero) { - if (PlayerInput.KeyDown(Keys.A)) moveCam.X -= moveSpeed; - if (PlayerInput.KeyDown(Keys.D)) moveCam.X += moveSpeed; - if (PlayerInput.KeyDown(Keys.S)) moveCam.Y -= moveSpeed; - if (PlayerInput.KeyDown(Keys.W)) moveCam.Y += moveSpeed; + if (GUITextBox.KeyboardDispatcher.Subscriber == null) + { + if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f; + if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f; - moveCam = moveCam * deltaTime * 60.0f; + if (PlayerInput.KeyDown(Keys.A)) moveCam.X -= moveSpeed; + if (PlayerInput.KeyDown(Keys.D)) moveCam.X += moveSpeed; + if (PlayerInput.KeyDown(Keys.S)) moveCam.Y -= moveSpeed; + if (PlayerInput.KeyDown(Keys.W)) moveCam.Y += moveSpeed; + + moveCam = moveCam * deltaTime * 60.0f; + } Zoom = MathHelper.Clamp(zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, 0.1f, 2.0f); diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 61ba42294..7d11a8001 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1037,7 +1037,7 @@ namespace Barotrauma public void StartStun(float stunTimer) { - if (stunTimer <= 0.0f) return; + if (stunTimer <= 0.0f || !MathUtils.IsValid(stunTimer)) return; AnimController.ResetPullJoints(); AnimController.StunTimer = Math.Max(AnimController.StunTimer, stunTimer); @@ -1247,25 +1247,31 @@ namespace Barotrauma // i++; //} - message.WriteRangedSingle(MathHelper.Clamp(AnimController.StunTimer,0.0f,60.0f), 0.0f, 60.0f, 8); - message.Write((byte)((health/maxHealth)*255.0f)); - message.Write((byte)(MathHelper.Clamp(oxygen * 2.55f, 0.0f, 255.0f))); + message.Write((byte)((health / maxHealth) * 255.0f)); + + if (AnimController.StunTimer<=0.0f && bleeding<=0.0f && oxygen>99.0f) + { + message.Write(true); + } + else + { + message.Write(false); + + message.WriteRangedSingle(MathHelper.Clamp(AnimController.StunTimer, 0.0f, 60.0f), 0.0f, 60.0f, 8); + + message.Write((byte)(MathHelper.Clamp(oxygen * 2.55f, 0.0f, 255.0f))); + + bleeding = MathHelper.Clamp(bleeding, 0.0f, 5.0f); + message.WriteRangedSingle(bleeding, 0.0f, 5.0f, 8); + + } + return true; case NetworkEventType.EntityUpdate: - var hasInputs = - IsKeyDown(InputType.Left) || - IsKeyDown(InputType.Right) || - IsKeyDown(InputType.Up) || - IsKeyDown(InputType.Down) || - IsKeyDown(InputType.Use) || - IsKeyDown(InputType.Aim); - message.Write(hasInputs); message.Write((float)NetTime.Now); - if (!hasInputs) return true; - message.Write(keys[(int)InputType.Use].DequeueHeld); bool secondaryHeld = keys[(int)InputType.Aim].DequeueHeld; @@ -1278,7 +1284,7 @@ namespace Barotrauma message.Write(keys[(int)InputType.Down].Held); message.Write(keys[(int)InputType.Run].Held); - + if (secondaryHeld) { Vector2 relativeCursorPosition = cursorPosition - Position; @@ -1382,46 +1388,17 @@ namespace Barotrauma inventory.ReadNetworkData(NetworkEventType.InventoryUpdate, message); return; case NetworkEventType.ImportantEntityUpdate: - //foreach (Limb limb in AnimController.Limbs) - //{ - // Vector2 limbPos = limb.SimPosition, vel = Vector2.Zero; - // float rotation = limb.Rotation; + + Health = (message.ReadByte() / 255.0f) * maxHealth; - // try - // { - // limbPos.X = message.ReadRangedSingle(-NetConfig.CharacterIgnoreDistance, NetConfig.CharacterIgnoreDistance, 16); - // limbPos.Y = message.ReadRangedSingle(-NetConfig.CharacterIgnoreDistance, NetConfig.CharacterIgnoreDistance, 16); - - // rotation = message.ReadFloat(); - // } - // catch - // { - // return; - // } - - // if (limb.body != null) - // { - // limb.body.TargetVelocity = limb.body.LinearVelocity; - // limb.body.TargetPosition = limbPos;// +vel * (float)(deltaTime / 60.0); - // limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0); - // limb.body.TargetAngularVelocity = limb.body.AngularVelocity; - // } - - //} - - float newStunTimer = 0.0f, newHealth = 0.0f, newOxygen = 0.0f; - - try - { - newStunTimer = message.ReadRangedSingle(0.0f, 60.0f, 8); - newHealth = (message.ReadByte() / 255.0f) * maxHealth; - newOxygen = (message.ReadByte() / 2.55f); - } - catch { return; } + bool allOk = message.ReadBoolean(); + if (allOk) return; + float newStunTimer = message.ReadRangedSingle(0.0f, 60.0f, 8); StartStun(newStunTimer); - Health = newHealth; - oxygen = newOxygen; + + Oxygen = (message.ReadByte() / 2.55f); + Bleeding = message.ReadRangedSingle(0.0f, 5.0f, 8); return; case NetworkEventType.EntityUpdate: @@ -1434,14 +1411,9 @@ namespace Barotrauma try { - bool hasInputs = message.ReadBoolean(); - sendingTime = message.ReadFloat(); - - if (!hasInputs) - { - if (sendingTime > LastNetworkUpdate) ClearInputs(); - return; - } + sendingTime = message.ReadFloat(); + + if (sendingTime > LastNetworkUpdate) ClearInputs(); actionKeyState = message.ReadBoolean(); secondaryKeyState = message.ReadBoolean(); @@ -1456,13 +1428,19 @@ namespace Barotrauma catch (Exception e) { +#if DEBUG + DebugConsole.ThrowError("Error in Character.ReadNetworkData: " + e.Message); +#endif return; } AnimController.IsStanding = true; - keys[(int)InputType.Use].Held = actionKeyState; - keys[(int)InputType.Aim].Held = secondaryKeyState; + keys[(int)InputType.Use].Held = actionKeyState; + keys[(int)InputType.Use].SetState(false, actionKeyState); + + keys[(int)InputType.Aim].Held = secondaryKeyState; + keys[(int)InputType.Aim].SetState(false, secondaryKeyState); if (sendingTime <= LastNetworkUpdate) return; diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 0cc60d9fc..448528576 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Barotrauma.Networking; +using Barotrauma.Items.Components; namespace Barotrauma { @@ -259,6 +260,15 @@ namespace Barotrauma } } break; + case "power": + Item reactorItem = Item.ItemList.Find(i => i.GetComponent() != null); + if (reactorItem == null) return; + + var reactor = reactorItem.GetComponent(); + reactor.ShutDownTemp = 7000.0f; + reactor.AutoTemp = true; + reactor.Temperature = 5000.0f; + break; case "shake": GameMain.GameScreen.Cam.Shake = 10.0f; break; diff --git a/Subsurface/Source/GUI/GUIComponent.cs b/Subsurface/Source/GUI/GUIComponent.cs index 1bf6fe961..5015214af 100644 --- a/Subsurface/Source/GUI/GUIComponent.cs +++ b/Subsurface/Source/GUI/GUIComponent.cs @@ -375,7 +375,7 @@ namespace Barotrauma public List FindChildren(object userData) { - return children .FindAll(c => c.userData == userData); + return children.FindAll(c => c.userData == userData); } public virtual void ClearChildren() diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index 844f127da..cc32ed0a0 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -381,7 +381,7 @@ namespace Barotrauma Item item = Entity.FindEntityByID(itemId) as Item; if (item == null) continue; - if (items[i] != item) items[i].Drop(character, false); + if (items[i] != item && items[i] != null) items[i].Drop(character, false); TryPutItem(item, i, false); } } diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index ec0ffc18b..3a8284b0a 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -118,7 +118,7 @@ namespace Barotrauma if (item.inventory != null && removeItem) { - item.Drop(); + item.Drop(null, false); item.inventory.RemoveItem(item); } diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 8f0d6cc9a..606ea60ae 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -390,8 +390,7 @@ namespace Barotrauma if (sectionIndex < 0 || sectionIndex > sections.Length - 1) return; - if (GameMain.Client == null) - SetDamage(sectionIndex, sections[sectionIndex].damage + damage); + if (GameMain.Client == null) SetDamage(sectionIndex, sections[sectionIndex].damage + damage); } @@ -453,7 +452,7 @@ namespace Barotrauma if (damage != sections[sectionIndex].damage && Math.Abs(sections[sectionIndex].lastSentDamage - damage)>5.0f) { - new NetworkEvent(NetworkEventType.WallDamage, ID, false); + new NetworkEvent(NetworkEventType.ImportantEntityUpdate, ID, false); //sections[sectionIndex].lastSentDamage = damage; } @@ -642,17 +641,19 @@ namespace Barotrauma { message.Write((float)NetTime.Now); - var updateSections = Array.FindAll(sections, s => s != null && Math.Abs(s.damage - s.lastSentDamage)/Health > 0.01f); + //var updateSections = Array.FindAll(sections, s => s != null && Math.Abs(s.damage - s.lastSentDamage) > 0.01f); - if (updateSections.Length == 0) return false; + //if (updateSections.Length == 0) return false; - Debug.Assert(updateSections.Length<255); + //Debug.Assert(updateSections.Length<255); - message.Write((byte)updateSections.Length); + // message.Write((byte)updateSections.Length); - for (int i = 0; i < updateSections.Length; i++) + for (int i = 0; i < sections.Length; i++) { - message.Write((byte)i); + //if (Math.Abs(sections[i].damage - sections[i].lastSentDamage) < 0.01f) continue; + + //message.Write((byte)i); message.WriteRangedSingle(sections[i].damage / Health, 0.0f, 1.0f, 8); sections[i].lastSentDamage = sections[i].damage; @@ -668,17 +669,19 @@ namespace Barotrauma float updateTime = message.ReadFloat(); if (updateTime < lastUpdate) return; - int sectionCount = message.ReadByte(); + // int sectionCount = message.ReadByte(); - for (int i = 0; i= sections.Length) continue; + //if (sectionIndex < 0 || sectionIndex >= sections.Length) continue; - SetDamage(sectionIndex, damage); + SetDamage(i, damage); } + + lastUpdate = updateTime; } } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 2343dab0c..ddfbaafd8 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -248,7 +248,7 @@ namespace Barotrauma.Networking if (gameStarted && disconnectedClients[i].Character!=null) { - disconnectedClients[i].Character.Remove(); + disconnectedClients[i].Character.Kill(CauseOfDeath.Damage, true); disconnectedClients[i].Character = null; } @@ -609,7 +609,7 @@ namespace Barotrauma.Networking { if (NetworkEvent.Events.Count == 0) return; - List recipients = ConnectedClients.FindAll(c => c.Character != null); + List recipients = ConnectedClients.FindAll(c => c.Character != null || c.Spectating); if (recipients.Count == 0) return; @@ -784,6 +784,7 @@ namespace Barotrauma.Networking var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300); Character.Controlled = null; + myCharacter = null; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.LightManager.LosEnabled = false; diff --git a/Subsurface/Source/Networking/NetConfig.cs b/Subsurface/Source/Networking/NetConfig.cs index ea298bca7..7ca0c7c49 100644 --- a/Subsurface/Source/Networking/NetConfig.cs +++ b/Subsurface/Source/Networking/NetConfig.cs @@ -32,6 +32,7 @@ namespace Barotrauma.Networking public const float IdSendInterval = 0.2f; public const float RerequestInterval = 0.2f; - public const int ResendAttempts = 8; + public const int ReliableMessageBufferSize = 100; + public const int ResendAttempts = 10; } } diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index f14824c73..98a5a9519 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -201,7 +201,7 @@ namespace Barotrauma.Networking } - System.Diagnostics.Debug.WriteLine(e.ToString()); + //System.Diagnostics.Debug.WriteLine(e.ToString()); object data; diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index f194caf36..dc58bc30d 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -229,7 +229,7 @@ namespace Barotrauma.Networking public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { - if (!gameStarted && Screen.Selected != GameMain.GameScreen) return; + if (!gameStarted || Screen.Selected != GameMain.GameScreen) return; inGameHUD.Draw(spriteBatch); } diff --git a/Subsurface/Source/Networking/ReliableSender.cs b/Subsurface/Source/Networking/ReliableSender.cs index 2a9de7300..c535f1a4e 100644 --- a/Subsurface/Source/Networking/ReliableSender.cs +++ b/Subsurface/Source/Networking/ReliableSender.cs @@ -97,14 +97,13 @@ namespace Barotrauma.Networking.ReliableMessages message.Write((byte)PacketTypes.ReliableMessage); message.Write(messageID); - int bufferSize=100; - if (messageBuffer.Count>bufferSize) + if (messageBuffer.Count > NetConfig.ReliableMessageBufferSize) { - - int end = messageCount-bufferSize; - int start = end - (messageBuffer.Count - bufferSize); - if (start<=0) + int end = messageCount - NetConfig.ReliableMessageBufferSize; + int start = end - (messageBuffer.Count - NetConfig.ReliableMessageBufferSize); + + if (start<0) { int wrappedStart = start + ushort.MaxValue; if (wrappedStart==0) wrappedStart = ushort.MaxValue; @@ -119,7 +118,7 @@ namespace Barotrauma.Networking.ReliableMessages } } - for (ushort i = (ushort)Math.Max(start,1); i <= (ushort)Math.Max(end,1); i++) + for (ushort i = (ushort)Math.Max(start,0); i <= (ushort)Math.Max(end,0); i++) { messageBuffer.Remove(i); if (i == ushort.MaxValue) break; @@ -219,12 +218,11 @@ namespace Barotrauma.Networking.ReliableMessages { foreach (var message in missingMessages.Where(m => m.Value.ResendRequestsSent > NetConfig.ResendAttempts).ToList()) { + Debug.WriteLine("Max rerequest attempts reached on message "+message.Value.ID); missingMessages.Remove(message.Key); } - int bufferSize = 20; - - while (missingMessageIds.Count>bufferSize) + while (missingMessageIds.Count>NetConfig.ReliableMessageBufferSize) { ushort id = missingMessageIds.Dequeue(); @@ -245,7 +243,8 @@ namespace Barotrauma.Networking.ReliableMessages resendRequest.Write((byte)PacketTypes.ResendRequest); resendRequest.Write(missingMessage.ID); - receiver.SendMessage(resendRequest, recipient, NetDeliveryMethod.Unreliable); + receiver.SendMessage(resendRequest, recipient, + missingMessage.ResendRequestsSent==0 ? NetDeliveryMethod.ReliableUnordered : NetDeliveryMethod.Unreliable); missingMessage.ResendTimer = Math.Max(recipient.AverageRoundtripTime, NetConfig.RerequestInterval); diff --git a/Subsurface/Source/PlayerInput.cs b/Subsurface/Source/PlayerInput.cs index 1c5214111..2ca454954 100644 --- a/Subsurface/Source/PlayerInput.cs +++ b/Subsurface/Source/PlayerInput.cs @@ -148,6 +148,12 @@ namespace Barotrauma if (held) heldQueue = true; } + public void SetState(bool hit, bool held) + { + if (hit) hitQueue = true; + if (held) heldQueue = true; + } + public bool Dequeue { get diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index a1f409a8e..065732e91 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -296,6 +296,7 @@ namespace Barotrauma infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "startButton")); + infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "settingsButton")); infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "spectateButton")); playerList.Parent.RemoveChild(playerList.Parent.children.Find(c => c.UserData as string == "banListButton")); @@ -313,7 +314,6 @@ namespace Barotrauma GUIButton settingsButton = new GUIButton(new Rectangle(-100, 0, 80, 30), "Settings", Alignment.BottomRight, GUI.Style, infoFrame); settingsButton.OnClicked = GameMain.Server.ToggleSettingsFrame; - settingsButton.CanBeSelected = false; settingsButton.UserData = "settingsButton"; var banListButton = new GUIButton(new Rectangle(0, 30, 100, 20), "Banned IPs", Alignment.BottomRight, GUI.Style, playerList.Parent); diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 24d32ac2e19030c45df8fcf78764771803e38ada..5a7ad99c68020aa873507a1f7a0599d44c8d6fa0 100644 GIT binary patch delta 12318 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 MnT(1*WVhe(pZ;}2NdN!< delta 10822 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 j5L