From 058a269ecb6bd16f60e59aa743cb6bf33d355bb2 Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 20 Feb 2016 13:00:05 +0200 Subject: [PATCH] More easily readable netstats (B/kB/MB instead of just B), scrollable netstats client list, possible to wear multiple items in same limb, handcuffs --- Subsurface/Content/Characters/Human/human.xml | 9 +- Subsurface/Content/Items/Clothes/clothes.xml | 18 +++ .../Content/Items/Clothes/securitygear.png | Bin 12408 -> 13775 bytes .../Animation/HumanoidAnimController.cs | 58 ++++++-- Subsurface/Source/Characters/Character.cs | 133 ++++++++++-------- Subsurface/Source/Characters/CharacterHUD.cs | 6 +- Subsurface/Source/Characters/Limb.cs | 57 ++++---- Subsurface/Source/GameSession/CrewManager.cs | 2 +- Subsurface/Source/Items/CharacterInventory.cs | 16 +-- .../Items/Components/Holdable/Propulsion.cs | 2 +- .../Source/Items/Components/Wearable.cs | 33 +++-- Subsurface/Source/Map/FireSource.cs | 2 +- Subsurface/Source/Networking/GameServer.cs | 56 ++++++-- Subsurface/Source/Networking/NetStats.cs | 12 +- Subsurface/Source/Utils/MathUtils.cs | 49 +++++++ Subsurface_Solution.v12.suo | Bin 819712 -> 869888 bytes 16 files changed, 305 insertions(+), 148 deletions(-) diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml index c4d03a9ca..28e4be637 100644 --- a/Subsurface/Content/Characters/Human/human.xml +++ b/Subsurface/Content/Characters/Human/human.xml @@ -75,20 +75,17 @@ - - - - + - + - + diff --git a/Subsurface/Content/Items/Clothes/clothes.xml b/Subsurface/Content/Items/Clothes/clothes.xml index 84d4c66d4..59237a2c2 100644 --- a/Subsurface/Content/Items/Clothes/clothes.xml +++ b/Subsurface/Content/Items/Clothes/clothes.xml @@ -95,6 +95,24 @@ + + + + + + + + + + + + + + oW`n7%V)>N{IzzpAtc=o#2 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 BFA@L% literal 12408 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 0) { @@ -134,10 +164,10 @@ namespace Barotrauma return; } - if (Anim != Animation.UsingConstruction) ResetPullJoints(); - if (TargetDir != dir) Flip(); + if (Anim != Animation.UsingConstruction) ResetPullJoints(); + if (SimplePhysicsEnabled) { UpdateStandingSimple(); @@ -815,7 +845,17 @@ namespace Barotrauma target.AnimController.IgnorePlatforms = IgnorePlatforms; - target.AnimController.TargetMovement = TargetMovement; + + if (target.Stun > 0.0f || target.IsDead) + { + target.AnimController.TargetMovement = TargetMovement; + } + else if (target is AICharacter) + { + target.AnimController.TargetMovement = Vector2.Lerp(target.AnimController.TargetMovement, (character.SimPosition + Vector2.UnitX*Dir) - target.SimPosition, 0.5f); + } + + } public override void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle) @@ -994,12 +1034,14 @@ namespace Barotrauma case LimbType.LeftArm: case LimbType.RightHand: case LimbType.RightArm: - difference = limb.body.SimPosition - torso.SimPosition; - difference = Vector2.Transform(difference, torsoTransform); - difference.Y = -difference.Y; - - TrySetLimbPosition(limb, limb.SimPosition, torso.SimPosition + Vector2.Transform(difference, -torsoTransform)); + if (!limb.pullJoint.Enabled) + { + difference = limb.body.SimPosition - torso.SimPosition; + difference = Vector2.Transform(difference, torsoTransform); + difference.Y = -difference.Y; + TrySetLimbPosition(limb, limb.SimPosition, torso.SimPosition + Vector2.Transform(difference, -torsoTransform)); + } limb.body.SetTransform(limb.body.SimPosition, -limb.body.Rotation); break; default: diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 80628d056..19204dfcd 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -149,6 +149,19 @@ namespace Barotrauma get { return inventory; } } + private float lockHandsTimer; + public bool LockHands + { + get + { + return lockHandsTimer > 0.0f; + } + set + { + lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f); + } + } + public Vector2 CursorPosition { get { return cursorPosition; } @@ -244,7 +257,7 @@ namespace Barotrauma if (!MathUtils.IsValid(value)) return; health = MathHelper.Clamp(value, 0.0f, maxHealth); } - } + } public float MaxHealth { @@ -304,14 +317,6 @@ namespace Barotrauma public bool IsDead { get { return isDead; } - //set - //{ - // if (isDead == value) return; - // if (isDead) - // { - // Revive(false); - // } - //} } public CauseOfDeath CauseOfDeath @@ -319,6 +324,14 @@ namespace Barotrauma get { return causeOfDeath; } } + public bool CanBeSelected + { + get + { + return isDead || Stun > 0.0f || LockHands; + } + } + public override Vector2 SimPosition { get { return AnimController.RefLimb.SimPosition; } @@ -658,8 +671,7 @@ namespace Barotrauma if (selectedCharacter!=null) { - if (Vector2.Distance(selectedCharacter.SimPosition, SimPosition) > 3.0f || - (!selectedCharacter.isDead && selectedCharacter.Stun <= 0.0f)) + if (Vector2.Distance(selectedCharacter.SimPosition, SimPosition) > 2.0f || !selectedCharacter.CanBeSelected) { DeselectCharacter(); } @@ -765,9 +777,7 @@ namespace Barotrauma selectedCharacter = character; - if (createNetworkEvent) - new NetworkEvent(NetworkEventType.SelectCharacter, ID, true, selectedCharacter.ID); - + if (createNetworkEvent) new NetworkEvent(NetworkEventType.SelectCharacter, ID, true, selectedCharacter.ID); } private void DeselectCharacter(bool createNetworkEvent = true) @@ -820,8 +830,6 @@ namespace Barotrauma (AnimController.CurrentHull.LethalPressure - 50.0f) / 50.0f); } cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (Submarine == null ? 400.0f : 250.0f)+pressureEffect, 0.05f); - - } cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition); @@ -844,69 +852,68 @@ namespace Barotrauma } - //find the closest item if selectkey has been hit, or if the Character is being - //controlled by the player (in order to highlight it) - - if (findClosestTimer <= 0.0f || Screen.Selected == GameMain.EditMapScreen) + if (!LockHands) { - closestCharacter = FindClosestCharacter(mouseSimPos); - if (closestCharacter != null && closestCharacter.info==null) - { - closestCharacter = null; - } + //find the closest item if selectkey has been hit, or if the Character is being + //controlled by the player (in order to highlight it) - closestItem = FindClosestItem(mouseSimPos); - - if (closestCharacter != null && closestItem != null) + if (findClosestTimer <= 0.0f || Screen.Selected == GameMain.EditMapScreen) { - if (Vector2.Distance(closestCharacter.SimPosition, mouseSimPos) < Vector2.Distance(closestItem.SimPosition, mouseSimPos)) - { - if (selectedConstruction != closestItem) closestItem = null; - } - else + closestCharacter = FindClosestCharacter(mouseSimPos); + if (closestCharacter != null && closestCharacter.info==null) { closestCharacter = null; } + + closestItem = FindClosestItem(mouseSimPos); + + if (closestCharacter != null && closestItem != null) + { + if (Vector2.Distance(closestCharacter.SimPosition, mouseSimPos) < Vector2.Distance(closestItem.SimPosition, mouseSimPos)) + { + if (selectedConstruction != closestItem) closestItem = null; + } + else + { + closestCharacter = null; + } + } + + findClosestTimer = 0.1f; + } + else + { + findClosestTimer -= deltaTime; } - findClosestTimer = 0.1f; - } - else - { - findClosestTimer -= deltaTime; - } - - if (selectedCharacter == null) - { - if (closestItem != null) + if (selectedCharacter == null && closestItem != null) { closestItem.IsHighlighted = true; - if (closestItem.Pick(this)) + if (!LockHands && closestItem.Pick(this)) { new NetworkEvent(NetworkEventType.PickItem, ID, true, new int[] - { - closestItem.ID, - IsKeyHit(InputType.Select) ? 1 : 0, - IsKeyHit(InputType.Use) ? 1 : 0 - }); + { + closestItem.ID, + IsKeyHit(InputType.Select) ? 1 : 0, + IsKeyHit(InputType.Use) ? 1 : 0 + }); + } + } + + if (IsKeyHit(InputType.Select)) + { + if (selectedCharacter != null) + { + DeselectCharacter(); + } + else if (closestCharacter != null && closestCharacter.IsHumanoid && closestCharacter.CanBeSelected) + { + SelectCharacter(closestCharacter); } } } - if (IsKeyHit(InputType.Select)) - { - if (selectedCharacter != null) - { - DeselectCharacter(); - } - else if (closestCharacter != null && closestCharacter.IsHumanoid && - (closestCharacter.isDead || closestCharacter.AnimController.StunTimer > 0.0f)) - { - SelectCharacter(closestCharacter); - } - } - DisableControls = false; } @@ -1003,6 +1010,8 @@ namespace Barotrauma Health -= bleeding*deltaTime; if (health <= 0.0f) Kill(CauseOfDeath.Bloodloss, false); + + if (!IsDead) LockHands = false; } diff --git a/Subsurface/Source/Characters/CharacterHUD.cs b/Subsurface/Source/Characters/CharacterHUD.cs index f35fb466a..c917b6eaf 100644 --- a/Subsurface/Source/Characters/CharacterHUD.cs +++ b/Subsurface/Source/Characters/CharacterHUD.cs @@ -56,16 +56,16 @@ namespace Barotrauma DrawStatusIcons(spriteBatch, character); - if (character.Inventory != null) character.Inventory.DrawOwn(spriteBatch); + if (character.Inventory != null && !character.LockHands) character.Inventory.DrawOwn(spriteBatch, Vector2.Zero); if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory!=null) { - character.SelectedCharacter.Inventory.Draw(spriteBatch); + character.SelectedCharacter.Inventory.DrawOwn(spriteBatch, new Vector2(GameMain.GraphicsWidth - 310, 0.0f)); //if (Vector2.Distance(selectedCharacter.SimPosition, SimPosition) > 2.0f) selectedCharacter = null; } - if (character.ClosestCharacter != null && (character.ClosestCharacter.IsDead || character.ClosestCharacter.Stun > 0.0f)) + if (character.ClosestCharacter != null && character.ClosestCharacter.CanBeSelected) { Vector2 startPos = character.DrawPosition + (character.ClosestCharacter.DrawPosition - character.DrawPosition) * 0.7f; startPos = cam.WorldToScreen(startPos); diff --git a/Subsurface/Source/Characters/Limb.cs b/Subsurface/Source/Characters/Limb.cs index 4463aa0b7..37ec28622 100644 --- a/Subsurface/Source/Characters/Limb.cs +++ b/Subsurface/Source/Characters/Limb.cs @@ -6,6 +6,7 @@ using FarseerPhysics.Dynamics.Joints; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Barotrauma.Items.Components; +using System.Collections.Generic; namespace Barotrauma { @@ -64,8 +65,7 @@ namespace Barotrauma private Direction dir; - private Wearable wearingItem; - private WearableSprite wearingItemSprite; + private List wearingItems; private Vector2 animTargetPos; @@ -172,21 +172,23 @@ namespace Barotrauma // set { bleeding = MathHelper.Clamp(value, 0.0f, 100.0f); } //} - public Wearable WearingItem + public List WearingItems { - get { return wearingItem; } - set { wearingItem = value; } + get { return wearingItems; } + set { wearingItems = value; } } - public WearableSprite WearingItemSprite - { - get { return wearingItemSprite; } - set { wearingItemSprite = value; } - } + //public WearableSprite WearingItemSprite + //{ + // get { return wearingItemSprite; } + // set { wearingItemSprite = value; } + //} public Limb (Character character, XElement element, float scale = 1.0f) { this.character = character; + + WearingItems = new List(); dir = Direction.Right; @@ -327,13 +329,16 @@ namespace Barotrauma totalArmorValue += armorValue; } - if (wearingItem!=null && - wearingItem.ArmorValue>0.0f && - SectorHit(wearingItem.ArmorSectorLimits, position)) + foreach (WearableSprite wearable in wearingItems) { - hitArmor = true; - totalArmorValue += wearingItem.ArmorValue; - } + if (wearable.WearableComponent.ArmorValue > 0.0f && + SectorHit(wearable.WearableComponent.ArmorSectorLimits, position)) + { + hitArmor = true; + totalArmorValue += wearable.WearableComponent.ArmorValue; + } + } + if (hitArmor) { @@ -456,7 +461,7 @@ namespace Barotrauma body.Dir = Dir; - if (wearingItem == null || !wearingItemSprite.HideLimb) + if (wearingItems.Find(w => w != null && w.HideLimb) == null) { body.Draw(spriteBatch, sprite, color, null, scale); } @@ -464,32 +469,32 @@ namespace Barotrauma { body.UpdateDrawPosition(); } - - if (wearingItem != null) + + foreach (WearableSprite wearable in wearingItems) { SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; - Vector2 origin = wearingItemSprite.Sprite.Origin; - if (body.Dir == -1.0f) origin.X = wearingItemSprite.Sprite.SourceRect.Width - origin.X; + Vector2 origin = wearable.Sprite.Origin; + if (body.Dir == -1.0f) origin.X = wearable.Sprite.SourceRect.Width - origin.X; float depth = sprite.Depth - 0.000001f; - if (wearingItemSprite.DepthLimb!=LimbType.None) + if (wearable.DepthLimb != LimbType.None) { - Limb depthLimb = character.AnimController.GetLimb(wearingItemSprite.DepthLimb); - if (depthLimb!=null) + Limb depthLimb = character.AnimController.GetLimb(wearable.DepthLimb); + if (depthLimb != null) { depth = depthLimb.sprite.Depth - 0.000001f; } } - wearingItemSprite.Sprite.Draw(spriteBatch, + wearable.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), color, origin, -body.DrawRotation, scale, spriteEffect, depth); } - + if (damage>0.0f && damagedSprite!=null) { SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; diff --git a/Subsurface/Source/GameSession/CrewManager.cs b/Subsurface/Source/GameSession/CrewManager.cs index 225faeb1e..d1b0ba614 100644 --- a/Subsurface/Source/GameSession/CrewManager.cs +++ b/Subsurface/Source/GameSession/CrewManager.cs @@ -71,7 +71,7 @@ namespace Barotrauma //listBox.Select(selection); Character character = selection as Character; - if (character == null) return false; + if (character == null || character.IsDead) return false; if (characters.Contains(character)) { diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index 207ca6863..b7c9697d9 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -210,7 +210,7 @@ namespace Barotrauma return TryPutItem(item, new List() {placeToSlots}, createNetworkEvent); } - public void DrawOwn(SpriteBatch spriteBatch) + public void DrawOwn(SpriteBatch spriteBatch, Vector2 offset) { string toolTip = ""; Rectangle highlightedSlot = Rectangle.Empty; @@ -227,8 +227,8 @@ namespace Barotrauma for (int i = 0; i < capacity; i++) { - slotRect.X = (int)SlotPositions[i].X; - slotRect.Y = (int)SlotPositions[i].Y; + slotRect.X = (int)(SlotPositions[i].X + offset.X); + slotRect.Y = (int)(SlotPositions[i].Y + offset.Y); if (i==1) //head { @@ -248,8 +248,8 @@ namespace Barotrauma for (int i = 0; i < capacity; i++) { - slotRect.X = (int)SlotPositions[i].X; - slotRect.Y = (int)SlotPositions[i].Y; + slotRect.X = (int)(SlotPositions[i].X + offset.X); + slotRect.Y = (int)(SlotPositions[i].Y + offset.Y); bool multiSlot = false; //skip if the item is in multiple slots @@ -295,8 +295,8 @@ namespace Barotrauma //check if the item is in multiple slots if (Items[i] != null) { - slotRect.X = (int)SlotPositions[i].X; - slotRect.Y = (int)SlotPositions[i].Y; + slotRect.X = (int)(SlotPositions[i].X + offset.X); + slotRect.Y = (int)(SlotPositions[i].Y + offset.Y); slotRect.Width = 40; slotRect.Height = 40; @@ -310,7 +310,7 @@ namespace Barotrauma { multiSlot = true; slotRect = Rectangle.Union( - new Rectangle((int)SlotPositions[n].X, (int)SlotPositions[n].Y, rectWidth, rectHeight), slotRect); + new Rectangle((int)(SlotPositions[n].X+offset.X), (int)(SlotPositions[n].Y+offset.Y), rectWidth, rectHeight), slotRect); } } } diff --git a/Subsurface/Source/Items/Components/Holdable/Propulsion.cs b/Subsurface/Source/Items/Components/Holdable/Propulsion.cs index bd3f523ad..5b29339db 100644 --- a/Subsurface/Source/Items/Components/Holdable/Propulsion.cs +++ b/Subsurface/Source/Items/Components/Holdable/Propulsion.cs @@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components { foreach (Limb limb in character.AnimController.Limbs) { - if (limb.WearingItem==null || limb.WearingItem.Item != item) continue; + if (limb.WearingItems.Find(w => w.WearableComponent.Item != this.item)==null) continue; limb.body.ApplyForce(propulsion); } diff --git a/Subsurface/Source/Items/Components/Wearable.cs b/Subsurface/Source/Items/Components/Wearable.cs index 6f267bc20..36539d95e 100644 --- a/Subsurface/Source/Items/Components/Wearable.cs +++ b/Subsurface/Source/Items/Components/Wearable.cs @@ -12,8 +12,11 @@ namespace Barotrauma.Items.Components public bool HideLimb; public LimbType DepthLimb; - public WearableSprite(Sprite sprite, bool hideLimb, LimbType depthLimb = LimbType.None) + public Wearable WearableComponent; + + public WearableSprite(Wearable item, Sprite sprite, bool hideLimb, LimbType depthLimb = LimbType.None) { + WearableComponent = item; Sprite = sprite; HideLimb = hideLimb; @@ -85,7 +88,7 @@ namespace Barotrauma.Items.Components spritePath = Path.GetDirectoryName( item.Prefab.ConfigFile)+"/"+spritePath; var sprite = new Sprite(subElement, "", spritePath); - wearableSprites[i] = new WearableSprite(sprite, ToolBox.GetAttributeBool(subElement, "hidelimb", false), + wearableSprites[i] = new WearableSprite(this, sprite, ToolBox.GetAttributeBool(subElement, "hidelimb", false), (LimbType)Enum.Parse(typeof(LimbType), ToolBox.GetAttributeString(subElement, "depthlimb", "None"), true)); @@ -105,10 +108,10 @@ namespace Barotrauma.Items.Components if (equipLimb == null) continue; //something is already on the limb -> unequip it - if (equipLimb.WearingItem != null && equipLimb.WearingItem != this) - { - equipLimb.WearingItem.Unequip(character); - } + //if (equipLimb.WearingItem != null && equipLimb.WearingItem != this) + //{ + // equipLimb.WearingItem.Unequip(character); + //} //sprite[i].Depth = equipLimb.sprite.Depth - 0.001f; @@ -117,8 +120,7 @@ namespace Barotrauma.Items.Components IsActive = true; limb[i] = equipLimb; - equipLimb.WearingItem = this; - equipLimb.WearingItemSprite = wearableSprites[i]; + equipLimb.WearingItems.Add(wearableSprites[i]); } } @@ -140,11 +142,20 @@ namespace Barotrauma.Items.Components Limb equipLimb = character.AnimController.GetLimb(limbType[i]); if (equipLimb == null) continue; - if (equipLimb.WearingItem != this) continue; + //foreach (WearableSprite wearable in equipLimb.WearingItems) + //{ + // if (wearable != wearableSprites[i]) continue; + + // equipLimb.WearingItems.Remove(wearableSprites[i]); + //} + + equipLimb.WearingItems.RemoveAll(w=> w!=null && w==wearableSprites[i]); + + //if (equipLimb.WearingItem != this) continue; limb[i] = null; - equipLimb.WearingItem = null; - equipLimb.WearingItemSprite = null; + //equipLimb.WearingItem = null; + //equipLimb.WearingItemSprite = null; } IsActive = false; diff --git a/Subsurface/Source/Map/FireSource.cs b/Subsurface/Source/Map/FireSource.cs index 4800ab0d5..24898c8bf 100644 --- a/Subsurface/Source/Map/FireSource.cs +++ b/Subsurface/Source/Map/FireSource.cs @@ -236,7 +236,7 @@ namespace Barotrauma float dmg = (float)Math.Sqrt(size.X) * deltaTime / c.AnimController.Limbs.Count(); foreach (Limb limb in c.AnimController.Limbs) { - if (limb.WearingItem != null && limb.WearingItem.Item.FireProof) continue; + if (limb.WearingItems.Find(w => w!=null && w.WearableComponent.Item.FireProof)!=null) continue; limb.Burnt += dmg * 10.0f; c.AddDamage(limb.SimPosition, DamageType.None, dmg, 0,0,false); } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 3719f4b10..ce544257f 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -35,6 +35,8 @@ namespace Barotrauma.Networking private ServerLog log; private GUIButton showLogButton; + private GUIScrollBar clientListScrollBar; + public TraitorManager TraitorManager; public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10) @@ -1177,33 +1179,57 @@ namespace Barotrauma.Networking if (!ShowNetStats) return; int width = 200, height = 300; - int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight*0.3f); + int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight * 0.3f); - GUI.DrawRectangle(spriteBatch, new Rectangle(x,y,width,height), Color.Black*0.7f, true); - spriteBatch.DrawString(GUI.Font, "Network statistics:", new Vector2(x+10, y+10), Color.White); + + if (clientListScrollBar == null) + { + clientListScrollBar = new GUIScrollBar(new Rectangle(x + width - 10, y, 10, height), GUI.Style, 1.0f); + } + + + GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black * 0.7f, true); + spriteBatch.DrawString(GUI.Font, "Network statistics:", new Vector2(x + 10, y + 10), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Connections: "+server.ConnectionsCount, new Vector2(x + 10, y + 30), Color.White); - spriteBatch.DrawString(GUI.SmallFont, "Received bytes: " + server.Statistics.ReceivedBytes, new Vector2(x + 10, y + 45), Color.White); + spriteBatch.DrawString(GUI.SmallFont, "Received bytes: " + MathUtils.GetBytesReadable(server.Statistics.ReceivedBytes), new Vector2(x + 10, y + 45), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Received packets: " + server.Statistics.ReceivedPackets, new Vector2(x + 10, y + 60), Color.White); - spriteBatch.DrawString(GUI.SmallFont, "Sent bytes: " + server.Statistics.SentBytes, new Vector2(x + 10, y + 75), Color.White); + spriteBatch.DrawString(GUI.SmallFont, "Sent bytes: " + MathUtils.GetBytesReadable(server.Statistics.SentBytes), new Vector2(x + 10, y + 75), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Sent packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White); int resentMessages = 0; - y += 110; - foreach (Client c in ConnectedClients) - { - spriteBatch.DrawString(GUI.SmallFont, c.name + ":", new Vector2(x + 10, y), Color.White); - spriteBatch.DrawString(GUI.SmallFont, "- avg roundtrip " + c.Connection.AverageRoundtripTime+" s", new Vector2(x + 20, y + 15), Color.White); - spriteBatch.DrawString(GUI.SmallFont, "- resent messages " + c.Connection.Statistics.ResentMessages, new Vector2(x + 20, y + 30), Color.White); + int clientListHeight = ConnectedClients.Count() * 40; + float scrollBarHeight = (height - 110) / (float)Math.Max(clientListHeight, 110); - resentMessages += (int)c.Connection.Statistics.ResentMessages; - - y += 50; + if (clientListScrollBar.BarSize != scrollBarHeight) + { + clientListScrollBar.BarSize = scrollBarHeight; } - netStats.AddValue(NetStats.NetStatType.ResentMessages, resentMessages); + int startY = y + 110; + y = (startY - (int)(clientListScrollBar.BarScroll * (clientListHeight-(height - 110)))); + foreach (Client c in ConnectedClients) + { + Color clientColor = c.Connection.AverageRoundtripTime > 0.3f ? Color.Red : Color.White; + + if (y >= startY && y < startY + height - 120) + { + spriteBatch.DrawString(GUI.SmallFont, c.name + ":", new Vector2(x + 10, y), clientColor); + spriteBatch.DrawString(GUI.SmallFont, "Ping: " + (int)(c.Connection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x + width - 100, y), clientColor); + } + if (y + 10 >= startY && y < startY + height - 130) spriteBatch.DrawString(GUI.SmallFont, "Resent messages: " + c.Connection.Statistics.ResentMessages, new Vector2(x + 10, y + 10), clientColor); + + resentMessages += (int)c.Connection.Statistics.ResentMessages; + + y += 40; + } + + clientListScrollBar.Update(1.0f / 60.0f); + clientListScrollBar.Draw(spriteBatch); + + netStats.AddValue(NetStats.NetStatType.ResentMessages, Math.Max(resentMessages, 0)); netStats.AddValue(NetStats.NetStatType.SentBytes, server.Statistics.SentBytes); netStats.AddValue(NetStats.NetStatType.ReceivedBytes, server.Statistics.ReceivedBytes); diff --git a/Subsurface/Source/Networking/NetStats.cs b/Subsurface/Source/Networking/NetStats.cs index 650012ae5..8fc3c3ddc 100644 --- a/Subsurface/Source/Networking/NetStats.cs +++ b/Subsurface/Source/Networking/NetStats.cs @@ -71,14 +71,14 @@ namespace Barotrauma.Networking graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, Color.Red); - spriteBatch.DrawString(GUI.SmallFont, - "Peak received: "+graphs[(int)NetStatType.ReceivedBytes].LargestValue()+" bytes/s " + - "Avg received: " + graphs[(int)NetStatType.ReceivedBytes].Average() + " bytes/s", - new Vector2(rect.X + 10, rect.Y+10), Color.Cyan); + spriteBatch.DrawString(GUI.SmallFont, + "Peak received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].LargestValue()) + "/s " + + "Avg received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].Average()) + "/s", + new Vector2(rect.X + 10, rect.Y + 10), Color.Cyan); - spriteBatch.DrawString(GUI.SmallFont, "Peak sent: " + graphs[(int)NetStatType.SentBytes].LargestValue() + " bytes/s " + - "Avg sent: " + graphs[(int)NetStatType.SentBytes].Average() + " bytes/s", + spriteBatch.DrawString(GUI.SmallFont, "Peak sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].LargestValue()) + "/s " + + "Avg sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].Average()) + " bytes/s", new Vector2(rect.X + 10, rect.Y + 30), Color.Orange); spriteBatch.DrawString(GUI.SmallFont, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s", diff --git a/Subsurface/Source/Utils/MathUtils.cs b/Subsurface/Source/Utils/MathUtils.cs index ddba37391..6f8c5788b 100644 --- a/Subsurface/Source/Utils/MathUtils.cs +++ b/Subsurface/Source/Utils/MathUtils.cs @@ -273,6 +273,55 @@ namespace Barotrauma return segments; } + + // Returns the human-readable file size for an arbitrary, 64-bit file size + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + public static string GetBytesReadable(long i) + { + // Get absolute value + long absolute_i = (i < 0 ? -i : i); + // Determine the suffix and readable value + string suffix; + double readable; + if (absolute_i >= 0x1000000000000000) // Exabyte + { + suffix = "EB"; + readable = (i >> 50); + } + else if (absolute_i >= 0x4000000000000) // Petabyte + { + suffix = "PB"; + readable = (i >> 40); + } + else if (absolute_i >= 0x10000000000) // Terabyte + { + suffix = "TB"; + readable = (i >> 30); + } + else if (absolute_i >= 0x40000000) // Gigabyte + { + suffix = "GB"; + readable = (i >> 20); + } + else if (absolute_i >= 0x100000) // Megabyte + { + suffix = "MB"; + readable = (i >> 10); + } + else if (absolute_i >= 0x400) // Kilobyte + { + suffix = "KB"; + readable = i; + } + else + { + return i.ToString("0 B"); // Byte + } + // Divide by 1024 to get fractional value + readable = (readable / 1024); + // Return formatted number with suffix + return readable.ToString("0.# ") + suffix; + } } class CompareCCW : IComparer diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 3427d79bfea8531bfbc165d3a1a375cec9cbfb42..64698d3020185c794de6fffd43d90a3fe46777a4 100644 GIT binary patch delta 26401 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 delta 16622 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