(3a5d98b) v0.9.6.0

This commit is contained in:
Regalis
2019-12-17 14:38:24 +01:00
parent 5c95c53118
commit a3569b8bf0
95 changed files with 1579 additions and 728 deletions
@@ -85,8 +85,7 @@ namespace Barotrauma
{
if (character.Inventory != null)
{
if (!character.LockHands && character.Stun < 0.1f &&
(character.SelectedConstruction == null || character.SelectedConstruction?.GetComponent<Controller>()?.User != character))
if (!LockInventory(character))
{
character.Inventory.Update(deltaTime, cam);
}
@@ -325,7 +324,7 @@ namespace Barotrauma
}
if (character.Inventory != null && !character.LockHands)
{
character.Inventory.Locked = (character.SelectedConstruction?.GetComponent<Controller>()?.User == character);
character.Inventory.Locked = LockInventory(character);
character.Inventory.DrawOwn(spriteBatch);
character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ?
CharacterInventory.Layout.Default :
@@ -368,6 +367,17 @@ namespace Barotrauma
}
}
private static bool LockInventory(Character character)
{
if (character?.Inventory == null || !character.AllowInput || character.LockHands) { return true; }
//lock if using a controller, except if we're also using a connection panel in the same item
return
character.SelectedConstruction != null &&
character.SelectedConstruction?.GetComponent<Controller>()?.User == character &&
character.SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != character;
}
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f)
{
if (order.TargetAllCharacters)
@@ -2041,6 +2041,16 @@ namespace Barotrauma
commands.Add(new Command("spawnsub", "spawnsub [subname]: Spawn a submarine at the position of the cursor", (string[] args) =>
{
if (GameMain.NetworkMember != null)
{
ThrowError("Cannot spawn additional submarines during a multiplayer session.");
return;
}
if (args.Length == 0)
{
ThrowError("Please enter the name of the submarine.");
return;
}
try
{
Submarine spawnedSub = Submarine.Load(args[0], false);
@@ -220,6 +220,7 @@ namespace Barotrauma
msgHolder.RectTransform.Resize(new Point(msgHolder.Rect.Width, msgHolder.Children.Sum(c => c.Rect.Height) + (int)(10 * GUI.Scale)), resizeChildren: false);
msgHolder.RectTransform.SizeChanged += Recalculate;
chatBox.RecalculateChildren();
chatBox.UpdateScrollBarSize();
}
CoroutineManager.StartCoroutine(UpdateMessageAnimation(msgHolder, 0.5f));
@@ -435,12 +435,30 @@ namespace Barotrauma
public void SelectNext(bool force = false, bool autoScroll = true)
{
Select(Math.Min(Content.CountChildren - 1, SelectedIndex + 1), force, autoScroll);
int index = SelectedIndex + 1;
while (index < Content.CountChildren)
{
if (Content.GetChild(index).Visible)
{
Select(index, force, autoScroll);
break;
}
index++;
}
}
public void SelectPrevious(bool force = false, bool autoScroll = true)
{
Select(Math.Max(0, SelectedIndex - 1), force, autoScroll);
int index = SelectedIndex - 1;
while (index >= 0)
{
if (Content.GetChild(index).Visible)
{
Select(index, force, autoScroll);
break;
}
index--;
}
}
public void Select(int childIndex, bool force = false, bool autoScroll = true)
@@ -233,7 +233,7 @@ namespace Barotrauma
switch (InputType)
{
case NumberType.Int:
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsNumber(c)).ToArray());
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsNumber(c) || c == '-').ToArray());
break;
case NumberType.Float:
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsDigit(c) || c == '.' || c == '-').ToArray());
@@ -126,6 +126,11 @@ namespace Barotrauma
set { text.Text = value; }
}
public Color? DefaultTextColor
{
get { return defaultTextColor; }
}
public GUITickBox(RectTransform rectT, string label, ScalableFont font = null, string style = "") : base(null, rectT)
{
CanBeFocused = true;
+13 -5
View File
@@ -318,6 +318,8 @@ namespace Barotrauma
private void InitUserStats()
{
return;
if (GameSettings.ShowUserStatisticsPrompt)
{
if (TextManager.ContainsTag("statisticspromptheader") && TextManager.ContainsTag("statisticsprompttext"))
@@ -404,14 +406,18 @@ namespace Barotrauma
GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice);
DebugConsole.Init();
if (Config.AutoUpdateWorkshopItems)
CrossThread.RequestExecutionOnMainThread(() =>
{
if (SteamManager.AutoUpdateWorkshopItems())
if (Config.AutoUpdateWorkshopItems)
{
ContentPackage.LoadAll();
Config.ReloadContentPackages();
if (SteamManager.AutoUpdateWorkshopItems())
{
ContentPackage.LoadAll();
Config.ReloadContentPackages();
}
}
}
});
if (SelectedPackages.None())
{
@@ -844,6 +850,8 @@ namespace Barotrauma
SteamManager.Update((float)Timing.Step);
TaskPool.Update();
SoundManager?.Update();
Timing.Accumulator -= Timing.Step;
@@ -431,7 +431,6 @@ namespace Barotrauma
}
else
{
characterArea.CanBeFocused = false;
characterArea.CanBeSelected = false;
}
@@ -204,9 +204,14 @@ namespace Barotrauma
{
campaign.SuppressStateSending = true;
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
{
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
}
campaign.startWatchmanID = startWatchmanID;
campaign.endWatchmanID = endWatchmanID;
@@ -215,7 +220,6 @@ namespace Barotrauma
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
campaign.CargoManager.SetPurchasedItems(purchasedItems);
if (myCharacterInfo != null)
{
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
color = color * (item.Condition / item.Prefab.Health);
color *= (item.Condition / item.Prefab.Health);
color.A = 255;
}
@@ -184,7 +184,7 @@ namespace Barotrauma.Items.Components
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
{
if (isStuck ||
if ((IsStuck && !isNetworkMessage) ||
(PredictedState == null && isOpen == open) ||
(PredictedState != null && isOpen == PredictedState.Value && isOpen == open))
{
@@ -210,11 +210,7 @@ namespace Barotrauma.Items.Components
StopPicking(null);
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse, item.WorldPosition);
}
}
//opening a partially stuck door makes it less stuck
if (isOpen) stuck = MathHelper.Clamp(stuck - 30.0f, 0.0f, 100.0f);
}
}
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
@@ -225,7 +221,7 @@ namespace Barotrauma.Items.Components
bool forcedOpen = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
if (isStuck) { OpenState = 0.0f; }
PredictedState = null;
}
}
@@ -17,7 +17,20 @@ namespace Barotrauma.Items.Components
{
get { return light; }
}
public override void OnScaleChanged()
{
light.SpriteScale = Vector2.One * item.Scale;
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
}
partial void SetLightSourceState(bool enabled, float brightness)
{
if (light == null) { return; }
light.Enabled = enabled;
light.Color = LightColor * brightness;
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f)
@@ -28,7 +41,7 @@ namespace Barotrauma.Items.Components
public override void FlipX(bool relativeToSub)
{
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX)
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
{
light.LightSpriteEffect = light.LightSpriteEffect == SpriteEffects.None ?
SpriteEffects.FlipHorizontally : SpriteEffects.None;
@@ -39,5 +52,11 @@ namespace Barotrauma.Items.Components
{
IsOn = msg.ReadBoolean();
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
light.Remove();
}
}
}
@@ -77,6 +77,26 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
state = msg.ReadBoolean();
ushort userID = msg.ReadUInt16();
if (userID == 0)
{
if (user != null)
{
IsActive = false;
CancelUsing(user);
user = null;
}
}
else
{
Character newUser = Entity.FindEntityByID(userID) as Character;
if (newUser != user)
{
CancelUsing(user);
}
user = newUser;
IsActive = true;
}
}
}
}
@@ -23,18 +23,23 @@ namespace Barotrauma.Items.Components
{
Enabled = false
};
powerIndicator.TextColor = powerIndicator.DefaultTextColor.Value;
highVoltageIndicator = new GUITickBox(new RectTransform(indicatorSize, paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, (int)(40 * GUI.yScale)) },
TextManager.Get("PowerTransferHighVoltage"), style: "IndicatorLightRed")
{
ToolTip = TextManager.Get("PowerTransferTipOvervoltage"),
Enabled = false
};
highVoltageIndicator.TextColor = highVoltageIndicator.DefaultTextColor.Value;
lowVoltageIndicator = new GUITickBox(new RectTransform(indicatorSize, paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, (int)(80 * GUI.yScale)) },
TextManager.Get("PowerTransferLowVoltage"), style: "IndicatorLightRed")
{
ToolTip = TextManager.Get("PowerTransferTipLowvoltage"),
Enabled = false
};
lowVoltageIndicator.TextColor = lowVoltageIndicator.DefaultTextColor.Value;
var textContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), paddedFrame.RectTransform, Anchor.TopRight));
@@ -142,7 +142,7 @@ namespace Barotrauma.Items.Components
{
if (repairSoundChannel == null || !repairSoundChannel.IsPlaying)
{
repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull);
repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull);
}
}
else
@@ -221,8 +221,17 @@ namespace Barotrauma.Items.Components
deteriorationTimer = msg.ReadSingle();
deteriorateAlwaysResetTimer = msg.ReadSingle();
DeteriorateAlways = msg.ReadBoolean();
CurrentFixer = msg.ReadBoolean() ? Character.Controlled : null;
ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
if (currentFixerID == 0)
{
CurrentFixer = null;
}
else
{
CurrentFixer = Entity.FindEntityByID(currentFixerID) as Character;
}
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -119,6 +119,7 @@ namespace Barotrauma.Items.Components
{
DrawWire(spriteBatch, draggingConnected, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), null, panel, "");
}
panel.TriggerRewiringSound();
if (!PlayerInput.LeftButtonHeld())
{
@@ -129,7 +130,11 @@ namespace Barotrauma.Items.Components
panel.DisconnectedWires.Add(draggingConnected);
}
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
if (GameMain.Client != null)
{
panel.Item.CreateClientEvent(panel);
}
draggingConnected = null;
}
}
@@ -205,7 +210,10 @@ namespace Barotrauma.Items.Components
SetWire(index, draggingConnected);
}
}
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
if (GameMain.Client != null)
{
panel.Item.CreateClientEvent(panel);
}
draggingConnected = null;
}
}
@@ -11,19 +11,28 @@ namespace Barotrauma.Items.Components
{
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
{
//how long the rewiring sound plays after doing changes to the wiring
const float RewireSoundDuration = 5.0f;
public static Wire HighlightedWire;
private SoundChannel rewireSoundChannel;
private float rewireSoundTimer;
partial void InitProjSpecific(XElement element)
{
if (GuiFrame == null) return;
if (GuiFrame == null) { return; }
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
{
UserData = this
};
}
public void TriggerRewiringSound()
{
rewireSoundTimer = RewireSoundDuration;
}
partial void UpdateProjSpecific(float deltaTime)
{
foreach (Wire wire in DisconnectedWires)
@@ -40,7 +49,9 @@ namespace Barotrauma.Items.Components
}
}
}
if (user != null && user.SelectedConstruction == item && HasRequiredItems(user, addMessage: false))
rewireSoundTimer -= deltaTime;
if (user != null && user.SelectedConstruction == item && rewireSoundTimer > 0.0f)
{
if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying)
{
@@ -51,12 +62,13 @@ namespace Barotrauma.Items.Components
{
rewireSoundChannel?.FadeOutAndDispose();
rewireSoundChannel = null;
rewireSoundTimer = 0.0f;
}
}
public override void Move(Vector2 amount)
{
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) return;
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) { return; }
MoveConnectedWires(amount);
}
@@ -103,6 +115,7 @@ namespace Barotrauma.Items.Components
//delay reading the state until midround syncing is done
//because some of the wires connected to the panel may not exist yet
long msgStartPos = msg.BitPosition;
msg.ReadUInt16(); //user ID
foreach (Connection connection in Connections)
{
for (int i = 0; i < Connection.MaxLinked; i++)
@@ -121,6 +134,8 @@ namespace Barotrauma.Items.Components
}
else
{
//don't trigger rewiring sounds if the rewiring is being done by the local user (in that case we'll trigger it locally)
if (Character.Controlled == null || user != Character.Controlled) { TriggerRewiringSound(); }
ApplyRemoteState(msg);
}
}
@@ -130,6 +145,17 @@ namespace Barotrauma.Items.Components
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
List<Wire> newWires = new List<Wire>();
ushort userID = msg.ReadUInt16();
if (userID == 0)
{
user = null;
}
else
{
user = Entity.FindEntityByID(userID) as Character;
}
foreach (Connection connection in Connections)
{
connection.ClearConnections();
@@ -145,6 +145,21 @@ namespace Barotrauma.Items.Components
}
Dock(DockingTarget);
if (joint == null)
{
string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." +
" Submarine: " + (item.Submarine?.Name ?? "null") +
", target submarine: " + (DockingTarget.item.Submarine?.Name ?? "null");
if (item.Submarine?.DockedTo.Contains(DockingTarget.item.Submarine) ?? false)
{
errorMsg += "\nAlready docked.";
}
if (item.Submarine == DockingTarget.item.Submarine)
{
errorMsg += "\nTrying to dock the submarine to itself.";
}
GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
if (isLocked)
{
@@ -916,21 +916,30 @@ namespace Barotrauma
case NetEntityEvent.Type.ApplyStatusEffect:
{
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
byte componentIndex = msg.ReadByte();
ushort targetID = msg.ReadUInt16();
byte targetLimbID = msg.ReadByte();
byte componentIndex = msg.ReadByte();
ushort targetCharacterID = msg.ReadUInt16();
byte targetLimbID = msg.ReadByte();
ushort useTargetID = msg.ReadUInt16();
Vector2? worldPosition = null;
bool hasPosition = msg.ReadBoolean();
if (hasPosition)
{
worldPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
ItemComponent targetComponent = componentIndex < components.Count ? components[componentIndex] : null;
Character target = FindEntityByID(targetID) as Character;
Limb targetLimb = target != null && targetLimbID < target.AnimController.Limbs.Length ? target.AnimController.Limbs[targetLimbID] : null;
Character targetCharacter = FindEntityByID(targetCharacterID) as Character;
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
targetCharacter.AnimController.Limbs[targetLimbID] : null;
Entity useTarget = FindEntityByID(useTargetID);
if (targetComponent == null)
{
ApplyStatusEffects(actionType, 1.0f, target, targetLimb, true);
ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, true, worldPosition: worldPosition);
}
else
{
targetComponent.ApplyStatusEffects(actionType, 1.0f, target, targetLimb);
targetComponent.ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, worldPosition: worldPosition);
}
}
break;
@@ -228,12 +228,12 @@ namespace Barotrauma.Lights
activeLights.Clear();
foreach (LightSource light in lights)
{
if (light.Color.A < 1 || light.Range < 1.0f || !light.Enabled) continue;
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
if (!light.Enabled) { continue; }
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.LightSourceParams.TextureRange, viewRect)) { continue; }
activeLights.Add(light);
}
//clear the lightmap
graphics.Clear(Color.Black);
graphics.BlendState = BlendState.Additive;
@@ -244,9 +244,9 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights)
{
if (!light.IsBackground) continue;
if (!light.IsBackground) { continue; }
light.DrawSprite(spriteBatch, cam);
light.DrawLightVolume(spriteBatch, lightEffect, transform);
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
backgroundSpritesDrawn = true;
}
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
@@ -288,7 +288,7 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights)
{
if (light.IsBackground) continue;
if (light.IsBackground) { continue; }
light.DrawSprite(spriteBatch, cam);
}
spriteBatch.End();
@@ -303,6 +303,8 @@ namespace Barotrauma.Lights
//draw characters to obstruct the highlighted items/characters and light sprites
//---------------------------------------------------------------------------------------------------
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
SolidColorEffect.Parameters["color"].SetValue(Color.Black.ToVector4());
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
@@ -347,8 +349,8 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights)
{
if (light.IsBackground) continue;
light.DrawLightVolume(spriteBatch, lightEffect, transform);
if (light.IsBackground) { continue; }
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
}
Vector3 offset = Vector3.Zero;// new Vector3(Submarine.MainSub.DrawPosition.X, Submarine.MainSub.DrawPosition.Y, 0.0f);
lightEffect.World = Matrix.CreateTranslation(Vector3.Zero) * transform;
@@ -31,10 +31,22 @@ namespace Barotrauma.Lights
get { return range; }
set
{
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
TextureRange = range;
if (OverrideLightTexture != null)
{
TextureRange += Math.Max(
Math.Abs(OverrideLightTexture.RelativeOrigin.X - 0.5f) * OverrideLightTexture.size.X,
Math.Abs(OverrideLightTexture.RelativeOrigin.Y - 0.5f) * OverrideLightTexture.size.Y);
}
}
}
public float TextureRange
{
get;
private set;
}
public Sprite OverrideLightTexture
{
@@ -89,6 +101,8 @@ namespace Barotrauma.Lights
break;
case "lighttexture":
OverrideLightTexture = new Sprite(subElement, preMultiplyAlpha: false);
//refresh TextureRange
Range = range;
break;
}
}
@@ -115,8 +129,16 @@ namespace Barotrauma.Lights
class LightSource
{
//how many pixels the position of the light needs to change for the light volume to be recalculated
const float MovementRecalculationThreshold = 10.0f;
//how many radians the light needs to rotate for the light volume to be recalculated
const float RotationRecalculationThreshold = 0.02f;
private static Texture2D lightTexture;
private VertexPositionColorTexture[] vertices;
private short[] indices;
private List<ConvexHullList> hullsInRange;
public Texture2D texture;
@@ -167,6 +189,9 @@ namespace Barotrauma.Lights
private int vertexCount;
private int indexCount;
private Vector2 translateVertices;
private float rotateVertices;
private readonly LightSourceParams lightSourceParams;
public LightSourceParams LightSourceParams => lightSourceParams;
@@ -177,26 +202,38 @@ namespace Barotrauma.Lights
get { return position; }
set
{
if (Math.Abs(position.X - value.X) < 0.1f && Math.Abs(position.Y - value.Y) < 0.1f) return;
Vector2 moveAmount = value - position;
if (Math.Abs(moveAmount.X) < 0.1f && Math.Abs(moveAmount.Y) < 0.1f) { return; }
position = value;
if (Vector2.DistanceSquared(prevCalculatedPosition, position) < 5.0f * 5.0f) return;
//translate light volume manually instead of doing a full recalculation when moving by a small amount
if (Vector2.DistanceSquared(prevCalculatedPosition, position) < MovementRecalculationThreshold * MovementRecalculationThreshold && vertices != null)
{
translateVertices = position - prevCalculatedPosition;
return;
}
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedPosition = position;
}
}
private float prevCalculatedRotation;
private float rotation;
public float Rotation
{
get { return rotation; }
set
{
if (Math.Abs(rotation - value) < 0.01f) return;
if (Math.Abs(value - rotation) < 0.001f) { return; }
rotation = value;
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
{
rotateVertices = rotation - prevCalculatedRotation;
return;
}
NeedsHullCheck = true;
NeedsRecalculation = true;
}
@@ -711,16 +748,25 @@ namespace Barotrauma.Lights
return retVal;
}
private void CalculateLightVertices(List<Vector2> rayCastHits)
{
List<VertexPositionColorTexture> vertices = new List<VertexPositionColorTexture>();
vertexCount = rayCastHits.Count * 2 + 1;
indexCount = (rayCastHits.Count) * 9;
//recreate arrays if they're too small or excessively large
if (vertices == null || vertices.Length < vertexCount || vertices.Length > vertexCount * 3)
{
vertices = new VertexPositionColorTexture[vertexCount];
indices = new short[indexCount];
}
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
Vector2 uvOffset = Vector2.Zero;
Vector2 overrideTextureDims = Vector2.One;
if (OverrideLightTexture != null)
@@ -728,15 +774,15 @@ namespace Barotrauma.Lights
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTexture.Origin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
if (LightSpriteEffect == SpriteEffects.FlipVertically) origin.Y = (OverrideLightTexture.SourceRect.Height - origin.Y);
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = OverrideLightTexture.SourceRect.Width - origin.X; }
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
}
// Add a vertex for the center of the mesh
vertices.Add(new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
Color.White, new Vector2(0.5f, 0.5f) + uvOffset));
vertices[0] = new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
Color.White, GetUV(new Vector2(0.5f, 0.5f) + uvOffset, LightSpriteEffect));
//hacky fix to exc excessively large light volumes (they used to be up to 4x the range of the light if there was nothing to block the rays).
//might want to tweak the raycast logic in a way that this isn't necessary
float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
@@ -800,68 +846,88 @@ namespace Barotrauma.Lights
//finally, create the vertices
VertexPositionColorTexture fullVert = new VertexPositionColorTexture(new Vector3(position.X + rawDiff.X, position.Y + rawDiff.Y, 0),
Color.White, new Vector2(0.5f, 0.5f) + diff);
Color.White, GetUV(new Vector2(0.5f, 0.5f) + diff, LightSpriteEffect));
VertexPositionColorTexture fadeVert = new VertexPositionColorTexture(new Vector3(position.X + rawDiff.X + nDiff.X, position.Y + rawDiff.Y + nDiff.Y, 0),
Color.White * 0.0f, new Vector2(0.5f, 0.5f) + diff);
Color.White * 0.0f, GetUV(new Vector2(0.5f, 0.5f) + diff, LightSpriteEffect));
vertices.Add(fullVert);
vertices.Add(fadeVert);
vertices[1 + i * 2] = fullVert;
vertices[1 + i * 2 + 1] = fadeVert;
}
// Compute the indices to form triangles
List<short> indices = new List<short>();
for (int i = 0; i < rayCastHits.Count-1; i++)
for (int i = 0; i < rayCastHits.Count - 1; i++)
{
//main light body
indices.Add(0);
indices.Add((short)((i*2 + 3) % vertices.Count));
indices.Add((short)((i*2 + 1) % vertices.Count));
indices[i * 9] = 0;
indices[i * 9 + 1] = (short)((i * 2 + 3) % vertexCount);
indices[i * 9 + 2] = (short)((i * 2 + 1) % vertexCount);
//faded light
indices.Add((short)((i*2 + 1) % vertices.Count));
indices.Add((short)((i*2 + 3) % vertices.Count));
indices.Add((short)((i*2 + 4) % vertices.Count));
indices[i * 9 + 3] = (short)((i * 2 + 1) % vertexCount);
indices[i * 9 + 4] = (short)((i * 2 + 3) % vertexCount);
indices[i * 9 + 5] = (short)((i * 2 + 4) % vertexCount);
indices.Add((short)((i*2 + 2) % vertices.Count));
indices.Add((short)((i*2 + 1) % vertices.Count));
indices.Add((short)((i*2 + 4) % vertices.Count));
indices[i * 9 + 6] = (short)((i * 2 + 2) % vertexCount);
indices[i * 9 + 7] = (short)((i * 2 + 1) % vertexCount);
indices[i * 9 + 8] = (short)((i * 2 + 4) % vertexCount);
}
//main light body
indices.Add(0);
indices.Add((short)(1));
indices.Add((short)(vertices.Count - 2));
indices[(rayCastHits.Count - 1) * 9] = 0;
indices[(rayCastHits.Count - 1) * 9 + 1] = (short)(1);
indices[(rayCastHits.Count - 1) * 9 + 2] = (short)(vertexCount - 2);
//faded light
indices.Add((short)(1));
indices.Add((short)(vertices.Count-1));
indices.Add((short)(vertices.Count-2));
indices[(rayCastHits.Count - 1) * 9 + 3] = (short)(1);
indices[(rayCastHits.Count - 1) * 9 + 4] = (short)(vertexCount - 1);
indices[(rayCastHits.Count - 1) * 9 + 5] = (short)(vertexCount - 2);
indices.Add((short)(1));
indices.Add((short)(2));
indices.Add((short)(vertices.Count-1));
vertexCount = vertices.Count;
indexCount = indices.Count;
indices[(rayCastHits.Count - 1) * 9 + 6] = (short)(1);
indices[(rayCastHits.Count - 1) * 9 + 7] = (short)(2);
indices[(rayCastHits.Count - 1) * 9 + 8] = (short)(vertexCount - 1);
//TODO: a better way to determine the size of the vertex buffer and handle changes in size?
//now we just create a buffer for 64 verts and make it larger if needed
if (lightVolumeBuffer == null)
{
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount*1.5)), BufferUsage.None);
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64*3, (int)(indexCount * 1.5)), BufferUsage.None);
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount * 1.5)), BufferUsage.None);
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64 * 3, (int)(indexCount * 1.5)), BufferUsage.None);
}
else if (vertexCount > lightVolumeBuffer.VertexCount || indexCount > lightVolumeIndexBuffer.IndexCount)
{
lightVolumeBuffer.Dispose();
lightVolumeIndexBuffer.Dispose();
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, (int)(vertexCount*1.5), BufferUsage.None);
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, (int)(vertexCount * 1.5), BufferUsage.None);
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), (int)(indexCount * 1.5), BufferUsage.None);
}
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices.ToArray());
lightVolumeIndexBuffer.SetData<short>(indices.ToArray());
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount);
lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount);
Vector2 GetUV(Vector2 vert, SpriteEffects effects)
{
if (effects == SpriteEffects.FlipHorizontally)
{
vert.X = 1.0f - vert.X;
}
else if (effects == SpriteEffects.FlipVertically)
{
vert.Y = 1.0f - vert.Y;
}
else if (effects == (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically))
{
vert.X = 1.0f - vert.X;
vert.Y = 1.0f - vert.Y;
}
vert.Y = 1.0f - vert.Y;
return vert;
}
translateVertices = Vector2.Zero;
rotateVertices = 0.0f;
prevCalculatedPosition = position;
prevCalculatedRotation = rotation;
}
/// <summary>
@@ -944,15 +1010,12 @@ namespace Barotrauma.Lights
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
drawPos.Y = -drawPos.Y;
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(currentTexture, drawPos, null, Color, -rotation, center, scale, SpriteEffects.None, 1);
return;
}
Vector3 offset = ParentSub == null ?
Vector3.Zero : new Vector3(ParentSub.DrawPosition.X, ParentSub.DrawPosition.Y, 0.0f);
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
if (NeedsRecalculation)
{
@@ -962,8 +1025,16 @@ namespace Barotrauma.Lights
lastRecalculationTime = (float)Timing.TotalTime;
NeedsRecalculation = false;
}
if (vertexCount == 0) return;
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
lightEffect.World =
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
Matrix.CreateRotationZ(rotateVertices) *
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
transform;
if (vertexCount == 0) { return; }
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f)) / 255.0f;
if (OverrideLightTexture != null)
@@ -483,6 +483,8 @@ namespace Barotrauma.Networking
if (DateTime.Now > timeOut)
{
clientPeer?.Close(Lidgren.Network.NetConnection.NoResponseMessage);
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionFailed"), TextManager.Get("CouldNotConnectToServer"));
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
reconnectBox?.Close(); reconnectBox = null;
break;
}
@@ -1232,7 +1234,9 @@ namespace Barotrauma.Networking
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame(""));
@@ -1274,6 +1278,7 @@ namespace Barotrauma.Networking
gameStarted = false;
Character.Controlled = null;
SpawnAsTraitor = false;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
respawnManager = null;
@@ -1432,6 +1437,8 @@ namespace Barotrauma.Networking
}
}
private bool initialUpdateReceived;
private void ReadLobbyUpdate(IReadMessage inc)
{
ServerNetObject objHeader;
@@ -1452,13 +1459,15 @@ namespace Barotrauma.Networking
UInt16 settingsLen = inc.ReadUInt16();
byte[] settingsData = inc.ReadBytes(settingsLen);
if (inc.ReadBoolean())
bool isInitialUpdate = inc.ReadBoolean();
if (isInitialUpdate)
{
if (GameSettings.VerboseLogging)
{
DebugConsole.NewMessage("Received initial lobby update, ID: " + updateID + ", last ID: " + GameMain.NetLobbyScreen.LastUpdateID, Color.Gray);
}
ReadInitialUpdate(inc);
initialUpdateReceived = true;
}
string selectSubName = inc.ReadString();
@@ -1489,7 +1498,9 @@ namespace Barotrauma.Networking
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
//ignore the message if we already a more up-to-date one
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID))
//or if we're still waiting for the initial update
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
(isInitialUpdate || initialUpdateReceived))
{
ReadWriteMessage settingsBuf = new ReadWriteMessage();
settingsBuf.Write(settingsData, 0, settingsLen); settingsBuf.BitPosition = 0;
@@ -2248,12 +2259,10 @@ namespace Barotrauma.Networking
{
if (gameStarted)
{
tickBox.Visible = false;
tickBox.Parent.Visible = false;
return false;
}
Vote(VoteType.StartRound, tickBox.Selected);
return true;
}
@@ -200,6 +200,7 @@ namespace Barotrauma.Networking
}
msg.BitPosition += msgLength * 8;
msg.ReadPadBits();
}
else
{
@@ -213,6 +214,20 @@ namespace Barotrauma.Networking
try
{
ReadEvent(msg, entity, sendingTime);
msg.ReadPadBits();
if (msg.BitPosition != msgPosition + msgLength * 8)
{
string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
+ "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
//TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
//msg.BitPosition = (int)(msgPosition + msgLength * 8);
}
}
catch (Exception e)
@@ -231,9 +246,9 @@ namespace Barotrauma.Networking
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
msg.BitPosition = (int)(msgPosition + msgLength * 8);
msg.ReadPadBits();
}
}
msg.ReadPadBits();
}
return true;
}
@@ -275,6 +275,7 @@ namespace Barotrauma.Networking
#if DEBUG
CoroutineManager.InvokeAfter(() =>
{
if (GameMain.Client == null) { return; }
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && sendType != Facepunch.Steamworks.Networking.SendType.Reliable) { return; }
int count = Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedDuplicatesChance ? 2 : 1;
for (int i = 0; i < count; i++)
@@ -18,7 +18,17 @@ namespace Barotrauma.Networking
public UInt64 OwnerID;
public bool OwnerVerified;
public string ServerName;
private string serverName;
public string ServerName
{
get { return serverName; }
set
{
serverName = value;
if (serverName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
}
}
public string ServerMessage;
public bool GameStarted;
public int PlayerCount;
@@ -455,38 +465,7 @@ namespace Barotrauma.Networking
if (SteamFriend.IsPlayingThisGame && SteamFriend.ServerLobbyId != 0)
{
LobbyID = SteamFriend.ServerLobbyId;
SteamManager.Instance.LobbyList.SetManualLobbyDataCallback(LobbyID, (lobby) =>
{
SteamManager.Instance.LobbyList.SetManualLobbyDataCallback(LobbyID, null);
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { return; }
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
string ip = lobby.GetData("hostipaddress");
UInt64 ownerId = SteamManager.SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
if (OwnerID != ownerId) { return; }
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
ServerName = lobby.Name;
Port = "";
QueryPort = "";
IP = ip;
PlayerCount = currPlayers;
MaxPlayers = maxPlayers;
HasPassword = hasPassword;
RespondedToSteamQuery = true;
LobbyID = lobby.LobbyID;
OwnerID = ownerId;
PingChecked = false;
OwnerVerified = true;
SteamManager.AssignLobbyDataToServerInfo(lobby, this);
onServerRulesReceived?.Invoke(this);
});
SteamManager.Instance.LobbyList.RequestLobbyData(LobbyID);
}
else
@@ -326,8 +326,8 @@ namespace Barotrauma.Steam
localQuery.OnFinished = onFinished;
#endif
instance.client.LobbyList.OnLobbiesUpdated = () => { UpdateLobbyQuery(onServerFound, onServerRulesReceived, onFinished); };
instance.client.LobbyList.Refresh();
instance.client.LobbyList.Request();
return true;
}
@@ -382,42 +382,6 @@ namespace Barotrauma.Steam
return true;
}
private static void UpdateLobbyQuery(Action<Networking.ServerInfo> onServerFound, Action<Networking.ServerInfo> onServerRulesReceived, Action onFinished)
{
foreach (LobbyList.Lobby lobby in instance.client.LobbyList.Lobbies)
{
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { continue; }
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
UInt64 ownerId = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
string ip = lobby.GetData("hostipaddress");
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
var serverInfo = new ServerInfo()
{
ServerName = lobby.Name,
Port = "",
QueryPort = "",
IP = ip,
PlayerCount = currPlayers,
MaxPlayers = maxPlayers,
HasPassword = hasPassword,
RespondedToSteamQuery = true,
LobbyID = lobby.LobbyID,
OwnerID = ownerId
};
serverInfo.PingChecked = false;
AssignLobbyDataToServerInfo(lobby, serverInfo);
onServerFound(serverInfo);
//onServerRulesReceived(serverInfo);
}
onFinished();
}
public static void AssignLobbyDataToServerInfo(LobbyList.Lobby lobby, ServerInfo serverInfo)
{
serverInfo.ServerMessage = lobby.GetData("message");
@@ -495,6 +459,7 @@ namespace Barotrauma.Steam
serverInfo.PingChecked = true;
serverInfo.Ping = s.Ping;
serverInfo.LobbyID = 0;
serverInfo.OwnerVerified = true;
if (responded)
{
s.FetchRules();
@@ -248,8 +248,10 @@ namespace Barotrauma
MapEntityCategory newCategory = (MapEntityCategory)userdata;
if (newCategory != selectedItemCategory)
{
searchBox.Text = "";
searchBox.Text = "";
storeItemList.ScrollBar.BarScroll = 0f;
}
FilterStoreItems((MapEntityCategory)userdata, searchBox.Text);
return true;
}
@@ -947,7 +949,9 @@ namespace Barotrauma
var itemFrame = myItemList.Content.GetChildByUserData(pi);
if (itemFrame == null)
{
itemFrame = CreateItemFrame(pi, pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation), myItemList);
var priceInfo = pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation);
if (priceInfo == null) { continue; }
itemFrame = CreateItemFrame(pi, priceInfo, myItemList);
}
itemFrame.GetChild(0).GetChild<GUINumberInput>().IntValue = pi.Quantity;
existingItemFrames.Add(itemFrame);
@@ -99,6 +99,7 @@ namespace Barotrauma.CharacterEditor
private Rectangle spriteSheetRect;
private Rectangle CalculateSpritesheetRectangle() =>
Textures == null || Textures.None() ? Rectangle.Empty :
new Rectangle(
spriteSheetOffsetX,
spriteSheetOffsetY,
@@ -656,12 +657,6 @@ namespace Barotrauma.CharacterEditor
}
if (!isFrozen)
{
if (character.AnimController.Invalid)
{
Reset(new Character[] { character });
SpawnCharacter(currentCharacterConfig);
}
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.Update((float)deltaTime);
@@ -722,6 +717,7 @@ namespace Barotrauma.CharacterEditor
foreach (Limb limb in character.AnimController.Limbs)
{
if (limb == null || limb.ActiveSprite == null) { continue; }
if (selectedJoints.Any(j => j.LimbA == limb || j.LimbB == limb)) { continue; }
// Select limbs on ragdoll
if (editLimbs && !spriteSheetRect.Contains(PlayerInput.MousePosition) && MathUtils.RectangleContainsPoint(GetLimbPhysicRect(limb), PlayerInput.MousePosition))
{
@@ -2727,11 +2723,19 @@ namespace Barotrauma.CharacterEditor
return false;
}
#endif
character.Params.Save();
GUI.AddMessage(GetCharacterEditorTranslation("CharacterSavedTo").Replace("[path]", CharacterParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
character.AnimController.SaveRagdoll();
GUI.AddMessage(GetCharacterEditorTranslation("RagdollSavedTo").Replace("[path]", RagdollParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
AnimParams.ForEach(p => p.Save());
if (!string.IsNullOrEmpty(RagdollParams.Texture) && !File.Exists(RagdollParams.Texture))
{
DebugConsole.ThrowError($"Invalid texture path: {RagdollParams.Texture}");
return false;
}
else
{
character.Params.Save();
GUI.AddMessage(GetCharacterEditorTranslation("CharacterSavedTo").Replace("[path]", CharacterParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
character.AnimController.SaveRagdoll();
GUI.AddMessage(GetCharacterEditorTranslation("RagdollSavedTo").Replace("[path]", RagdollParams.FullPath), Color.Green, font: GUI.Font, lifeTime: 5);
AnimParams.ForEach(p => p.Save());
}
return true;
};
// Spacing
@@ -40,6 +40,10 @@ namespace Barotrauma.CharacterEditor
canEnterSubmarine = ragdoll.CanEnterSubmarine;
canWalk = ragdoll.CanWalk;
texturePath = ragdoll.Texture;
if (string.IsNullOrEmpty(texturePath) && !name.Equals(Character.HumanSpeciesName, StringComparison.OrdinalIgnoreCase))
{
texturePath = ragdoll.Limbs.FirstOrDefault()?.GetSprite().Texture;
}
}
public static Wizard instance;
@@ -265,8 +269,30 @@ namespace Barotrauma.CharacterEditor
};
if (ofd.ShowDialog() == DialogResult.OK)
{
string file = ofd.FileName;
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(file), Environment.CurrentDirectory);
string destinationPath = relativePath;
//copy file to XML path if it's not located relative to the game's files
if (relativePath.StartsWith("..") ||
Path.GetPathRoot(Environment.CurrentDirectory) != Path.GetPathRoot(file))
{
destinationPath = Path.Combine(Path.GetDirectoryName(XMLPath), Path.GetFileName(file));
string destinationDir = Path.GetDirectoryName(destinationPath);
if (!Directory.Exists(destinationDir))
{
Directory.CreateDirectory(destinationDir);
}
if (!File.Exists(destinationPath))
{
File.Copy(file, Path.GetFullPath(destinationPath), overwrite: true);
}
}
isTextureSelected = true;
texturePathElement.Text = ToolBox.ConvertAbsoluteToRelativePath(ofd.FileName);
texturePathElement.Text = destinationPath;
}
return true;
}
@@ -8,6 +8,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -84,7 +85,7 @@ namespace Barotrauma
}
}
#else
FetchRemoteContent(Frame.RectTransform);
FetchRemoteContent();
#endif
@@ -753,12 +754,20 @@ namespace Barotrauma
exeName = "DedicatedServer.exe";
}
string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
string arguments = "-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
" -public " + isPublicBox.Selected.ToString() +
" -playstyle " + ((PlayStyle)playstyleBanner.UserData).ToString() +
" -password \"" + passwordBox.Text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
" -maxplayers " + maxPlayersBox.Text;
if (!string.IsNullOrWhiteSpace(passwordBox.Text))
{
arguments += " -password \"" + ToolBox.EscapeCharacters(passwordBox.Text) + "\"";
}
else
{
arguments += " -nopassword";
}
int ownerKey = 0;
if (Steam.SteamManager.GetSteamID()!=0)
@@ -774,6 +783,7 @@ namespace Barotrauma
string filename = exeName;
#if LINUX || OSX
filename = "./" + Path.GetFileNameWithoutExtension(exeName);
arguments = ToolBox.EscapeCharacters(arguments);
#endif
var processInfo = new ProcessStartInfo
{
@@ -1149,34 +1159,15 @@ namespace Barotrauma
}
#endregion
private void FetchRemoteContent(RectTransform parent)
private void FetchRemoteContent()
{
if (string.IsNullOrEmpty(GameMain.Config.RemoteContentUrl)) { return; }
try
{
var client = new RestClient(GameMain.Config.RemoteContentUrl);
var request = new RestRequest("MenuContent.xml", Method.GET);
IRestResponse response = client.Execute(request);
if (response.ResponseStatus != ResponseStatus.Completed)
{
return;
}
if (response.StatusCode != HttpStatusCode.OK)
{
return;
}
string xml = response.Content;
int index = xml.IndexOf('<');
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
if (string.IsNullOrWhiteSpace(xml)) { return; }
XElement element = XDocument.Parse(xml)?.Root;
foreach (XElement subElement in element.Elements())
{
GUIComponent.FromXML(subElement, parent);
}
client.ExecuteAsync(request, RemoteContentReceived);
CoroutineManager.StartCoroutine(WairForRemoteContentReceived());
}
catch (Exception e)
@@ -1189,5 +1180,60 @@ namespace Barotrauma
return;
}
}
private IEnumerable<object> WairForRemoteContentReceived()
{
while (true)
{
lock (remoteContentLock)
{
if (remoteContentResponse != null) { break; }
}
yield return new WaitForSeconds(0.1f);
}
lock (remoteContentLock)
{
if (remoteContentResponse.ResponseStatus != ResponseStatus.Completed || remoteContentResponse.StatusCode != HttpStatusCode.OK)
{
yield return CoroutineStatus.Success;
}
try
{
string xml = remoteContentResponse.Content;
int index = xml.IndexOf('<');
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
if (!string.IsNullOrWhiteSpace(xml))
{
XElement element = XDocument.Parse(xml)?.Root;
foreach (XElement subElement in element.Elements())
{
GUIComponent.FromXML(subElement, Frame.RectTransform);
}
}
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
#endif
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.WairForRemoteContentReceived:Exception", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Reading received remote main menu content failed. " + e.Message);
}
}
yield return CoroutineStatus.Success;
}
private readonly object remoteContentLock = new object();
private IRestResponse remoteContentResponse;
private void RemoteContentReceived(IRestResponse response, RestRequestAsyncHandle handle)
{
lock (remoteContentLock)
{
remoteContentResponse = response;
}
}
}
}
@@ -319,6 +319,14 @@ namespace Barotrauma
RelativeSpacing = panelSpacing
};
GameMain.Instance.OnResolutionChanged += () =>
{
if (innerFrame != null)
{
innerFrame.RectTransform.MaxSize = new Point(int.MaxValue, GameMain.GraphicsHeight - 50);
}
};
var panelContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), innerFrame.RectTransform, Anchor.Center), isHorizontal: true)
{
Stretch = true,
@@ -440,6 +448,14 @@ namespace Barotrauma
Stretch = true
};
GameMain.Instance.OnResolutionChanged += () =>
{
if (panelContainer != null && sideBar != null)
{
sideBar.RectTransform.MaxSize = new Point(650, panelContainer.RectTransform.Rect.Height);
}
};
//player info panel ------------------------------------------------------------
myCharacterFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), sideBar.RectTransform));
@@ -1781,24 +1797,23 @@ namespace Barotrauma
RelativeSpacing = 0.03f
};
var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true)
var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true
};
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 1.0f), headerContainer.RectTransform),
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), headerContainer.RectTransform),
text: selectedClient.Name, font: GUI.LargeFont);
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width);
if (selectedClient.SteamID != 0 && Steam.SteamManager.IsInitialized)
{
var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), headerContainer.RectTransform, Anchor.TopCenter),
var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), headerContainer.RectTransform, Anchor.TopCenter) { MaxSize = new Point(int.MaxValue, (int)(40 * GUI.Scale)) },
TextManager.Get("ViewSteamProfile"))
{
UserData = selectedClient
};
GUITextBlock.AutoScaleAndNormalize(nameText, viewSteamProfileButton.TextBlock);
viewSteamProfileButton.TextBlock.AutoScale = true;
viewSteamProfileButton.OnClicked = (bt, userdata) =>
{
Steam.SteamManager.Instance.Overlay.OpenUrl("https://steamcommunity.com/profiles/" + selectedClient.SteamID.ToString());
@@ -2054,7 +2069,7 @@ namespace Barotrauma
}
var closeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform, Anchor.BottomRight),
TextManager.Get("Close"))
TextManager.Get("Close"), style: "GUIButtonLarge")
{
IgnoreLayoutGroups = true,
OnClicked = ClosePlayerFrame
@@ -2409,6 +2424,13 @@ namespace Barotrauma
AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - characterInfoFrame.Rect.Width, button.Rect.Bottom)
});
characterInfoFrame.RectTransform.SizeChanged += () =>
{
if (characterInfoFrame == null || HeadSelectionList?.RectTransform == null || button == null) { return; }
HeadSelectionList.RectTransform.Resize(new Point(characterInfoFrame.Rect.Width, (characterInfoFrame.Rect.Bottom - button.Rect.Bottom) + characterInfoFrame.Rect.Height * 2));
HeadSelectionList.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - characterInfoFrame.Rect.Width, button.Rect.Bottom);
};
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black)
{
UserData = "outerglow",
@@ -2446,7 +2468,7 @@ namespace Barotrauma
headSprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(headSprite, head.Value.ToPoint()), headSprite.SourceRect.Size);
characterSprites.Add(headSprite);
if (row == null || itemsInRow >= 4)
if (itemsInRow >= 4 || row == null || gender != (Gender)row.UserData)
{
row = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform), true)
{
@@ -2536,6 +2558,14 @@ namespace Barotrauma
JobSelectionFrame = new GUIFrame(new RectTransform(frameSize, GUI.Canvas, Anchor.TopLeft)
{ AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - frameSize.X, characterInfoFrame.Rect.Bottom) }, "GUIFrameListBox");
characterInfoFrame.RectTransform.SizeChanged += () =>
{
if (characterInfoFrame == null || JobSelectionFrame?.RectTransform == null) { return; }
Point size = new Point(characterInfoFrame.Rect.Width, characterInfoFrame.Rect.Height * 2);
JobSelectionFrame.RectTransform.Resize(size);
JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - size.X, characterInfoFrame.Rect.Bottom);
};
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), JobSelectionFrame.RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black)
{
UserData = "outerglow",
@@ -2600,32 +2630,24 @@ namespace Barotrauma
image.Visible = currVisible == (variantIndex + 1);
}
var variantButton = new GUIButton(new RectTransform(new Vector2(0.15f), jobButton.RectTransform, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f, 0.05f + 0.2f * variantIndex) }, (variantIndex + 1).ToString(), style: null)
var variantButton = CreateJobVariantButton(jobPrefab, variantIndex, images.Length, jobButton);
variantButton.OnClicked = (btn, obj) =>
{
Color = new Color(50, 50, 50, 200),
HoverColor = Color.Gray * 0.75f,
PressedColor = Color.Black * 0.75f,
SelectedColor = new Color(45, 70, 100, 200),
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex+1),
OnClicked = (btn, obj) =>
currSelected.Selected = false;
int k = ((Pair<JobPrefab, int>)obj).Second;
btn.Parent.UserData = obj;
for (int j = 0; j < images.Length; j++)
{
currSelected.Selected = false;
int k = ((Pair<JobPrefab, int>)obj).Second;
btn.Parent.UserData = obj;
for (int j = 0; j < images.Length; j++)
foreach (GUIImage image in images[j])
{
foreach (GUIImage image in images[j])
{
image.Visible = k == (j + 1);
}
image.Visible = k == (j + 1);
}
currSelected = btn;
currSelected.Selected = true;
return false;
}
};
currSelected = btn;
currSelected.Selected = true;
return false;
};
if (currVisible == (variantIndex + 1))
{
currSelected = variantButton;
@@ -2818,7 +2840,7 @@ namespace Barotrauma
subList.Enabled = !enabled && AllowSubSelection;
shuttleList.Enabled = !enabled && GameMain.Client.HasPermission(ClientPermissions.SelectSub);
StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && GameMain.Client.GameStarted && !enabled;
StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && !GameMain.Client.GameStarted && !enabled;
if (campaignViewButton != null) { campaignViewButton.Visible = enabled; }
@@ -2943,34 +2965,28 @@ namespace Barotrauma
}
if (images.Length > 1)
{
var variantButton = new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f, 0.25f + 0.2f * variantIndex) }, (variantIndex + 1).ToString(), style: null)
var variantButton = CreateJobVariantButton(jobPrefab, variantIndex, images.Length, slot);
variantButton.OnClicked = (btn, obj) =>
{
Color = new Color(50, 50, 50, 200),
HoverColor = Color.Gray * 0.75f,
PressedColor = Color.Black * 0.75f,
SelectedColor = new Color(45, 70, 100, 200),
Selected = jobPrefab.Second == (variantIndex + 1),
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex + 1),
OnClicked = (btn, obj) =>
{
int k = ((Pair<JobPrefab, int>)obj).Second;
btn.Parent.UserData = obj;
UpdateJobPreferences(listBox);
return false;
}
int k = ((Pair<JobPrefab, int>)obj).Second;
btn.Parent.UserData = obj;
UpdateJobPreferences(listBox);
return false;
};
}
}
//info button
new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopLeft, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, style: "GUIButtonInfo")
new GUIButton(new RectTransform(new Vector2(0.2f), slot.RectTransform, Anchor.TopLeft, scaleBasis: ScaleBasis.BothHeight)
{ RelativeOffset = new Vector2(0.05f) },
style: "GUIButtonInfo")
{
UserData = jobPrefab.First,
OnClicked = ViewJobInfo
};
//remove button
new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, style: "GUICancelButton")
new GUIButton(new RectTransform(new Vector2(0.2f), slot.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothHeight) { RelativeOffset = new Vector2(0.05f) }, style: "GUICancelButton")
{
UserData = i,
OnClicked = (btn, obj) =>
@@ -3013,6 +3029,24 @@ namespace Barotrauma
}
}
private GUIButton CreateJobVariantButton(Pair<JobPrefab, int> jobPrefab, int variantIndex, int variantCount, GUIComponent slot)
{
float relativeHeight = Math.Min(0.7f / variantCount, 0.2f);
var btn = new GUIButton(new RectTransform(new Vector2(relativeHeight), slot.RectTransform, scaleBasis: ScaleBasis.BothHeight)
{ RelativeOffset = new Vector2(0.05f, 0.25f + relativeHeight * 1.05f * variantIndex) },
(variantIndex + 1).ToString(), style: null)
{
Color = new Color(50, 50, 50, 200),
HoverColor = Color.Gray * 0.75f,
PressedColor = Color.Black * 0.75f,
SelectedColor = new Color(45, 70, 100, 200),
Selected = jobPrefab.Second == (variantIndex + 1),
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex + 1),
};
return btn;
}
public Pair<string, string> FailedSelectedSub;
public Pair<string, string> FailedSelectedShuttle;
@@ -13,6 +13,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Barotrauma
@@ -426,7 +427,7 @@ namespace Barotrauma
ToolTip = mode.Name,
Selected = true,
OnSelected = (tickBox) => { FilterServers(); return true; },
UserData = mode.Name
UserData = mode.Identifier
};
gameModeTickBoxes.Add(selectionTick);
filterTextList.Add(selectionTick.TextBlock);
@@ -736,6 +737,7 @@ namespace Barotrauma
info.PlayerCount = GameMain.Client.ConnectedClients.Count;
info.PingChecked = false;
info.HasPassword = serverSettings.HasPassword;
info.OwnerVerified = true;
if (isInfoNew)
{
@@ -747,6 +749,12 @@ namespace Barotrauma
public void AddToRecentServers(ServerInfo info)
{
if (!string.IsNullOrEmpty(info.IP))
{
//don't add localhost to recent servers
if (IPAddress.TryParse(info.IP, out IPAddress ip) && IPAddress.IsLoopback(ip)) { return; }
}
info.Recent = true;
ServerInfo existingInfo = recentServers.Find(serverInfo => info.OwnerID == serverInfo.OwnerID && (info.OwnerID != 0 ? true : (info.IP == serverInfo.IP && info.Port == serverInfo.Port)));
if (existingInfo == null)
@@ -898,6 +906,11 @@ namespace Barotrauma
base.Select();
SelectedTab = ServerListTab.All;
RefreshServers();
if (SteamManager.IsInitialized && SteamManager.Instance.LobbyList != null)
{
SteamManager.Instance.LobbyList.OnLobbyDataReceived = OnLobbyDataReceived;
}
}
public override void Deselect()
@@ -905,7 +918,7 @@ namespace Barotrauma
base.Deselect();
if (SteamManager.IsInitialized && SteamManager.Instance.LobbyList != null)
{
SteamManager.Instance.LobbyList.OnLobbiesUpdated = null;
SteamManager.Instance.LobbyList.OnLobbyDataReceived = null;
}
}
@@ -947,6 +960,7 @@ namespace Barotrauma
(remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion));
child.Visible =
serverInfo.OwnerVerified &&
serverInfo.ServerName.ToLowerInvariant().Contains(searchBox.Text.ToLowerInvariant()) &&
(!filterSameVersion.Selected || (remoteVersion != null && NetworkMember.IsCompatible(remoteVersion, GameMain.Version))) &&
(!filterPassword.Selected || !serverInfo.HasPassword) &&
@@ -1405,7 +1419,8 @@ namespace Barotrauma
{
yield return new WaitForSeconds((float)(refreshDisableTimer - DateTime.Now).TotalSeconds);
}
recentServers.Concat(favoriteServers).ForEach(si => si.OwnerVerified = false);
if (GameMain.Config.UseSteamMatchmaking)
{
serverList.ClearChildren();
@@ -1479,7 +1494,8 @@ namespace Barotrauma
PlayerCount = playerCount,
MaxPlayers = maxPlayers,
HasPassword = hasPassWord,
GameVersion = gameVersion
GameVersion = gameVersion,
OwnerVerified = true
};
foreach (string contentPackageName in contentPackageNames.Split(','))
{
@@ -1511,6 +1527,37 @@ namespace Barotrauma
}
}
private void OnLobbyDataReceived(Facepunch.Steamworks.LobbyList.Lobby lobby)
{
if (string.IsNullOrWhiteSpace(lobby.GetData("haspassword"))) { return; }
bool.TryParse(lobby.GetData("haspassword"), out bool hasPassword);
int.TryParse(lobby.GetData("playercount"), out int currPlayers);
int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers);
//UInt64.TryParse(lobby.GetData("connectsteamid"), out ulong connectSteamId);
string ip = lobby.GetData("hostipaddress");
UInt64 ownerId = SteamManager.SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
ServerInfo newInfo = new ServerInfo();
if (string.IsNullOrWhiteSpace(ip)) { ip = ""; }
newInfo.ServerName = lobby.Name;
newInfo.Port = "";
newInfo.QueryPort = "";
newInfo.IP = ip;
newInfo.PlayerCount = currPlayers;
newInfo.MaxPlayers = maxPlayers;
newInfo.HasPassword = hasPassword;
newInfo.RespondedToSteamQuery = true;
newInfo.LobbyID = lobby.LobbyID;
newInfo.OwnerID = ownerId;
newInfo.PingChecked = false;
newInfo.OwnerVerified = true;
SteamManager.AssignLobbyDataToServerInfo(lobby, newInfo);
AddToServerList(newInfo);
}
private void AddToServerList(ServerInfo serverInfo)
{
var serverFrame = serverList.Content.FindChild(d => (d.UserData is ServerInfo info) &&
@@ -1552,7 +1599,8 @@ namespace Barotrauma
if (serverInfo.OwnerVerified)
{
DebugConsole.NewMessage(serverInfo.OwnerID + " verified!");
var childrenToRemove = serverList.Content.FindChildren(c => (c.UserData is ServerInfo info) && info != serverInfo && info.OwnerID == serverInfo.OwnerID).ToList();
var childrenToRemove = serverList.Content.FindChildren(c => (c.UserData is ServerInfo info) && info != serverInfo &&
(serverInfo.OwnerID != 0 ? info.OwnerID == serverInfo.OwnerID : info.IP == serverInfo.IP)).ToList();
foreach (var child in childrenToRemove)
{
serverList.Content.RemoveChild(child);
@@ -1594,7 +1642,13 @@ namespace Barotrauma
UserData = "password"
};
var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[2] * 1.1f, 1.0f), serverContent.RectTransform), serverInfo.ServerName, style: "GUIServerListTextBox");
var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[2] * 1.1f, 1.0f), serverContent.RectTransform),
#if !DEBUG
serverInfo.ServerName,
#else
((serverInfo.OwnerID != 0 || serverInfo.LobbyID != 0) ? "[STEAMP2P] " : "[LIDGREN] ") + serverInfo.ServerName,
#endif
style: "GUIServerListTextBox");
new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[3], 0.9f), serverContent.RectTransform, Anchor.Center), label: "")
{
@@ -1793,7 +1847,7 @@ namespace Barotrauma
GameMain.Config.PlayerName = clientNameBox.Text;
GameMain.Config.SaveNewPlayerConfig();
CoroutineManager.StartCoroutine(ConnectToServer(endpoint, serverName));
CoroutineManager.StartCoroutine(ConnectToServer(endpoint, serverName), "ConnectToServer");
return true;
}
@@ -1822,40 +1876,31 @@ namespace Barotrauma
public void GetServerPing(ServerInfo serverInfo, GUITextBlock serverPingText)
{
if (activePings.Contains(serverInfo.IP)) { return; }
activePings.Add(serverInfo.IP);
if (CoroutineManager.IsCoroutineRunning("ConnectToServer")) { return; }
lock (activePings)
{
if (activePings.Contains(serverInfo.IP)) { return; }
activePings.Add(serverInfo.IP);
}
serverInfo.PingChecked = false;
serverInfo.Ping = -1;
var pingThread = new Thread(() => { PingServer(serverInfo, 1000); })
{
IsBackground = true
};
pingThread.Start();
CoroutineManager.StartCoroutine(UpdateServerPingText(serverInfo, serverPingText, 1000));
}
private IEnumerable<object> UpdateServerPingText(ServerInfo serverInfo, GUITextBlock serverPingText, int timeOut)
{
DateTime timeOutTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: timeOut);
while (DateTime.Now < timeOutTime)
{
if (serverInfo.PingChecked)
TaskPool.Add(PingServerAsync(serverInfo?.IP, 1000),
new Tuple<ServerInfo, GUITextBlock>(serverInfo, serverPingText),
(rtt, obj) =>
{
if (serverInfo.Ping != -1)
var info = obj.Item1;
var text = obj.Item2;
info.Ping = rtt.Result; info.PingChecked = true;
text.TextColor = GetPingTextColor(info.Ping);
text.Text = info.Ping > -1 ? info.Ping.ToString() : "?";
lock (activePings)
{
serverPingText.TextColor = GetPingTextColor(serverInfo.Ping);
}
serverPingText.Text = serverInfo.Ping > -1 ? serverInfo.Ping.ToString() : "?";
activePings.Remove(serverInfo.IP);
yield return CoroutineStatus.Success;
}
yield return CoroutineStatus.Running;
}
yield return CoroutineStatus.Success;
activePings.Remove(serverInfo.IP);
}
});
}
private Color GetPingTextColor(int ping)
@@ -1864,18 +1909,27 @@ namespace Barotrauma
return ToolBox.GradientLerp(ping / 200.0f, Color.LightGreen, Color.Yellow * 0.8f, Color.Red * 0.75f);
}
public void PingServer(ServerInfo serverInfo, int timeOut)
public async Task<int> PingServerAsync(string ip, int timeOut)
{
if (string.IsNullOrWhiteSpace(serverInfo?.IP))
await Task.Yield();
int activePingCount = 100;
while (activePingCount > 25)
{
serverInfo.PingChecked = true;
serverInfo.Ping = -1;
return;
lock (activePings)
{
activePingCount = activePings.Count;
}
await Task.Delay(25);
}
if (string.IsNullOrWhiteSpace(ip))
{
return -1;
}
long rtt = -1;
IPAddress address = null;
IPAddress.TryParse(serverInfo.IP, out address);
IPAddress.TryParse(ip, out address);
if (address != null)
{
//don't attempt to ping if the address is IPv6 and it's not supported
@@ -1902,8 +1956,8 @@ namespace Barotrauma
}
catch (PingException ex)
{
string errorMsg = "Failed to ping a server (" + serverInfo.ServerName + ", " + serverInfo.IP + ") - " + (ex?.InnerException?.Message ?? ex.Message);
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
string errorMsg = "Failed to ping a server (" + ip + ") - " + (ex?.InnerException?.Message ?? ex.Message);
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ip, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg);
#if DEBUG
DebugConsole.NewMessage(errorMsg, Color.Red);
#endif
@@ -1911,10 +1965,9 @@ namespace Barotrauma
}
}
serverInfo.PingChecked = true;
serverInfo.Ping = (int)rtt;
return (int)rtt;
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.CornflowerBlue);
@@ -30,7 +30,7 @@ namespace Barotrauma
private List<GUIButton> tabButtons = new List<GUIButton>();
private HashSet<string> pendingPreviewImageDownloads = new HashSet<string>();
private readonly HashSet<string> pendingPreviewImageDownloads = new HashSet<string>();
private Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>();
private enum Tab
@@ -379,10 +379,16 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(item.PreviewImageUrl))
{
string imagePreviewPath = Path.Combine(SteamManager.WorkshopItemPreviewImageFolder, item.Id + ".png");
if (!pendingPreviewImageDownloads.Contains(item.PreviewImageUrl))
{
pendingPreviewImageDownloads.Add(item.PreviewImageUrl);
bool isNewImage;
lock (pendingPreviewImageDownloads)
{
isNewImage = !pendingPreviewImageDownloads.Contains(item.PreviewImageUrl);
if (isNewImage) { pendingPreviewImageDownloads.Add(item.PreviewImageUrl); }
}
if (isNewImage)
{
if (File.Exists(imagePreviewPath))
{
File.Delete(imagePreviewPath);
@@ -397,10 +403,13 @@ namespace Barotrauma
var request = new RestRequest(fileName, Method.GET);
client.ExecuteAsync(request, response =>
{
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
lock (pendingPreviewImageDownloads)
{
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
}
OnPreviewImageDownloaded(response, imagePreviewPath);
CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath));
});
});
}
else
{
@@ -410,7 +419,10 @@ namespace Barotrauma
}
catch (Exception e)
{
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
lock (pendingPreviewImageDownloads)
{
pendingPreviewImageDownloads.Remove(item.PreviewImageUrl);
}
DebugConsole.ThrowError("Downloading the preview image of the Workshop item \"" + TextManager.EnsureUTF8(item.Title) + "\" failed.", e);
}
}
@@ -595,8 +607,13 @@ namespace Barotrauma
private IEnumerable<object> WaitForItemPreviewDownloaded(Facepunch.Steamworks.Workshop.Item item, GUIListBox listBox, string previewImagePath)
{
while (pendingPreviewImageDownloads.Contains(item.PreviewImageUrl))
while (true)
{
lock (pendingPreviewImageDownloads)
{
if (!pendingPreviewImageDownloads.Contains(item.PreviewImageUrl)) { break; }
}
yield return CoroutineStatus.Running;
}
@@ -522,11 +522,6 @@ namespace Barotrauma
OnSelected = (GUITickBox obj) => { Gap.ShowGaps = obj.Selected; return true; },
};
tickBoxHolder.Children.ForEach(c =>
{
if (c is GUITickBox tb) { tb.RectTransform.MinSize = new Point(0, 32); }
});
GUITextBlock.AutoScaleAndNormalize(tickBoxHolder.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
//spacing
@@ -14,7 +14,7 @@ namespace Barotrauma.SpriteDeformations
/// A positive value means that this deformation is or could be used for multiple sprites.
/// This behaviour is not automatic, and has to be implemented for any particular case separately (currently only used in Limbs).
/// </summary>
[Serialize(-1, true), Editable]
[Serialize(-1, true), Editable(minValue: -1, maxValue: 100)]
public int Sync
{
get;
@@ -64,7 +64,7 @@ namespace Barotrauma
{
foreach (RoundSound sound in sounds)
{
if (sound.Sound == null)
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
@@ -90,7 +90,7 @@ namespace Barotrauma
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound.Sound == null)
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);