(a00338777) v0.9.2.1

This commit is contained in:
Joonas Rikkonen
2019-08-26 19:58:19 +03:00
parent 0f63da27b2
commit 80698b58b0
311 changed files with 11763 additions and 4507 deletions

View File

@@ -40,6 +40,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\GameMain.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\CrewManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\CampaignMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\MultiPlayerCampaign.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\SinglePlayerCampaign.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\BasicTutorial.cs" />
@@ -165,6 +166,10 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetStats.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\OrderChatMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\ClientPeer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\LidgrenClientPeer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\SteamP2PClientPeer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\SteamP2POwnerPeer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\RespawnManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerLog.cs" />

View File

@@ -93,6 +93,9 @@
<Reference Include="nVLC, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\nVLC.3.0.0\lib\net40\nVLC.dll</HintPath>
</Reference>
<Reference Include="NVorbis, Version=0.8.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NVorbis.0.8.6\lib\net35\NVorbis.dll</HintPath>
</Reference>
<Reference Include="OpenTK, Version=3.0.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\OpenTK.3.0.1\lib\net20\OpenTK.dll</HintPath>
</Reference>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">ReleaseMac</Configuration>
@@ -91,6 +91,9 @@
<Reference Include="nVLC, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\nVLC.3.0.0\lib\net40\nVLC.dll</HintPath>
</Reference>
<Reference Include="NVorbis, Version=0.8.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NVorbis.0.8.6\lib\net35\NVorbis.dll</HintPath>
</Reference>
<Reference Include="OpenTK, Version=3.0.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\OpenTK.3.0.1\lib\net20\OpenTK.dll</HintPath>
</Reference>
@@ -316,8 +319,8 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="Icon.bmp">
<LogicalName>Icon.bmp</LogicalName>
<EmbeddedResource Include="Icon.bmp">
<LogicalName>Icon.bmp</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Import Project="ClientCode.projitems" Label="Shared" />

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.200.0")]
[assembly: AssemblyFileVersion("0.9.1.0")]
[assembly: AssemblyVersion("0.9.2.1")]
[assembly: AssemblyFileVersion("0.9.2.1")]

View File

@@ -185,9 +185,9 @@ namespace Barotrauma
position += amount;
}
public void ClientWrite(NetOutgoingMessage msg)
public void ClientWrite(IWriteMessage msg)
{
if (Character.Controlled != null && !Character.Controlled.IsDead) return;
if (Character.Controlled != null && !Character.Controlled.IsDead) { return; }
msg.Write((byte)ClientNetObject.SPECTATING_POS);
msg.Write(position.X);

View File

@@ -138,7 +138,7 @@ namespace Barotrauma
brokenItemsCheckTimer = 1.0f;
foreach (Item item in Item.ItemList)
{
if (!item.Repairables.Any(r => item.Condition < r.ShowRepairUIThreshold)) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage < r.ShowRepairUIThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition;
@@ -210,7 +210,7 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2);
textPos.Y += offset.Y;
if (character.FocusedCharacter.CanInventoryBeAccessed)
if (character.FocusedCharacter.CanBeDragged)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBind(InputType.Grab).ToString()),
Color.LightGreen, Color.Black, 2, GUI.SmallFont);

View File

@@ -1,5 +1,5 @@
using Barotrauma.Extensions;
using Lidgren.Network;
using Barotrauma.Networking;
using System;
using System.Linq;
using System.Collections.Generic;
@@ -53,7 +53,7 @@ namespace Barotrauma
if (personalityTrait != null && TextManager.Language == "English")
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), personalityTrait.Name), font: font);
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + personalityTrait.Name.Replace(" ", ""))), font: font);
}
//spacing
@@ -220,7 +220,7 @@ namespace Barotrauma
}
public static CharacterInfo ClientRead(string configPath, NetBuffer inc)
public static CharacterInfo ClientRead(string configPath, IReadMessage inc)
{
ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString();

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -22,12 +21,12 @@ namespace Barotrauma
}
//freeze AI characters if more than 1 seconds have passed since last update from the server
if (lastRecvPositionUpdateTime < NetTime.Now - 1.0f)
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - 1.0f)
{
AnimController.Frozen = true;
memState.Clear();
//hide after 2 seconds
if (lastRecvPositionUpdateTime < NetTime.Now - 2.0f)
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - 2.0f)
{
Enabled = false;
return;
@@ -99,22 +98,22 @@ namespace Barotrauma
}
}
public virtual void ClientWrite(NetBuffer msg, object[] extraData = null)
public virtual void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
if (extraData != null)
{
switch ((NetEntityEvent.Type)extraData[0])
{
case NetEntityEvent.Type.InventoryState:
msg.WriteRangedInteger(0, 3, 0);
msg.WriteRangedIntegerDeprecated(0, 3, 0);
Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Treatment:
msg.WriteRangedInteger(0, 3, 1);
msg.WriteRangedIntegerDeprecated(0, 3, 1);
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break;
case NetEntityEvent.Type.Status:
msg.WriteRangedInteger(0, 3, 2);
msg.WriteRangedIntegerDeprecated(0, 3, 2);
break;
}
}
@@ -132,7 +131,7 @@ namespace Barotrauma
msg.Write(inputCount);
for (int i = 0; i < inputCount; i++)
{
msg.WriteRangedInteger(0, (int)InputNetFlags.MaxVal, (int)memInput[i].states);
msg.WriteRangedIntegerDeprecated(0, (int)InputNetFlags.MaxVal, (int)memInput[i].states);
msg.Write(memInput[i].intAim);
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
@@ -147,14 +146,14 @@ namespace Barotrauma
msg.WritePadBits();
}
public virtual void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
switch (type)
{
case ServerNetObject.ENTITY_POSITION:
bool facingRight = AnimController.Dir > 0.0f;
lastRecvPositionUpdateTime = (float)NetTime.Now;
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
AnimController.Frozen = false;
Enabled = true;
@@ -222,8 +221,8 @@ namespace Barotrauma
}
Vector2 pos = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
msg.ReadSingle(),
msg.ReadSingle());
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
Vector2 linearVelocity = new Vector2(
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
@@ -235,7 +234,7 @@ namespace Barotrauma
float? angularVelocity = null;
if (!fixedRotation)
{
rotation = msg.ReadFloat();
rotation = msg.ReadSingle();
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
@@ -336,7 +335,7 @@ namespace Barotrauma
}
}
public static Character ReadSpawnData(NetBuffer inc, bool spawn = true)
public static Character ReadSpawnData(IReadMessage inc, bool spawn = true)
{
DebugConsole.NewMessage("READING CHARACTER SPAWN DATA", Color.Cyan);
@@ -347,7 +346,7 @@ namespace Barotrauma
string speciesName = inc.ReadString();
string seed = inc.ReadString();
Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat());
Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle());
bool enabled = inc.ReadBoolean();
@@ -414,8 +413,17 @@ namespace Barotrauma
return character;
}
private void ReadTraitorStatus(IReadMessage msg)
{
IsTraitor = msg.ReadBoolean();
if (IsTraitor)
{
TraitorCurrentObjective = msg.ReadString();
}
}
private void ReadStatus(NetBuffer msg)
private void ReadStatus(IReadMessage msg)
{
bool isDead = msg.ReadBoolean();
if (isDead)
@@ -450,7 +458,7 @@ namespace Barotrauma
else
{
if (IsDead) Revive();
CharacterHealth.ClientRead(msg);
}
}

View File

@@ -1,6 +1,5 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -1375,7 +1374,7 @@ namespace Barotrauma
(int)(limbHealth.HighlightArea.Height * scale));
}
public void ClientRead(NetBuffer inc)
public void ClientRead(IReadMessage inc)
{
List<Pair<AfflictionPrefab, float>> newAfflictions = new List<Pair<AfflictionPrefab, float>>();

View File

@@ -441,7 +441,7 @@ namespace Barotrauma
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
if (herpesStrength > 0.0f)
{
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * (herpesStrength / 100.0f), spriteEffect);
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * Math.Min(herpesStrength / 10.0f, 1.0f), spriteEffect);
depthStep += 0.000001f;
}
}

View File

@@ -199,6 +199,7 @@ namespace Barotrauma
return client.HasPermission(ClientPermissions.Kick);
case "ban":
case "banip":
case "banendpoint":
return client.HasPermission(ClientPermissions.Ban);
case "unban":
case "unbanip":
@@ -317,7 +318,13 @@ namespace Barotrauma
private static void AssignRelayToServer(string names, bool relay)
{
commands.First(c => c.names.Intersect(names.Split('|')).Count() > 0).RelayToServer = relay;
Command command = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
if (command == null)
{
DebugConsole.Log("Could not assign to relay to server: " + names);
return;
}
command.RelayToServer = relay;
}
private static void InitProjectSpecific()
@@ -358,16 +365,23 @@ namespace Barotrauma
}
}));
commands.Add(new Command("startclient", "", (string[] args) =>
commands.Add(new Command("startlidgrenclient", "", (string[] args) =>
{
if (args.Length == 0) return;
if (GameMain.Client == null)
{
GameMain.Client = new GameClient("Name", args[0]);
GameMain.Client = new GameClient("Name", args[0], 0);
}
}));
commands.Add(new Command("startsteamp2pclient", "", (string[] args) =>
{
if (GameMain.Client == null)
{
GameMain.Client = new GameClient("Name", null, 76561198977850505); //this is juan's alt account, feel free to abuse this one
}
}));
commands.Add(new Command("enablecheats", "enablecheats: Enables cheat commands and disables Steam achievements during this play session.", (string[] args) =>
{
@@ -465,10 +479,16 @@ namespace Barotrauma
commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true);
commands.Add(new Command("say", "", (string[] args) => { }));
AssignRelayToServer("say", true);
commands.Add(new Command("msg", "", (string[] args) => { }));
AssignRelayToServer("msg", true);
commands.Add(new Command("setmaxplayers|maxplayers", "", (string[] args) => { }));
AssignRelayToServer("setmaxplayers", true);
commands.Add(new Command("setpassword|password", "", (string[] args) => { }));
AssignRelayToServer("setpassword", true);
commands.Add(new Command("traitorlist", "", (string[] args) => { }));
AssignRelayToServer("traitorlist", true);
AssignOnExecute("control", (string[] args) =>
{
@@ -551,6 +571,7 @@ namespace Barotrauma
if (args.Length < 3)
{
ThrowError("Not enough arguments provided! At least three required.");
return;
}
if (!byte.TryParse(args[0], out byte r))
{
@@ -952,7 +973,36 @@ namespace Barotrauma
}
}, isCheat: false));
commands.Add(new Command("setentityproperties", "setentityproperties [property name] [value]: Sets the value of some property on all selected items/structures in the sub editor.", (string[] args) =>
{
if (args.Length != 2 || Screen.Selected != GameMain.SubEditorScreen) { return; }
foreach (MapEntity me in MapEntity.SelectedList)
{
if (me is ISerializableEntity serializableEntity)
{
if (serializableEntity.SerializableProperties == null)
{
continue;
}
if (!serializableEntity.SerializableProperties.TryGetValue(args[0].ToLowerInvariant(), out SerializableProperty property))
{
NewMessage("Property \"" + args[0] + "\" not found in the entity \"" + me.ToString() + "\".", Color.Orange);
continue;
}
if (!property.TrySetValue(me, args[1]))
{
NewMessage("Failed to set the value of \"" + args[0] + "\" to \"" + args[1] + "\" on the entity \"" + me.ToString() + "\".", Color.Orange);
}
}
}
}));
#if DEBUG
commands.Add(new Command("printreceivertransfers", "", (string[] args) =>
{
GameMain.Client.PrintReceiverTransters();
}));
commands.Add(new Command("checkmissingloca", "", (string[] args) =>
{
foreach (MapEntityPrefab me in MapEntityPrefab.List)
@@ -1201,6 +1251,8 @@ namespace Barotrauma
{
GameMain.Config.MusicVolume = 0.5f;
GameMain.Config.SoundVolume = 0.5f;
GameMain.Config.DynamicRangeCompressionEnabled = true;
GameMain.Config.VoipAttenuationEnabled = true;
NewMessage("Music and sound volume set to 0.5", Color.Green);
GameMain.Config.GraphicsWidth = 0;
@@ -1416,11 +1468,11 @@ namespace Barotrauma
);
AssignOnClientExecute(
"banip",
"banendpoint|banip",
(string[] args) =>
{
if (GameMain.Client == null || args.Length == 0) return;
ShowQuestionPrompt("Reason for banning the ip \"" + args[0] + "\"?", (reason) =>
ShowQuestionPrompt("Reason for banning the endpoint \"" + args[0] + "\"?", (reason) =>
{
ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) =>
{
@@ -1436,7 +1488,7 @@ namespace Barotrauma
}
GameMain.Client.SendConsoleCommand(
"banip " +
"banendpoint " +
args[0] + " " +
(banDuration.HasValue ? banDuration.Value.TotalSeconds.ToString() : "0") + " " +
reason);
@@ -1779,6 +1831,21 @@ namespace Barotrauma
character.ReloadHead(id, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
}
}, isCheat: true));
commands.Add(new Command("spawnsub", "spawnsub [subname]: Spawn a submarine at the position of the cursor", (string[] args) =>
{
try
{
Submarine spawnedSub = Submarine.Load(args[0], false);
spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
}
catch (Exception e)
{
string errorMsg = "Failed to spawn a submarine. Arguments: \"" + string.Join(" ", args) + "\".";
ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace);
}
}, isCheat: true));
}
private static void ReloadWearables(Character character, int variant = 0)

View File

@@ -4,16 +4,13 @@ namespace Barotrauma
{
partial class Mission
{
public void ShowMessage(int index)
partial void ShowMessageProjSpecific(int index)
{
if (index >= Headers.Count && index >= Messages.Count) return;
string header = index < Headers.Count ? Headers[index] : "";
string message = index < Messages.Count ? Messages[index] : "";
//TODO: reimplement
//GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);
new GUIMessageBox(header, message);
}
}

View File

@@ -5,19 +5,14 @@ namespace Barotrauma
{
partial class MissionMode : GameMode
{
public override void MsgBox()
public override void ShowStartMessage()
{
if (mission == null) return;
var missionMsg = new GUIMessageBox(mission.Name, mission.Description, new Vector2(0.25f, 0.0f), new Point(400, 200))
new GUIMessageBox(mission.Name, mission.Description, new Vector2(0.25f, 0.0f), new Point(400, 200))
{
UserData = "missionstartmessage"
};
#if SERVER
Networking.GameServer.Log(TextManager.Get("Mission") + ": " + mission.Name, Networking.ServerLog.MessageType.ServerMessage);
Networking.GameServer.Log(mission.Description, Networking.ServerLog.MessageType.ServerMessage);
#endif
}
}
}

View File

@@ -310,6 +310,16 @@ namespace Barotrauma
"Sounds (Ctrl+S to hide): ", Color.White, Color.Black * 0.5f, 0, SmallFont);
y += 15;
DrawString(spriteBatch, new Vector2(500, y),
"Current playback amplitude: " + GameMain.SoundManager.PlaybackAmplitude.ToString(), Color.White, Color.Black * 0.5f, 0, SmallFont);
y += 15;
DrawString(spriteBatch, new Vector2(500, y),
"Compressed dynamic range gain: " + GameMain.SoundManager.CompressionDynamicRangeGain.ToString(), Color.White, Color.Black * 0.5f, 0, SmallFont);
y += 15;
DrawString(spriteBatch, new Vector2(500, y),
"Loaded sounds: " + GameMain.SoundManager.LoadedSoundCount + " (" + GameMain.SoundManager.UniqueLoadedSoundCount + " unique)", Color.White, Color.Black * 0.5f, 0, SmallFont);
y += 15;
@@ -343,18 +353,21 @@ namespace Barotrauma
soundStr += " (looping)";
clr = Color.Yellow;
}
if (playingSoundChannel.IsStream)
{
soundStr += " (streaming)";
clr = Color.Lime;
}
if (!playingSoundChannel.IsPlaying)
{
soundStr += " (stopped)";
clr *= 0.5f;
}
else if (playingSoundChannel.Muffled)
{
soundStr += " (muffled)";
clr = Color.Lerp(clr, Color.LightGray, 0.5f);
}
}
DrawString(spriteBatch, new Vector2(500, y), soundStr, clr, Color.Black * 0.5f, 0, SmallFont);
@@ -640,6 +653,12 @@ namespace Barotrauma
return MouseOn;
}
public static bool HasSizeChanged(Point referenceResolution, float referenceUIScale, float referenceHUDScale)
{
return GameMain.GraphicsWidth != referenceResolution.X || GameMain.GraphicsHeight != referenceResolution.Y ||
referenceUIScale != Inventory.UIScale || referenceHUDScale != Scale;
}
public static void Update(float deltaTime)
{
HandlePersistingElements(deltaTime);
@@ -1461,6 +1480,9 @@ namespace Barotrauma
if (pauseMenuOpen)
{
Inventory.draggingItem = null;
Inventory.DraggingInventory = null;
PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f);
var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.35f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) });

View File

@@ -67,6 +67,21 @@ namespace Barotrauma
}
// TODO: refactor?
public GUIComponent FindChild(Func<GUIComponent, bool> predicate, bool recursive = false)
{
var matchingChild = Children.FirstOrDefault(predicate);
if (recursive && matchingChild == null)
{
foreach (GUIComponent child in Children)
{
matchingChild = child.FindChild(predicate, recursive);
if (matchingChild != null) return matchingChild;
}
}
return matchingChild;
}
public GUIComponent FindChild(object userData, bool recursive = false)
{
var matchingChild = Children.FirstOrDefault(c => c.userData == userData);

View File

@@ -1,11 +1,13 @@
using Microsoft.Xna.Framework;
using EventInput;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
public class GUIDropDown : GUIComponent
public class GUIDropDown : GUIComponent, IKeyboardSubscriber
{
public delegate bool OnSelectedHandler(GUIComponent selected, object obj = null);
public OnSelectedHandler OnSelected;
@@ -42,10 +44,22 @@ namespace Barotrauma
set { button.Enabled = value; }
}
public GUIComponent Selected
public GUIComponent SelectedComponent
{
get { return listBox.SelectedComponent; }
}
public bool Selected
{
get
{
return Dropped;
}
set
{
Dropped = value;
}
}
public GUIListBox ListBox
{
@@ -69,6 +83,30 @@ namespace Barotrauma
}
}
public void ReceiveTextInput(char inputChar)
{
GUI.KeyboardDispatcher.Subscriber = null;
}
public void ReceiveTextInput(string text) { }
public void ReceiveCommandInput(char command) { }
public void ReceiveSpecialInput(Keys key)
{
switch (key)
{
case Keys.Up:
case Keys.Down:
listBox.ReceiveSpecialInput(key);
GUI.KeyboardDispatcher.Subscriber = this;
break;
case Keys.Enter:
case Keys.Space:
case Keys.Escape:
GUI.KeyboardDispatcher.Subscriber = null;
break;
}
}
private List<object> selectedDataMultiple = new List<object>();
public IEnumerable<object> SelectedDataMultiple
{
@@ -287,6 +325,12 @@ namespace Barotrauma
{
OnDropped?.Invoke(this, userData);
listBox.UpdateScrollBarSize();
GUI.KeyboardDispatcher.Subscriber = this;
}
else if (GUI.KeyboardDispatcher.Subscriber == this)
{
GUI.KeyboardDispatcher.Subscriber = null;
}
return true;
}
@@ -343,6 +387,10 @@ namespace Barotrauma
if (!listBoxRect.Contains(PlayerInput.MousePosition) && !button.Rect.Contains(PlayerInput.MousePosition))
{
Dropped = false;
if (GUI.KeyboardDispatcher.Subscriber == this)
{
GUI.KeyboardDispatcher.Subscriber = null;
}
}
}
}

View File

@@ -651,7 +651,9 @@ namespace Barotrauma
case Keys.Up:
SelectPrevious();
break;
default:
case Keys.Enter:
case Keys.Space:
case Keys.Escape:
GUI.KeyboardDispatcher.Subscriber = null;
break;
}

View File

@@ -26,6 +26,7 @@ namespace Barotrauma
public TextGetterHandler TextGetter;
public bool Wrap;
private bool playerInput;
public bool RoundToNearestPixel = true;
@@ -193,7 +194,7 @@ namespace Barotrauma
/// If the rectT height is set 0, the height is calculated from the text.
/// </summary>
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
: base(style, rectT)
{
if (color.HasValue)
@@ -208,6 +209,7 @@ namespace Barotrauma
this.textAlignment = textAlignment;
this.Wrap = wrap;
this.Text = text ?? "";
this.playerInput = playerInput;
if (rectT.Rect.Height == 0 && !string.IsNullOrEmpty(text))
{
CalculateHeightFromText();
@@ -254,7 +256,7 @@ namespace Barotrauma
if (Wrap && rect.Width > 0)
{
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale);
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale, playerInput);
TextSize = MeasureText(wrappedText);
}
else if (OverflowClip)

View File

@@ -199,6 +199,12 @@ namespace Barotrauma
}
}
public Vector4 Padding
{
get { return textBlock.Padding; }
set { textBlock.Padding = value; }
}
// TODO: should this be defined in the stylesheet?
public Color SelectionColor { get; set; } = Color.White * 0.25f;
@@ -229,7 +235,7 @@ namespace Barotrauma
this.color = color ?? Color.White;
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
GUI.Style.Apply(frame, style == "" ? "GUITextBox" : style);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.Center), text, textColor, font, textAlignment, wrap);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.Center), text, textColor, font, textAlignment, wrap, playerInput: true);
GUI.Style.Apply(textBlock, "", this);
CaretEnabled = true;
caretPosDirty = true;
@@ -308,6 +314,7 @@ namespace Barotrauma
protected List<Tuple<Vector2, int>> GetAllPositions()
{
float halfHeight = Font.MeasureString("T").Y * 0.5f;
string textDrawn = Censor ? textBlock.CensoredText : textBlock.WrappedText;
var positions = new List<Tuple<Vector2, int>>();
if (textDrawn.Contains("\n"))
@@ -323,9 +330,9 @@ namespace Barotrauma
for (int j = 0; j <= line.Length; j++)
{
Vector2 lineTextSize = Font.MeasureString(line.Substring(0, j));
Vector2 indexPos = new Vector2(lineTextSize.X + textBlock.Padding.X, totalTextHeight + textBlock.Padding.Y);
Vector2 indexPos = new Vector2(lineTextSize.X + textBlock.Padding.X, totalTextHeight + textBlock.Padding.Y - halfHeight);
//DebugConsole.NewMessage($"index: {index}, pos: {indexPos}", Color.AliceBlue);
positions.Add(new Tuple<Vector2, int>(textBlock.Rect.Location.ToVector2() + indexPos, index + j));
positions.Add(new Tuple<Vector2, int>(indexPos, index + j));
}
index = totalIndex;
}
@@ -336,9 +343,9 @@ namespace Barotrauma
for (int i = 0; i <= textBlock.Text.Length; i++)
{
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, i));
Vector2 indexPos = new Vector2(textSize.X + textBlock.Padding.X, textSize.Y + textBlock.Padding.Y) + textBlock.TextPos - textBlock.Origin;
Vector2 indexPos = new Vector2(textSize.X + textBlock.Padding.X, textSize.Y + textBlock.Padding.Y - halfHeight) + textBlock.TextPos - textBlock.Origin;
//DebugConsole.NewMessage($"index: {i}, pos: {indexPos}", Color.WhiteSmoke);
positions.Add(new Tuple<Vector2, int>(textBlock.Rect.Location.ToVector2() + indexPos, i));
positions.Add(new Tuple<Vector2, int>(indexPos, i));
}
}
return positions;
@@ -346,10 +353,64 @@ namespace Barotrauma
public int GetCaretIndexFromScreenPos(Vector2 pos)
{
var positions = GetAllPositions().OrderBy(p => Vector2.DistanceSquared(p.Item1, pos));
var posIndex = positions.FirstOrDefault();
return GetCaretIndexFromLocalPos(pos - textBlock.Rect.Location.ToVector2());
}
public int GetCaretIndexFromLocalPos(Vector2 pos)
{
var positions = GetAllPositions();
if (positions.Count==0) { return 0; }
float halfHeight = Font.MeasureString("T").Y * 0.5f;
var currPosition = positions[0];
for (int i=1;i<positions.Count;i++)
{
var p1 = positions[i];
var p2 = currPosition;
float diffY = Math.Abs(p1.Item1.Y - pos.Y) - Math.Abs(p2.Item1.Y - pos.Y);
if (diffY < -3.0f)
{
currPosition = p1; continue;
}
else if (diffY > 3.0f)
{
continue;
}
else
{
diffY = Math.Abs(p1.Item1.Y - pos.Y);
if (diffY < halfHeight)
{
//we are on this line, select the nearest character
float diffX = Math.Abs(p1.Item1.X - pos.X) - Math.Abs(p2.Item1.X - pos.X);
if (diffX < -1.0f)
{
currPosition = p1; continue;
}
else
{
continue;
}
}
else
{
//we are on a different line, preserve order
if (p1.Item2 < p2.Item2)
{
if (p1.Item1.Y > pos.Y) { currPosition = p1; }
}
else if (p1.Item2 > p2.Item2)
{
if (p1.Item1.Y < pos.Y) { currPosition = p1; }
}
continue;
}
}
}
//GUI.AddMessage($"index: {posIndex.Item2}, pos: {posIndex.Item1}", Color.WhiteSmoke);
return posIndex != null ? posIndex.Item2 : textBlock.Text.Length;
return currPosition != null ? currPosition.Item2 : textBlock.Text.Length;
}
public void Select()
@@ -674,9 +735,9 @@ namespace Barotrauma
{
InitSelectionStart();
}
float lineHeight = Font.MeasureString(Text).Y;
int newIndex = GetCaretIndexFromScreenPos(new Vector2(CaretScreenPos.X, CaretScreenPos.Y - lineHeight / 2));
CaretIndex = newIndex != CaretIndex ? newIndex : 0;
float lineHeight = Font.MeasureString("T").Y;
int newIndex = GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y-lineHeight));
CaretIndex = newIndex;
caretTimer = 0;
HandleSelection();
break;
@@ -685,9 +746,9 @@ namespace Barotrauma
{
InitSelectionStart();
}
lineHeight = Font.MeasureString(Text).Y;
newIndex = GetCaretIndexFromScreenPos(new Vector2(CaretScreenPos.X, CaretScreenPos.Y + lineHeight * 2));
CaretIndex = newIndex != CaretIndex ? newIndex : Text.Length;
lineHeight = Font.MeasureString("T").Y;
newIndex = GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y+lineHeight));
CaretIndex = newIndex;
caretTimer = 0;
HandleSelection();
break;

View File

@@ -114,10 +114,13 @@ namespace Barotrauma
if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.LeftButtonHeld()))
{
Hovered?.Invoke();
if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None())
if (RequireMouseOn || PlayerInput.LeftButtonDown())
{
selectedWidgets.Add(this);
Selected?.Invoke();
if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None())
{
selectedWidgets.Add(this);
Selected?.Invoke();
}
}
}
else if (selectedWidgets.Contains(this))

View File

@@ -101,6 +101,10 @@ namespace Barotrauma
private GameTime fixedTime;
public string ConnectName;
public string ConnectEndpoint;
public UInt64 ConnectLobby;
private static SpriteBatch spriteBatch;
private Viewport defaultViewport;
@@ -175,6 +179,12 @@ namespace Barotrauma
ConsoleArguments = args;
ConnectName = null;
ConnectEndpoint = null;
ConnectLobby = 0;
ToolBox.ParseConnectCommand(ConsoleArguments, out ConnectName, out ConnectEndpoint, out ConnectLobby);
GUI.KeyboardDispatcher = new EventInput.KeyboardDispatcher(Window);
PerformanceCounter = new PerformanceCounter();
@@ -355,13 +365,13 @@ namespace Barotrauma
}
SoundManager = new Sounds.SoundManager();
SoundManager.SetCategoryGainMultiplier("default", Config.SoundVolume);
SoundManager.SetCategoryGainMultiplier("ui", Config.SoundVolume);
SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume);
SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume);
SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume * 20.0f);
SoundManager.SetCategoryGainMultiplier("default", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("ui", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume, 0);
SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume * 20.0f, 0);
if (ConsoleArguments.Contains("skipintro")) {
if (ConsoleArguments.Contains("-skipintro")) {
Config.EnableSplashScreen = false;
}
@@ -390,7 +400,7 @@ namespace Barotrauma
{
if (SteamManager.AutoUpdateWorkshopItems())
{
ContentPackage.LoadAll(ContentPackage.Folder);
ContentPackage.LoadAll();
Config.ReloadContentPackages();
}
}
@@ -494,7 +504,12 @@ namespace Barotrauma
if (SteamManager.USE_STEAM)
{
SteamWorkshopScreen = new SteamWorkshopScreen();
SteamWorkshopScreen = new SteamWorkshopScreen();
if (SteamManager.IsInitialized)
{
SteamManager.Instance.Friends.OnInvitedToGame += OnInvitedToGame;
SteamManager.Instance.Lobby.OnLobbyJoinRequested += OnLobbyJoinRequested;
}
}
SubEditorScreen = new SubEditorScreen();
@@ -594,6 +609,18 @@ namespace Barotrauma
}
}
public void OnInvitedToGame(Facepunch.Steamworks.SteamFriend friend, string connectCommand)
{
ToolBox.ParseConnectCommand(connectCommand.Split(' '), out ConnectName, out ConnectEndpoint, out ConnectLobby);
DebugConsole.NewMessage(ConnectName+", "+ConnectEndpoint,Color.Yellow);
}
public void OnLobbyJoinRequested(UInt64 lobbyId)
{
SteamManager.JoinLobby(lobbyId, true);
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
@@ -622,7 +649,7 @@ namespace Barotrauma
{
if (WindowActive || !Config.MuteOnFocusLost)
{
SoundManager.ListenerGain = 1.0f;
SoundManager.ListenerGain = SoundManager.CompressionDynamicRangeGain;
}
else
{
@@ -674,6 +701,40 @@ namespace Barotrauma
}
else if (hasLoaded)
{
if (ConnectLobby != 0)
{
if (Client != null)
{
Client.Disconnect();
Client = null;
GameMain.MainMenuScreen.Select();
}
Steam.SteamManager.JoinLobby(ConnectLobby, true);
ConnectLobby = 0;
ConnectEndpoint = null;
ConnectName = null;
}
else if (!string.IsNullOrWhiteSpace(ConnectEndpoint))
{
if (Client != null)
{
Client.Disconnect();
Client = null;
GameMain.MainMenuScreen.Select();
}
UInt64 serverSteamId = SteamManager.SteamIDStringToUInt64(ConnectEndpoint);
Client = new GameClient(SteamManager.GetUsername(),
serverSteamId != 0 ? null : ConnectEndpoint,
serverSteamId,
string.IsNullOrWhiteSpace(ConnectName) ? ConnectEndpoint : ConnectName);
ConnectLobby = 0;
ConnectEndpoint = null;
ConnectName = null;
}
SoundPlayer.Update((float)Timing.Step);
if (PlayerInput.KeyHit(Keys.Escape) && WindowActive)
@@ -754,6 +815,8 @@ namespace Barotrauma
SteamManager.Update((float)Timing.Step);
SoundManager?.Update();
Timing.Accumulator -= Timing.Step;
sw.Stop();

View File

@@ -1251,6 +1251,7 @@ namespace Barotrauma
if (!hoverArea.Contains(PlayerInput.MousePosition) || PlayerInput.RightButtonClicked())
{
orderTargetFrame = null;
OrderOptionButtons.Clear();
}
}
}
@@ -1361,7 +1362,9 @@ namespace Barotrauma
{
reportButtonFrame.Visible = true;
var reportButtonParent = ChatBox ?? GameMain.Client.ChatBox;
var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox;
if (reportButtonParent == null) { return; }
reportButtonFrame.RectTransform.AbsoluteOffset = new Point(
Math.Min(reportButtonParent.GUIFrame.Rect.X, reportButtonParent.ToggleButton.Rect.X) - reportButtonFrame.Rect.Width - (int)(10 * GUI.Scale),
reportButtonParent.GUIFrame.Rect.Y);

View File

@@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
namespace Barotrauma
{
abstract partial class CampaignMode : GameMode
{
public override void ShowStartMessage()
{
if (Mission == null) return;
new GUIMessageBox(Mission.Name, Mission.Description, new Vector2(0.25f, 0.0f), new Point(400, 200))
{
UserData = "missionstartmessage"
};
}
}
}

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -10,6 +9,8 @@ namespace Barotrauma
{
partial class MultiPlayerCampaign : CampaignMode
{
public bool SuppressStateSending = false;
private UInt16 startWatchmanID, endWatchmanID;
public static GUIComponent StartCampaignSetup( IEnumerable<Submarine> submarines, IEnumerable<string> saveFiles)
@@ -129,7 +130,7 @@ namespace Barotrauma
}
}
public void ClientWrite(NetBuffer msg)
public void ClientWrite(IWriteMessage msg)
{
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
@@ -147,7 +148,7 @@ namespace Barotrauma
}
//static because we may need to instantiate the campaign if it hasn't been done yet
public static void ClientRead(NetBuffer msg)
public static void ClientRead(IReadMessage msg)
{
byte campaignID = msg.ReadByte();
UInt16 updateID = msg.ReadUInt16();
@@ -213,6 +214,8 @@ namespace Barotrauma
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
{
campaign.SuppressStateSending = true;
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
@@ -236,6 +239,8 @@ namespace Barotrauma
}
campaign.lastUpdateID = updateID;
campaign.SuppressStateSending = false;
}
}

View File

@@ -218,7 +218,7 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (!captain_sonar.IsActive);
} while (captain_sonar.CurrentMode != Sonar.Mode.Active);
do { yield return null; } while (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 4000f);
RemoveCompletedObjective(segments[5]);
yield return new WaitForSeconds(4f, false);

View File

@@ -73,6 +73,7 @@ namespace Barotrauma.Tutorials
private Character mechanic;
private Sprite mechanic_repairIcon;
private Color mechanic_repairIconColor;
private Sprite mechanic_weldIcon;
public MechanicTutorial(XElement element) : base(element)
{
@@ -97,6 +98,7 @@ namespace Barotrauma.Tutorials
var repairOrder = Order.PrefabList.Find(order => order.AITag == "repairsystems");
mechanic_repairIcon = repairOrder.SymbolSprite;
mechanic_repairIconColor = repairOrder.Color;
mechanic_weldIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(1, 256, 127, 127), new Vector2(0.5f, 0.5f));
// Other tutorial items
tutorial_securityFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoorlight")).GetComponent<LightComponent>();
@@ -309,7 +311,7 @@ namespace Barotrauma.Tutorials
yield return null;
} while (!mechanic.HasEquippedItem("divingmask") || !mechanic.HasEquippedItem("weldingtool")); // Wait until equipped
SetDoorAccess(mechanic_secondDoor, mechanic_secondDoorLight, true);
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_1, mechanic_repairIcon, mechanic_repairIconColor);
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_1, mechanic_weldIcon, mechanic_repairIconColor);
do { yield return null; } while (WallHasDamagedSections(mechanic_brokenWall_1)); // Highlight until repaired
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_1);
RemoveCompletedObjective(segments[2]);
@@ -443,10 +445,10 @@ namespace Barotrauma.Tutorials
{
HighlightInventorySlot(mechanic_fabricator.OutputContainer.Inventory, "extinguisher", highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic.Inventory.slots.Length; i++)
/*for (int i = 0; i < mechanic.Inventory.slots.Length; i++)
{
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}*/
}
else if (mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium") != null && mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("sodium") != null && !mechanic_fabricator.IsActive)
{
@@ -521,7 +523,7 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, true);
// Room 7
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_2, mechanic_repairIcon, mechanic_repairIconColor);
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_2, mechanic_weldIcon, mechanic_repairIconColor);
do { yield return null; } while (WallHasDamagedSections(mechanic_brokenWall_2));
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_2);
TriggerTutorialSegment(9, GameMain.Config.KeyBind(InputType.Use)); // Repairing machinery (pump)

View File

@@ -305,10 +305,17 @@ namespace Barotrauma.Tutorials
do
{
float distance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerhead.WorldPosition);
if (distance > originalDistance * 1.5f)
{
// Don't let the Hammerhead go too far.
officer_hammerhead.TeleportTo(officer_hammerheadSpawnPos + new Vector2(0, -1000));
}
if (distance > originalDistance)
{
// Don't let the Hammerhead go too far from the periscope.
officer_hammerhead.TeleportTo(officer_hammerheadSpawnPos);
// Ensure that the Hammerhead targets the player
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
var ai = officer_hammerhead.AIController as EnemyAIController;
ai.sight = 2.0f;
}
yield return null;
}

View File

@@ -110,21 +110,33 @@ namespace Barotrauma
{
infoFrameContent.ClearChildren();
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), infoFrameContent.RectTransform))
var isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
var missionFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, isTraitor ? 0.95f : 0.45f), infoFrameContent.RectTransform))
{
RelativeSpacing = 0.05f
};
if (Mission == null)
if (Mission != null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform, Anchor.TopCenter), TextManager.Get("NoMission"));
return;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), Mission.Name, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), TextManager.GetWithVariable("MissionReward", "[reward]", Mission.Reward.ToString()));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), Mission.Description, wrap: true);
}
else
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform, Anchor.TopCenter), TextManager.Get("NoMission"), font: GUI.LargeFont);
}
if (isTraitor)
{
var traitorFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.45f), infoFrameContent.RectTransform, Anchor.BottomLeft))
{
RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorFrame.RectTransform), TextManager.Get("Traitors"), font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorFrame.RectTransform), GameMain.Client.Character.TraitorCurrentObjective, wrap: true);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Mission.Name, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), TextManager.GetWithVariable("MissionReward", "[reward]", Mission.Reward.ToString()));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), Mission.Description, wrap: true);
}
public void AddToGUIUpdateList()

View File

@@ -57,10 +57,11 @@ namespace Barotrauma
var infoText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
summaryText, wrap: true);
GUIComponent endText = null;
if (!string.IsNullOrWhiteSpace(endMessage))
{
var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
endMessage, wrap: true);
endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
TextManager.GetServerMessage(endMessage), wrap: true);
}
//don't show the mission info if the mission was not completed and there's no localized "mission failed" text available
@@ -70,7 +71,9 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(message))
{
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), infoTextBox.Content.RectTransform), style: null);
var spacingTransform = new RectTransform(new Vector2(1.0f, 0.1f), infoTextBox.Content.RectTransform);
new GUIFrame(spacingTransform, style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("Mission"), GameMain.GameSession.Mission.Name),
@@ -86,12 +89,7 @@ namespace Barotrauma
}
}
}
foreach (GUIComponent child in infoTextBox.Content.Children)
{
child.CanBeFocused = false;
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
TextManager.Get("RoundSummaryCrewStatus"), font: GUI.LargeFont);
@@ -166,6 +164,16 @@ namespace Barotrauma
UserData = "buttonarea"
};
paddedFrame.Recalculate();
foreach (GUIComponent child in infoTextBox.Content.Children)
{
child.CanBeFocused = false;
if (child is GUITextBlock textBlock)
{
textBlock.CalculateHeightFromText();
}
}
return frame;
}
}

View File

@@ -73,11 +73,23 @@ namespace Barotrauma
public void CreateSettingsFrame(Tab selectedTab = Tab.Graphics)
{
settingsFrame = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), GUI.Canvas, Anchor.Center));
RectTransform settingsHolder = null;
if (Screen.Selected == GameMain.MainMenuScreen)
{
settingsFrame = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), GUI.Canvas, Anchor.Center));
settingsHolder = settingsFrame.RectTransform;
}
else
{
settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null, color: Color.Black * 0.5f);
var settingsFrameContent = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), settingsFrame.RectTransform, Anchor.Center));
settingsHolder = settingsFrameContent.RectTransform;
}
Vector2 tickBoxScale = Vector2.One * 0.05f;
var settingsFramePadding = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), settingsFrame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { RelativeSpacing = 0.01f, IsHorizontal = true };
var settingsFramePadding = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), settingsHolder, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { RelativeSpacing = 0.01f, IsHorizontal = true };
/// General tab --------------------------------------------------------------
@@ -104,6 +116,10 @@ namespace Barotrauma
OnSelected = SelectContentPackage,
Selected = SelectedContentPackages.Contains(contentPackage)
};
if (contentPackage.CorePackage)
{
tickBox.TextColor = Color.White;
}
if (!contentPackage.IsCompatible())
{
tickBox.TextColor = Color.Red;
@@ -251,11 +267,11 @@ namespace Barotrauma
Selected = VSyncEnabled
};
//TODO: remove hardcoded texts after the texts have been added to localization
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform, scaleBasis: ScaleBasis.BothHeight),
TextManager.Get("PauseOnFocusLost", returnNull: true) ?? "Pause on focus lost");
TextManager.Get("PauseOnFocusLost"));
pauseOnFocusLostBox.Selected = PauseOnFocusLost;
pauseOnFocusLostBox.ToolTip = TextManager.Get("PauseOnFocusLostToolTip", returnNull: true) ?? "Pauses the game when its window is not in focus. Note that the game won't be paused when a multiplayer session is active.";
pauseOnFocusLostBox.ToolTip = TextManager.Get("PauseOnFocusLostToolTip");
pauseOnFocusLostBox.OnSelected = (tickBox) =>
{
PauseOnFocusLost = tickBox.Selected;
@@ -377,12 +393,12 @@ namespace Barotrauma
/// Audio tab ----------------------------------------------------------------
var audioSliders = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter)
var audioSliders = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.3f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter)
{ RelativeOffset = new Vector2(0.0f, 0.02f) })
{ RelativeSpacing = 0.01f };
GUITextBlock soundVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TextManager.Get("SoundVolume"));
GUIScrollBar soundScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform),
GUITextBlock soundVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform), TextManager.Get("SoundVolume"));
GUIScrollBar soundScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform),
barSize: 0.05f)
{
UserData = soundVolumeText,
@@ -397,8 +413,8 @@ namespace Barotrauma
};
soundScrollBar.OnMoved(soundScrollBar, soundScrollBar.BarScroll);
GUITextBlock musicVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TextManager.Get("MusicVolume"));
GUIScrollBar musicScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform),
GUITextBlock musicVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform), TextManager.Get("MusicVolume"));
GUIScrollBar musicScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform),
barSize: 0.05f)
{
UserData = musicVolumeText,
@@ -413,8 +429,8 @@ namespace Barotrauma
};
musicScrollBar.OnMoved(musicScrollBar, musicScrollBar.BarScroll);
GUITextBlock voiceChatVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TextManager.Get("VoiceChatVolume"));
GUIScrollBar voiceChatScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform),
GUITextBlock voiceChatVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform), TextManager.Get("VoiceChatVolume"));
GUIScrollBar voiceChatScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.15f), audioSliders.RectTransform),
barSize: 0.05f)
{
UserData = voiceChatVolumeText,
@@ -429,7 +445,17 @@ namespace Barotrauma
};
voiceChatScrollBar.OnMoved(voiceChatScrollBar, voiceChatScrollBar.BarScroll);
GUITickBox muteOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, audioSliders.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("MuteOnFocusLost"));
var leftTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.28f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopLeft)
{ RelativeOffset = new Vector2(0.02f, 0.32f) })
{ RelativeSpacing = 0.01f };
var centerTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopLeft)
{ RelativeOffset = new Vector2(0.3f, 0.32f) })
{ RelativeSpacing = 0.01f };
var rightTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopRight)
{ RelativeOffset = new Vector2(0.02f, 0.32f) })
{ RelativeSpacing = 0.01f };
GUITickBox muteOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, leftTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("MuteOnFocusLost"));
muteOnFocusLostBox.Selected = MuteOnFocusLost;
muteOnFocusLostBox.ToolTip = TextManager.Get("MuteOnFocusLostToolTip");
muteOnFocusLostBox.OnSelected = (tickBox) =>
@@ -439,7 +465,31 @@ namespace Barotrauma
return true;
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TextManager.Get("VoiceChat"));
GUITickBox dynamicRangeCompressionTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, centerTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("DynamicRangeCompression"));
dynamicRangeCompressionTickBox.Selected = DynamicRangeCompressionEnabled;
dynamicRangeCompressionTickBox.ToolTip = TextManager.Get("DynamicRangeCompressionToolTip");
dynamicRangeCompressionTickBox.OnSelected = (tickBox) =>
{
DynamicRangeCompressionEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
GUITickBox voipAttenuationTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, rightTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("VoipAttenuation"));
voipAttenuationTickBox.Selected = VoipAttenuationEnabled;
voipAttenuationTickBox.ToolTip = TextManager.Get("VoipAttenuationToolTip");
voipAttenuationTickBox.OnSelected = (tickBox) =>
{
VoipAttenuationEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var voipSettings = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.4f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter)
{ RelativeOffset = new Vector2(0.0f, 0.38f) })
{ RelativeSpacing = 0.01f };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), voipSettings.RectTransform), TextManager.Get("VoiceChat"));
IList<string> deviceNames = Alc.GetStringList((IntPtr)null, Alc.CaptureDeviceSpecifier);
foreach (string name in deviceNames)
@@ -447,7 +497,7 @@ namespace Barotrauma
DebugConsole.NewMessage(name + " " + name.Length.ToString(), Color.Lime);
}
GUITickBox directionalVoiceChat = new GUITickBox(new RectTransform(tickBoxScale, audioSliders.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("DirectionalVoiceChat"));
GUITickBox directionalVoiceChat = new GUITickBox(new RectTransform(tickBoxScale / 0.4f, voipSettings.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("DirectionalVoiceChat"));
directionalVoiceChat.Selected = UseDirectionalVoiceChat;
directionalVoiceChat.ToolTip = TextManager.Get("DirectionalVoiceChatToolTip");
directionalVoiceChat.OnSelected = (tickBox) =>
@@ -466,7 +516,7 @@ namespace Barotrauma
VoiceSetting = VoiceMode.Disabled;
}
#if (!OSX)
var deviceList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TrimAudioDeviceName(VoiceCaptureDevice), deviceNames.Count);
var deviceList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.15f), voipSettings.RectTransform), TrimAudioDeviceName(VoiceCaptureDevice), deviceNames.Count);
if (deviceNames?.Count > 0)
{
foreach (string name in deviceNames)
@@ -490,7 +540,7 @@ namespace Barotrauma
}
#else
var defaultDeviceGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), audioSliders.RectTransform), true, Anchor.CenterLeft);
var defaultDeviceGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), voipSettings.RectTransform), true, Anchor.CenterLeft);
var currentDeviceTextBlock = new GUITextBlock(new RectTransform(new Vector2(.7f, 0.75f), null),
TextManager.AddPunctuation(':', TextManager.Get("CurrentDevice"), TrimAudioDeviceName(VoiceCaptureDevice)))
{
@@ -526,13 +576,12 @@ namespace Barotrauma
currentDeviceTextBlock.RectTransform.Parent = defaultDeviceGroup.RectTransform;
#endif
//var radioButtonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.12f), audioSliders.RectTransform));
GUIRadioButtonGroup voiceMode = new GUIRadioButtonGroup();
for (int i = 0; i < 3; i++)
{
string langStr = "VoiceMode." + ((VoiceMode)i).ToString();
var tick = new GUITickBox(new RectTransform(tickBoxScale, audioSliders.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get(langStr))
var tick = new GUITickBox(new RectTransform(tickBoxScale / 0.4f, voipSettings.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get(langStr))
{
ToolTip = TextManager.Get(langStr + "ToolTip")
};
@@ -540,8 +589,8 @@ namespace Barotrauma
voiceMode.AddRadioButton((VoiceMode)i, tick);
}
var micVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), TextManager.Get("MicrophoneVolume"));
var micVolumeSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform),
var micVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), voipSettings.RectTransform), TextManager.Get("MicrophoneVolume"));
var micVolumeSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.15f), voipSettings.RectTransform),
barSize: 0.05f)
{
UserData = micVolumeText,
@@ -559,7 +608,7 @@ namespace Barotrauma
micVolumeSlider.OnMoved(micVolumeSlider, micVolumeSlider.BarScroll);
var extraVoiceSettingsContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), audioSliders.RectTransform, Anchor.BottomCenter), style: null);
var extraVoiceSettingsContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f), voipSettings.RectTransform, Anchor.BottomCenter), style: null);
var voiceInputContainer = new GUILayoutGroup(new RectTransform(Vector2.One, extraVoiceSettingsContainer.RectTransform, Anchor.BottomCenter));
new GUITextBlock(new RectTransform(new Vector2(0.6f, 0.25f), voiceInputContainer.RectTransform), TextManager.Get("InputType.Voice"));

View File

@@ -107,6 +107,17 @@ namespace Barotrauma
SetSlotPositions(layout);
}
protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
{
var item = Items[slotIndex];
if (item == null) return null;
var container = item.GetComponent<ItemContainer>();
if (container == null || !container.KeepOpenWhenEquipped || !character.HasEquippedItem(container.Item)) return null;
return container.Inventory;
}
protected override void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true)
{
base.PutItem(item, i, user, removeItem, createNetworkEvent);
@@ -456,7 +467,7 @@ namespace Barotrauma
}
}
}
List<SlotReference> hideSubInventories = new List<SlotReference>();
foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots)
{
@@ -465,6 +476,8 @@ namespace Barotrauma
UpdateSubInventory(deltaTime, highlightedSubInventorySlot.SlotIndex, cam);
}
if (!highlightedSubInventorySlot.Inventory.IsInventoryHoverAvailable(character, null)) continue;
Rectangle hoverArea = GetSubInventoryHoverArea(highlightedSubInventorySlot);
if (highlightedSubInventorySlot.Inventory?.slots == null || (!hoverArea.Contains(PlayerInput.MousePosition)))
{
@@ -476,35 +489,16 @@ namespace Barotrauma
}
}
if (doubleClickedItem != null)
{
QuickUseItem(doubleClickedItem, true, true, true);
}
//activate the subinventory of the currently selected slot
if (selectedSlot?.ParentInventory == this)
{
var subInventory = GetSubInventory(selectedSlot.SlotIndex);
if (subInventory != null)
if (subInventory != null && subInventory.IsInventoryHoverAvailable(character, null))
{
selectedSlot.Inventory = subInventory;
if (!highlightedSubInventorySlots.Any(s => s.Inventory == subInventory))
{
var slot = selectedSlot;
highlightedSubInventorySlots.Add(selectedSlot);
UpdateSubInventory(deltaTime, selectedSlot.SlotIndex, cam);
//hide previously opened subinventories if this one overlaps with them
Rectangle hoverArea = GetSubInventoryHoverArea(slot);
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
if (highlightedSubInventorySlot == slot) continue;
if (hoverArea.Intersects(GetSubInventoryHoverArea(highlightedSubInventorySlot)))
{
hideSubInventories.Add(highlightedSubInventorySlot);
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
}
ShowSubInventory(selectedSlot, deltaTime, cam, hideSubInventories, false);
}
}
}
@@ -513,66 +507,49 @@ namespace Barotrauma
{
if (subInventorySlot.Inventory == null) continue;
subInventorySlot.Inventory.HideTimer -= deltaTime;
if (subInventorySlot.Inventory.HideTimer <= 0.0f)
if (subInventorySlot.Inventory.HideTimer < 0.25f)
{
highlightedSubInventorySlots.Remove(subInventorySlot);
}
}
for (int i = 0; i < capacity; i++)
if (character.SelectedCharacter == null) // Permanently open subinventories only available when the default UI layout is in use -> not when grabbing characters
{
if (Items[i] != null && Items[i].AllowedSlots.Any(a => a != InvSlotType.Any))
for (int i = 0; i < capacity; i++)
{
slots[i].EquipButtonState = slots[i].EquipButtonRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld())
var item = Items[i];
if (item != null)
{
slots[i].EquipButtonState = GUIComponent.ComponentState.None;
}
if (slots[i].EquipButtonState != GUIComponent.ComponentState.Hover)
{
slots[i].QuickUseTimer = Math.Max(0.0f, slots[i].QuickUseTimer - deltaTime * 5.0f);
continue;
}
var quickUseAction = GetQuickUseAction(Items[i], allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
slots[i].QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.Get("QuickUseAction." + quickUseAction.ToString());
//equipped item that can't be put in the inventory, use delayed dropping
if (quickUseAction == QuickUseAction.Drop)
{
slots[i].QuickUseButtonToolTip =
TextManager.Get("QuickUseAction.HoldToUnequip", returnNull: true) ??
(GameMain.Config.Language == "English" ? "Hold to unequip" : TextManager.Get("QuickUseAction.Unequip"));
if (PlayerInput.LeftButtonHeld())
if (HideSlot(i)) continue;
if (character.HasEquippedItem(item)) // Keep a subinventory display open permanently when the container is equipped
{
slots[i].QuickUseTimer = Math.Max(0.1f, slots[i].QuickUseTimer + deltaTime);
if (slots[i].QuickUseTimer >= 1.0f)
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null && itemContainer.KeepOpenWhenEquipped && !highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
{
Items[i].Drop(Character.Controlled);
GUI.PlayUISound(GUISoundType.DropItem);
ShowSubInventory(new SlotReference(this, slots[i], i, false, itemContainer.Inventory), deltaTime, cam, hideSubInventories, true);
}
}
else
{
slots[i].QuickUseTimer = Math.Max(0.0f, slots[i].QuickUseTimer - deltaTime * 5.0f);
}
}
else
{
if (PlayerInput.LeftButtonDown()) slots[i].EquipButtonState = GUIComponent.ComponentState.Pressed;
if (PlayerInput.LeftButtonClicked())
{
QuickUseItem(Items[i], allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
}
}
}
}
if (doubleClickedItem != null)
{
QuickUseItem(doubleClickedItem, true, true, true);
}
for (int i = 0; i < capacity; i++)
{
var item = Items[i];
if (item != null)
{
var slot = slots[i];
if (item.AllowedSlots.Any(a => a != InvSlotType.Any))
{
HandleButtonEquipStates(item, slot, deltaTime);
}
}
}
//cancel dragging if too far away from the container of the dragged item
if (draggingItem != null)
@@ -603,6 +580,89 @@ namespace Barotrauma
doubleClickedItem = null;
}
private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime)
{
slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld())
{
slot.EquipButtonState = GUIComponent.ComponentState.None;
}
if (slot.EquipButtonState != GUIComponent.ComponentState.Hover)
{
slot.QuickUseTimer = Math.Max(0.0f, slot.QuickUseTimer - deltaTime * 5.0f);
return;
}
var quickUseAction = GetQuickUseAction(item, allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.Get("QuickUseAction." + quickUseAction.ToString());
//equipped item that can't be put in the inventory, use delayed dropping
if (quickUseAction == QuickUseAction.Drop)
{
slot.QuickUseButtonToolTip =
TextManager.Get("QuickUseAction.HoldToUnequip", returnNull: true) ??
(GameMain.Config.Language == "English" ? "Hold to unequip" : TextManager.Get("QuickUseAction.Unequip"));
if (PlayerInput.LeftButtonHeld())
{
slot.QuickUseTimer = Math.Max(0.1f, slot.QuickUseTimer + deltaTime);
if (slot.QuickUseTimer >= 1.0f)
{
item.Drop(Character.Controlled);
GUI.PlayUISound(GUISoundType.DropItem);
}
}
else
{
slot.QuickUseTimer = Math.Max(0.0f, slot.QuickUseTimer - deltaTime * 5.0f);
}
}
else
{
if (PlayerInput.LeftButtonDown()) slot.EquipButtonState = GUIComponent.ComponentState.Pressed;
if (PlayerInput.LeftButtonClicked())
{
QuickUseItem(item, allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
}
}
}
private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory)
{
Rectangle hoverArea = GetSubInventoryHoverArea(slotRef);
if (isEquippedSubInventory)
{
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
if (highlightedSubInventorySlot == slotRef) continue;
if (hoverArea.Intersects(GetSubInventoryHoverArea(highlightedSubInventorySlot)))
{
return; // If an equipped one intersects with a currently active hover one, do not open
}
}
}
slotRef.Inventory.OpenState = isEquippedSubInventory ? 1f : 0f; // Reset animation when initially equipped
highlightedSubInventorySlots.Add(slotRef);
slotRef.Inventory.HideTimer = 1f;
UpdateSubInventory(deltaTime, slotRef.SlotIndex, cam);
//hide previously opened subinventories if this one overlaps with them
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
if (highlightedSubInventorySlot == slotRef) continue;
if (hoverArea.Intersects(GetSubInventoryHoverArea(highlightedSubInventorySlot)))
{
hideSubInventories.Add(highlightedSubInventorySlot);
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
}
}
private void AssignQuickUseNumKeys()
{
@@ -633,14 +693,34 @@ namespace Barotrauma
if (item.ParentInventory != this)
{
//in another inventory -> attempt to place in the character's inventory
if (item.ParentInventory.Locked)
if (item.ParentInventory.Locked || item.ParentInventory == null)
{
return QuickUseAction.None;
}
else if (allowInventorySwap)
{
return item.ParentInventory is CharacterInventory ?
QuickUseAction.TakeFromCharacter : QuickUseAction.TakeFromContainer;
if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory
{
return item.ParentInventory is CharacterInventory ?
QuickUseAction.TakeFromCharacter : QuickUseAction.TakeFromContainer;
}
else
{
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked &&
allowInventorySwap)
{
// Move the item from the subinventory to the selected container
return QuickUseAction.PutToContainer;
}
else
{
// Take from the subinventory and place it in the character's main inventory if no target container is selected
return QuickUseAction.TakeFromContainer;
}
}
}
}
else
@@ -756,7 +836,23 @@ namespace Barotrauma
}
break;
case QuickUseAction.TakeFromContainer:
success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true);
// Check open subinventories and put the item in it if equipped
ItemInventory activeSubInventory = null;
for (int i = 0; i < capacity; i++)
{
activeSubInventory = GetActiveEquippedSubInventory(i);
if (activeSubInventory != null)
{
success = activeSubInventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true);
break;
}
}
// No subinventory found or placing unsuccessful -> attempt to put in the character's inventory
if (!success)
{
success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true);
}
break;
}

View File

@@ -134,7 +134,7 @@ namespace Barotrauma.Items.Components
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
pos.Y = -pos.Y;
if (brokenSprite == null || item.Health > 0.0f)
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
new Rectangle((int) (doorSprite.SourceRect.X + doorSprite.size.X * openState),
@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
pos.Y = -pos.Y;
if (brokenSprite == null || item.Health > 0.0f)
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
new Rectangle(doorSprite.SourceRect.X,
@@ -217,7 +217,7 @@ namespace Barotrauma.Items.Components
}
public override void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
base.ClientRead(type, msg, sendingTime);

View File

@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -188,7 +187,9 @@ namespace Barotrauma.Items.Components
if (loopingSound != null)
{
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > loopingSound.Range * loopingSound.Range)
float targetGain = 0.0f;
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > loopingSound.Range * loopingSound.Range ||
(targetGain = GetSoundVolume(loopingSound)) <= 0.0001f)
{
if (loopingSoundChannel != null)
{
@@ -220,7 +221,6 @@ namespace Barotrauma.Items.Components
lastMuffleCheckTime = (float)Timing.TotalTime;
}
loopingSoundChannel.Muffled = shouldMuffleLooping;
float targetGain = GetSoundVolume(loopingSound);
float gainDiff = targetGain - loopingSoundChannel.Gain;
loopingSoundChannel.Gain += Math.Abs(gainDiff) < 0.1f ? gainDiff : Math.Sign(gainDiff) * 0.1f;
loopingSoundChannel.Position = new Vector3(position.X, position.Y, 0.0f);
@@ -258,7 +258,6 @@ namespace Barotrauma.Items.Components
itemSound = matchingSounds[index];
PlaySound(matchingSounds[index], position, user);
}
}
@@ -278,6 +277,8 @@ namespace Barotrauma.Items.Components
}
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
float volume = GetSoundVolume(itemSound);
if (volume <= 0.0001f) { return; }
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
0.01f,
@@ -291,7 +292,7 @@ namespace Barotrauma.Items.Components
else
{
float volume = GetSoundVolume(itemSound);
if (volume <= 0.0f) { return; }
if (volume <= 0.0001f) { return; }
SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, item.CurrentHull);
}
}
@@ -465,14 +466,14 @@ namespace Barotrauma.Items.Components
}
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync = false)
protected void StartDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
{
if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
}
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync)
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync)
{
while (GameMain.Client != null &&
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))

View File

@@ -73,6 +73,11 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false)]
public bool KeepOpenWhenEquipped { get; set; }
[Serialize(false, false)]
public bool MovableFrame { get; set; }
public Vector2 DrawSize
{
//use the extents of the item as the draw size

View File

@@ -1,11 +1,10 @@
using Barotrauma.Networking;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
partial class LevelResource : ItemComponent, IServerSerializable
{
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
deattachTimer = msg.ReadSingle();
if (deattachTimer >= DeattachDuration)

View File

@@ -1,6 +1,5 @@
using Barotrauma.Lights;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -36,7 +35,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
IsOn = msg.ReadBoolean();
}

View File

@@ -38,6 +38,8 @@ namespace Barotrauma.Items.Components
private void ToggleCrewArea(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession.CrewManager;
if (crewManager == null) { return; }
if (storeOriginalState)
{
crewAreaOriginalState = crewManager.ToggleCrewAreaOpen;
@@ -48,6 +50,8 @@ namespace Barotrauma.Items.Components
private void ToggleChatBox(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession.CrewManager;
if (crewManager == null) { return; }
if (crewManager.IsSinglePlayer)
{
if (crewManager.ChatBox != null)

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
@@ -94,12 +93,12 @@ namespace Barotrauma.Items.Components
return true;
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(pendingState);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SetActive(msg.ReadBoolean());
progressTimer = msg.ReadSingle();

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -134,13 +133,13 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//targetForce can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger(-10, 10, (int)(targetForce / 10.0f));
msg.WriteRangedIntegerDeprecated(-10, 10, (int)(targetForce / 10.0f));
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{

View File

@@ -1,6 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -460,13 +459,13 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem);
msg.WriteRangedInteger(-1, fabricationRecipes.Count - 1, itemIndex);
msg.WriteRangedIntegerDeprecated(-1, fabricationRecipes.Count - 1, itemIndex);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
UInt16 userID = msg.ReadUInt16();
@@ -487,4 +486,4 @@ namespace Barotrauma.Items.Components
}
}
}
}
}

View File

@@ -17,6 +17,7 @@ namespace Barotrauma.Items.Components
private GUIScrollBar isActiveSlider;
private GUIScrollBar pumpSpeedSlider;
private GUITickBox powerIndicator;
private GUITickBox autoControlIndicator;
private List<Pair<Vector2, ParticleEmitter>> pumpOutEmitters = new List<Pair<Vector2, ParticleEmitter>>();
private List<Pair<Vector2, ParticleEmitter>> pumpInEmitters = new List<Pair<Vector2, ParticleEmitter>>();
@@ -71,12 +72,20 @@ namespace Barotrauma.Items.Components
return true;
};
var rightArea = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 1.0f), paddedFrame.RectTransform, Anchor.CenterRight)) { RelativeSpacing = 0.1f };
var rightArea = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.95f), paddedFrame.RectTransform, Anchor.CenterRight))
{
RelativeSpacing = 0.1f,
Stretch = true
};
powerIndicator = new GUITickBox(new RectTransform(new Point((int)(30 * GUI.Scale)), rightArea.RectTransform), TextManager.Get("PumpPowered"), style: "IndicatorLightGreen")
{
CanBeFocused = false
};
autoControlIndicator = new GUITickBox(new RectTransform(new Point((int)(30 * GUI.Scale)), rightArea.RectTransform), TextManager.Get("PumpAutoControl", fallBackTag: "ReactorAutoControl"), style: "IndicatorLightGreen")
{
CanBeFocused = false
};
var pumpSpeedText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), rightArea.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.0f) },
"", textAlignment: Alignment.BottomLeft, wrap: true);
@@ -86,12 +95,12 @@ namespace Barotrauma.Items.Components
var sliderArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), rightArea.RectTransform, Anchor.CenterLeft), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
RelativeSpacing = 0.01f
};
var outLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpOut"), textAlignment: Alignment.Center, wrap: true, font: GUI.SmallFont);
pumpSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.8f, 1.0f), sliderArea.RectTransform), barSize: 0.25f, style: "GUISlider")
var outLabel = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpOut"), textAlignment: Alignment.Center, wrap: false, font: GUI.SmallFont);
pumpSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 1.0f), sliderArea.RectTransform), barSize: 0.25f, style: "GUISlider")
{
Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
@@ -109,9 +118,11 @@ namespace Barotrauma.Items.Components
return true;
}
};
var inLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpIn"), textAlignment: Alignment.Center, wrap: true, font: GUI.SmallFont);
var inLabel = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpIn"), textAlignment: Alignment.Center, wrap: false, font: GUI.SmallFont);
rightArea.Recalculate();
sliderArea.Recalculate();
GUITextBlock.AutoScaleAndNormalize(outLabel, inLabel);
}
@@ -120,7 +131,6 @@ namespace Barotrauma.Items.Components
if (pumpSpeedSlider != null)
{
pumpSpeedSlider.BarScroll = (flowPercentage + 100.0f) / 200.0f;
}
}
@@ -151,6 +161,8 @@ namespace Barotrauma.Items.Components
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
powerIndicator.Selected = hasPower && IsActive;
autoControlIndicator.Selected = controlLockTimer > 0.0f && IsActive;
pumpSpeedSlider.Enabled = controlLockTimer <= 0.0f && IsActive;
if (!PlayerInput.LeftButtonHeld())
{
@@ -164,14 +176,14 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f));
msg.WriteRangedIntegerDeprecated(-10, 10, (int)(flowPercentage / 10.0f));
msg.Write(IsActive);
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -612,7 +611,7 @@ namespace Barotrauma.Items.Components
tempRangeIndicator.Remove();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(autoTemp);
msg.Write(shutDown);
@@ -622,7 +621,7 @@ namespace Barotrauma.Items.Components
correctionTimer = CorrectionDelay;
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{

View File

@@ -332,15 +332,18 @@ namespace Barotrauma.Items.Components
float dockingDist = Vector2.Distance(steering.ActiveDockingSource.Item.WorldPosition, steering.DockingTarget.Item.WorldPosition);
if (prevDockingDist > steering.DockingAssistThreshold && dockingDist <= steering.DockingAssistThreshold)
{
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, 0.25f));
zoomSlider.BarScroll = 0.25f;
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, zoomSlider.BarScroll));
}
else if (prevDockingDist > steering.DockingAssistThreshold * 0.75f && dockingDist <= steering.DockingAssistThreshold * 0.75f)
{
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, 0.5f));
zoomSlider.BarScroll = 0.5f;
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, zoomSlider.BarScroll));
}
else if (prevDockingDist > steering.DockingAssistThreshold * 0.5f && dockingDist <= steering.DockingAssistThreshold * 0.5f)
{
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, 0.25f));
zoomSlider.BarScroll = 0.25f;
zoom = Math.Max(zoom, MathHelper.Lerp(MinZoom, MaxZoom, zoomSlider.BarScroll));
}
prevDockingDist = Math.Min(dockingDist, prevDockingDist);
}
@@ -1181,7 +1184,7 @@ namespace Barotrauma.Items.Components
2, GUI.SmallFont);
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(currentMode == Mode.Active);
if (currentMode == Mode.Active)
@@ -1195,9 +1198,9 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
long msgStartPos = msg.Position;
int msgStartPos = msg.BitPosition;
bool isActive = msg.ReadBoolean();
float zoomT = 1.0f;
@@ -1215,8 +1218,8 @@ namespace Barotrauma.Items.Components
if (correctionTimer > 0.0f)
{
int msgLength = (int)(msg.Position - msgStartPos);
msg.Position = msgStartPos;
int msgLength = (int)(msg.BitPosition - msgStartPos);
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return;
}

View File

@@ -301,10 +301,9 @@ namespace Barotrauma.Items.Components
dockingContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GuiFrame.RectTransform, Anchor.BottomLeft)
{ MinSize = new Point(150, 0) }, style: null);
var paddedDockingContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), dockingContainer.RectTransform, Anchor.Center), style: null);
//TODO: add new texts for these ("Dock" & "Undock")
dockText = TextManager.Get("captain.dock");
undockText = TextManager.Get("captain.undock");
dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock");
undockText = TextManager.Get("label.navterminalundock", fallBackTag: "captain.undock");
dockingButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), paddedDockingContainer.RectTransform, Anchor.Center), dockText, style: "GUIButtonLarge")
{
OnClicked = (btn, userdata) =>
@@ -786,7 +785,7 @@ namespace Barotrauma.Items.Components
steeringIndicator?.Remove();
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(autoPilot);
msg.Write(dockingNetworkMessagePending);
@@ -813,9 +812,9 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
long msgStartPos = msg.Position;
int msgStartPos = msg.BitPosition;
bool autoPilot = msg.ReadBoolean();
Vector2 newSteeringInput = steeringInput;
@@ -831,8 +830,8 @@ namespace Barotrauma.Items.Components
if (maintainPos)
{
newPosToMaintain = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
msg.ReadSingle(),
msg.ReadSingle());
}
else
{
@@ -841,15 +840,15 @@ namespace Barotrauma.Items.Components
}
else
{
newSteeringInput = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newTargetVelocity = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newSteeringAdjustSpeed = msg.ReadFloat();
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
newTargetVelocity = new Vector2(msg.ReadSingle(), msg.ReadSingle());
newSteeringAdjustSpeed = msg.ReadSingle();
}
if (correctionTimer > 0.0f)
{
int msgLength = (int)(msg.Position - msgStartPos);
msg.Position = msgStartPos;
int msgLength = (int)(msg.BitPosition - msgStartPos);
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return;
}

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -128,12 +127,12 @@ namespace Barotrauma.Items.Components
}
public void ClientWrite(NetBuffer msg, object[] extraData)
public void ClientWrite(IWriteMessage msg, object[] extraData)
{
msg.WriteRangedInteger(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10));
msg.WriteRangedIntegerDeprecated(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10));
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{

View File

@@ -117,17 +117,20 @@ namespace Barotrauma.Items.Components
float progressBarState = targetItem.ConditionPercentage / 100.0f;
if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState))
{
Vector2 progressBarPos = targetItem.DrawPosition;
var progressBar = user.UpdateHUDProgressBar(
targetItem,
progressBarPos,
progressBarState,
Color.Red, Color.Green);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
var door = targetItem.GetComponent<Door>();
if (door == null || door.Stuck <= 0)
{
Vector2 progressBarPos = targetItem.DrawPosition;
var progressBar = user.UpdateHUDProgressBar(
targetItem,
progressBarPos,
progressBarState,
Color.Red, Color.Green);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
prevProgressBarState = progressBarState;
}
prevProgressBarState = progressBarState;
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
foreach (var emitter in ParticleEmitterHitItem)

View File

@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
@@ -15,6 +14,11 @@ namespace Barotrauma.Items.Components
get { return repairButton; }
}
private GUIButton repairButton;
public GUIButton SabotageButton
{
get { return sabotageButton; }
}
private GUIButton sabotageButton;
private GUIProgressBar progressBar;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
@@ -22,6 +26,9 @@ namespace Barotrauma.Items.Components
private List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private string repairButtonText, repairingText;
private string sabotageButtonText, sabotagingText;
private FixActions requestStartFixAction;
[Serialize("", false)]
public string Description
@@ -39,7 +46,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false;
return (item.Condition < ShowRepairUIThreshold || (currentFixer == character && !item.IsFullCondition));
return item.ConditionPercentage < ShowRepairUIThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition)));
}
partial void InitProjSpecific(XElement element)
@@ -61,24 +68,34 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < requiredSkills.Count; i++)
{
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int)requiredSkills[i].Level).ToString()),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) requiredSkills[i].Level).ToString()),
font: GUI.SmallFont)
{
UserData = requiredSkills[i]
};
}
progressBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
progressBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
color: Color.Green, barSize: 0.0f);
repairButtonText = TextManager.Get("RepairButton");
repairingText = TextManager.Get("Repairing");
repairButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.TopCenter),
repairButtonText)
repairButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.TopCenter), repairButtonText)
{
OnClicked = (btn, obj) =>
{
currentFixer = Character.Controlled;
requestStartFixAction = FixActions.Repair;
item.CreateClientEvent(this);
return true;
}
};
sabotageButtonText = TextManager.Get("SabotageButton");
sabotagingText = TextManager.Get("Sabotaging");
sabotageButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), sabotageButtonText)
{
OnClicked = (btn, obj) =>
{
requestStartFixAction = FixActions.Sabotage;
item.CreateClientEvent(this);
return true;
}
@@ -101,6 +118,21 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime)
{
if (!GameMain.IsMultiplayer)
{
switch (requestStartFixAction)
{
case FixActions.Repair:
case FixActions.Sabotage:
StartRepairing(Character.Controlled, requestStartFixAction);
requestStartFixAction = FixActions.None;
break;
default:
requestStartFixAction = FixActions.None;
break;
}
}
for (int i = 0; i < particleEmitters.Count; i++)
{
if (item.ConditionPercentage >= particleEmitterConditionRanges[i].X && item.ConditionPercentage <= particleEmitterConditionRanges[i].Y)
@@ -117,11 +149,17 @@ namespace Barotrauma.Items.Components
progressBar.BarSize = item.Condition / item.MaxCondition;
progressBar.Color = ToolBox.GradientLerp(progressBar.BarSize, Color.Red, Color.Orange, Color.Green);
repairButton.Enabled = currentFixer == null;
repairButton.Text = currentFixer == null ?
repairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && item.ConditionPercentage <= ShowRepairUIThreshold;
repairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ?
repairButtonText :
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
sabotageButton.Visible = character.IsTraitor;
sabotageButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Sabotage)) && character.IsTraitor && item.ConditionPercentage > MinSabotageCondition;
sabotageButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Sabotage || !character.IsTraitor) ?
sabotageButtonText :
sabotagingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{
@@ -139,14 +177,18 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
deteriorateAlwaysResetTimer = msg.ReadSingle();
DeteriorateAlways = msg.ReadBoolean();
CurrentFixer = msg.ReadBoolean() ? Character.Controlled : null;
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
msg.WriteRangedInteger((int)requestStartFixAction, 0, 2);
}
public void Draw(SpriteBatch spriteBatch, bool editing)

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -30,7 +29,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
return character == Character.Controlled && character == user;
return character == Character.Controlled && character == user && character.SelectedConstruction == item;
}
public override void AddToGUIUpdateList()
@@ -40,7 +39,7 @@ namespace Barotrauma.Items.Components
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (character != Character.Controlled || character != user) return;
if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; }
if (HighlightedWire != null)
{
@@ -64,13 +63,13 @@ namespace Barotrauma.Items.Components
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (GameMain.Client.MidRoundSyncing)
{
//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.Position;
long msgStartPos = msg.BitPosition;
foreach (Connection connection in Connections)
{
for (int i = 0; i < Connection.MaxLinked; i++)
@@ -83,8 +82,8 @@ namespace Barotrauma.Items.Components
{
msg.ReadUInt16();
}
int msgLength = (int)(msg.Position - msgStartPos);
msg.Position = msgStartPos;
int msgLength = (int)(msg.BitPosition - msgStartPos);
msg.BitPosition = (int)msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime, waitForMidRoundSync: true);
}
else
@@ -93,7 +92,7 @@ namespace Barotrauma.Items.Components
}
}
private void ApplyRemoteState(NetBuffer msg)
private void ApplyRemoteState(IReadMessage msg)
{
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
List<Wire> newWires = new List<Wire>();

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -132,7 +131,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything)
for (int i = 0; i < customInterfaceElementList.Count; i++)
@@ -148,7 +147,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
for (int i = 0; i < customInterfaceElementList.Count; i++)
{

View File

@@ -1,7 +1,6 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -140,6 +139,11 @@ namespace Barotrauma.Items.Components
};
}
public override void Move(Vector2 amount)
{
widgets.Clear();
}
partial void LaunchProjSpecific()
{
recoilTimer = Math.Max(Reload, 0.1f);
@@ -496,7 +500,7 @@ namespace Barotrauma.Items.Components
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, zoom);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
UInt16 projectileID = msg.ReadUInt16();
//projectile removed, do nothing

View File

@@ -1,7 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
@@ -20,22 +19,22 @@ namespace Barotrauma
public bool Disabled;
public GUIComponent.ComponentState State;
public Vector2 DrawOffset;
public Color Color;
public Color HighlightColor;
public float HighlightScaleUpAmount;
private CoroutineHandle highlightCoroutine;
public float HighlightTimer;
public Sprite SlotSprite;
public Keys QuickUseKey;
public int SubInventoryDir = -1;
public bool IsHighlighted
{
get
@@ -130,13 +129,15 @@ namespace Barotrauma
protected float prevUIScale = UIScale;
protected float prevHUDScale = GUI.Scale;
protected Point prevScreenResolution;
protected static Sprite slotSpriteSmall, slotSpriteHorizontal, slotSpriteVertical, slotSpriteRound;
public static Sprite EquipIndicator, EquipIndicatorHighlight;
public static Sprite DropIndicator, DropIndicatorHighlight;
public static Inventory DraggingInventory;
public Rectangle BackgroundFrame { get; protected set; }
private ushort[] receivedItemIDs;
private CoroutineHandle syncItemsCoroutine;
@@ -144,6 +145,13 @@ namespace Barotrauma
private bool isSubInventory;
private const float movableFrameRectHeight = 40f;
private Color movableFrameRectColor = new Color(60, 60, 60);
private Rectangle movableFrameRect;
private Point savedPosition, originalPos;
private bool canMove = false;
private bool positionUpdateQueued = false;
public class SlotReference
{
public readonly Inventory ParentInventory;
@@ -198,12 +206,12 @@ namespace Barotrauma
/// If set, the inventory is automatically positioned inside the rect
/// </summary>
public RectTransform RectTransform;
public static SlotReference SelectedSlot
{
get { return selectedSlot; }
}
public virtual void CreateSlots()
{
slots = new InventorySlot[capacity];
@@ -249,7 +257,7 @@ namespace Barotrauma
slotRect.Y = (int)(topLeft.Y + (rectSize.Y + spacing.Y) * ((int)Math.Floor((double)i / slotsPerRow)));
slots[i] = new InventorySlot(slotRect);
slots[i].InteractRect = new Rectangle(
(int)(slots[i].Rect.X - spacing.X / 2 - 1), (int)(slots[i].Rect.Y - spacing.Y / 2 - 1),
(int)(slots[i].Rect.X - spacing.X / 2 - 1), (int)(slots[i].Rect.Y - spacing.Y / 2 - 1),
(int)(slots[i].Rect.Width + spacing.X + 2), (int)(slots[i].Rect.Height + spacing.Y + 2));
if (slots[i].Rect.Width > slots[i].Rect.Height)
@@ -273,6 +281,22 @@ namespace Barotrauma
{
}
public bool Movable()
{
return movableFrameRect.Size != Point.Zero;
}
public bool IsInventoryHoverAvailable(Character owner, ItemContainer container)
{
if (container == null && this is ItemInventory)
{
container = (this as ItemInventory).Container;
}
if (container == null) return false;
return owner.SelectedCharacter != null || !container.KeepOpenWhenEquipped || (!(owner is Character)) || !owner.HasEquippedItem(container.Item);
}
protected virtual bool HideSlot(int i)
{
return slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null);
@@ -280,7 +304,7 @@ namespace Barotrauma
public virtual void Update(float deltaTime, Camera cam, bool subInventory = false)
{
if (slots == null || isSubInventory != subInventory ||
if (slots == null || isSubInventory != subInventory ||
(RectTransform != null && RectTransform.Rect != prevRect))
{
CreateSlots();
@@ -341,12 +365,12 @@ namespace Barotrauma
}
}
slot.State = GUIComponent.ComponentState.None;
if (mouseOn && (draggingItem != null || selectedSlot == null || selectedSlot.Slot == slot))
// &&
//(highlightedSubInventories.Count == 0 || highlightedSubInventories.Contains(this) || highlightedSubInventorySlot?.Slot == slot || highlightedSubInventory.Owner == item))
if (mouseOn && (draggingItem != null || selectedSlot == null || selectedSlot.Slot == slot) && DraggingInventory == null)
// &&
//(highlightedSubInventories.Count == 0 || highlightedSubInventories.Contains(this) || highlightedSubInventorySlot?.Slot == slot || highlightedSubInventory.Owner == item))
{
slot.State = GUIComponent.ComponentState.Hover;
@@ -369,7 +393,7 @@ namespace Barotrauma
{
doubleClickedItem = item;
}
}
}
}
}
@@ -384,7 +408,12 @@ namespace Barotrauma
return container.Inventory;
}
float openState;
protected virtual ItemInventory GetActiveEquippedSubInventory(int slotIndex)
{
return null;
}
public float OpenState;
public void UpdateSubInventory(float deltaTime, int slotIndex, Camera cam)
{
@@ -394,16 +423,45 @@ namespace Barotrauma
var container = item.GetComponent<ItemContainer>();
if (container == null || !container.DrawInventory) return;
var subInventory = container.Inventory;
var subInventory = container.Inventory;
if (subInventory.slots == null) subInventory.CreateSlots();
canMove = container.MovableFrame && !subInventory.IsInventoryHoverAvailable(Owner as Character, container) && subInventory.originalPos != Point.Zero;
if (canMove)
{
if (subInventory.movableFrameRect.Contains(PlayerInput.MousePosition) && PlayerInput.RightButtonClicked())
{
container.Inventory.savedPosition = container.Inventory.originalPos;
}
if (subInventory.movableFrameRect.Contains(PlayerInput.MousePosition) || (DraggingInventory != null && DraggingInventory == subInventory))
{
if (DraggingInventory == null)
{
if (PlayerInput.LeftButtonDown())
{
DraggingInventory = subInventory;
}
}
else if (PlayerInput.LeftButtonReleased())
{
DraggingInventory = null;
subInventory.savedPosition = PlayerInput.MousePosition.ToPoint();
}
else
{
subInventory.savedPosition = PlayerInput.MousePosition.ToPoint();
}
}
}
int itemCapacity = subInventory.Items.Length;
var slot = slots[slotIndex];
int dir = slot.SubInventoryDir;
if (itemCapacity == 1 && false)
{
Point slotSize = (slotSpriteRound.size * UIScale).ToPoint();
subInventory.slots[0].Rect =
subInventory.slots[0].Rect =
new Rectangle(slot.Rect.Center.X - slotSize.X / 2, dir > 0 ? slot.Rect.Bottom + 5 : slot.EquipButtonRect.Bottom + 5, slotSize.X, slotSize.Y);
subInventory.slots[0].InteractRect = subInventory.slots[0].Rect;
@@ -425,30 +483,43 @@ namespace Barotrauma
int width = (int)(subRect.Width * columns + spacing.X * (columns - 1));
int startX = slot.Rect.Center.X - (int)(width / 2.0f);
//prevent the inventory from extending outside the left side of the screen
startX = Math.Max(startX, 10);
//same for the right side of the screen
startX -= Math.Max((startX + width) - GameMain.GraphicsWidth, 0);
subRect.X = startX;
int startY = dir < 0 ?
slot.EquipButtonRect.Y - subRect.Height - (int)(35 * UIScale) :
slot.EquipButtonRect.Bottom + (int)(10 * UIScale);
subRect.Y = startY;
if (canMove)
{
startX += subInventory.savedPosition.X - subInventory.originalPos.X;
startY += subInventory.savedPosition.Y - subInventory.originalPos.Y;
}
float totalHeight = itemCapacity / columns * (subRect.Height + spacing.Y);
subInventory.openState = subInventory.HideTimer >= 0.5f ?
Math.Min(subInventory.openState + deltaTime * 5.0f, 1.0f) :
Math.Max(subInventory.openState - deltaTime * 3.0f, 0.0f);
int padding = (int)(20 * UIScale);
//prevent the inventory from extending outside the left side of the screen
startX = Math.Max(startX, padding);
//same for the right side of the screen
startX -= Math.Max(startX + width - GameMain.GraphicsWidth + padding, 0);
//prevent the inventory from extending outside the top of the screen
startY = Math.Max(startY, (int)totalHeight - padding / 2);
//same for the bottom side of the screen
startY -= Math.Max(startY - GameMain.GraphicsHeight + padding * 2 + (canMove ? (int)(movableFrameRectHeight * UIScale) : 0), 0);
subRect.X = startX;
subRect.Y = startY;
subInventory.OpenState = subInventory.HideTimer >= 0.5f ?
Math.Min(subInventory.OpenState + deltaTime * 5.0f, 1.0f) :
Math.Max(subInventory.OpenState - deltaTime * 3.0f, 0.0f);
for (int i = 0; i < itemCapacity; i++)
{
{
subInventory.slots[i].Rect = subRect;
subInventory.slots[i].Rect.Location += new Point(0, (int)totalHeight * -dir);
subInventory.slots[i].DrawOffset = Vector2.SmoothStep( new Vector2(0, -50 * dir), new Vector2(0, totalHeight * dir), subInventory.openState);
subInventory.slots[i].DrawOffset = Vector2.SmoothStep(new Vector2(0, -50 * dir), new Vector2(0, totalHeight * dir), subInventory.OpenState);
subInventory.slots[i].InteractRect = new Rectangle(
(int)(subInventory.slots[i].Rect.X - spacing.X / 2 - 1), (int)(subInventory.slots[i].Rect.Y - spacing.Y / 2 - 1),
(int)(subInventory.slots[i].Rect.Width + spacing.X + 2), (int)(subInventory.slots[i].Rect.Height + spacing.Y + 2));
@@ -463,14 +534,19 @@ namespace Barotrauma
{
subRect.X = (int)(subInventory.slots[i].Rect.Right + spacing.X);
}
}
}
if (canMove)
{
subInventory.movableFrameRect.X = subRect.X - (int)spacing.X;
subInventory.movableFrameRect.Y = subRect.Y + (int)(spacing.Y / 2f);
}
slots[slotIndex].State = GUIComponent.ComponentState.Hover;
}
subInventory.isSubInventory = true;
subInventory.isSubInventory = true;
subInventory.Update(deltaTime, cam, true);
}
public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false)
{
if (slots == null || isSubInventory != subInventory) return;
@@ -497,7 +573,7 @@ namespace Barotrauma
{
if (Character.Controlled == null) return false;
if (draggingItem != null) return true;
if (draggingItem != null || DraggingInventory != null) return true;
if (Character.Controlled.Inventory != null)
{
@@ -571,25 +647,32 @@ namespace Barotrauma
if (slotIndex < 0 || slotIndex >= Items.Length) return;
#endif
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
if (slots[slotIndex].SubInventoryDir > 0)
if (!canMove)
{
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(
new Point(0, slots[slotIndex].Rect.Bottom),
new Point(GameMain.GraphicsWidth, (int)Math.Max(GameMain.GraphicsHeight - slots[slotIndex].Rect.Bottom, 0)));
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
if (slots[slotIndex].SubInventoryDir > 0)
{
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(
new Point(0, slots[slotIndex].Rect.Bottom),
new Point(GameMain.GraphicsWidth, (int)Math.Max(GameMain.GraphicsHeight - slots[slotIndex].Rect.Bottom, 0)));
}
else
{
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(
new Point(0, 0),
new Point(GameMain.GraphicsWidth, slots[slotIndex].Rect.Y));
}
container.Inventory.Draw(spriteBatch, true);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
else
{
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(
new Point(0, 0),
new Point(GameMain.GraphicsWidth, slots[slotIndex].Rect.Y));
container.Inventory.Draw(spriteBatch, true);
}
container.Inventory.Draw(spriteBatch, true);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
container.InventoryBottomSprite?.Draw(spriteBatch,
new Vector2(slots[slotIndex].Rect.Center.X, slots[slotIndex].Rect.Y) + slots[slotIndex].DrawOffset,
@@ -597,10 +680,33 @@ namespace Barotrauma
container.InventoryTopSprite?.Draw(spriteBatch,
new Vector2(
slots[slotIndex].Rect.Center.X,
slots[slotIndex].Rect.Center.X,
container.Inventory.slots[container.Inventory.slots.Length - 1].Rect.Y) + container.Inventory.slots[container.Inventory.slots.Length - 1].DrawOffset,
0.0f, UIScale);
if (container.MovableFrame && !IsInventoryHoverAvailable(Owner as Character, container))
{
if (positionUpdateQueued) // Wait a frame before updating the positioning of the container after a resolution change to have everything working
{
container.Inventory.originalPos = container.Inventory.savedPosition = container.Inventory.movableFrameRect.Center;
positionUpdateQueued = false;
}
if (container.Inventory.movableFrameRect.Size == Point.Zero || GUI.HasSizeChanged(prevScreenResolution, prevUIScale, prevHUDScale))
{
// Reset position
container.Inventory.savedPosition = container.Inventory.originalPos;
prevScreenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = UIScale;
prevHUDScale = GUI.Scale;
int height = (int)(movableFrameRectHeight * UIScale);
container.Inventory.movableFrameRect = new Rectangle(container.Inventory.BackgroundFrame.X, container.Inventory.BackgroundFrame.Y - height, container.Inventory.BackgroundFrame.Width, height);
positionUpdateQueued = true;
}
GUI.DrawRectangle(spriteBatch, container.Inventory.movableFrameRect, movableFrameRectColor, true);
}
}
public static void UpdateDragging()
@@ -609,13 +715,13 @@ namespace Barotrauma
{
Character.Controlled.ClearInputs();
if (CharacterHealth.OpenHealthWindow != null &&
if (CharacterHealth.OpenHealthWindow != null &&
CharacterHealth.OpenHealthWindow.OnItemDropped(draggingItem, false))
{
draggingItem = null;
return;
}
if (selectedSlot == null)
{
if (DraggingItemToWorld &&
@@ -672,12 +778,23 @@ namespace Barotrauma
selectedSlot = null;
}
}
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{
Rectangle hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
hoverArea = Rectangle.Union(hoverArea, subSlot.Slot.EquipButtonRect);
Rectangle hoverArea;
if (!subSlot.Inventory.Movable())
{
hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
hoverArea = Rectangle.Union(hoverArea, subSlot.Slot.EquipButtonRect);
}
else
{
hoverArea = subSlot.Inventory.BackgroundFrame;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
hoverArea = Rectangle.Union(hoverArea, subSlot.Inventory.movableFrameRect);
}
if (subSlot.Inventory?.slots != null)
{
foreach (InventorySlot slot in subSlot.Inventory.slots)
@@ -697,12 +814,17 @@ namespace Barotrauma
hoverArea.Height -= over;
}
}
hoverArea.Inflate(10, 10);
float inflateAmount = 10 * UIScale;
hoverArea.Inflate(inflateAmount, inflateAmount);
return hoverArea;
}
public static void DrawFront(SpriteBatch spriteBatch)
{
if (GUI.PauseMenuOpen || GUI.SettingsMenuOpen) return;
foreach (var slot in highlightedSubInventorySlots)
{
int slotIndex = Array.IndexOf(slot.ParentInventory.slots, slot.Slot);
@@ -710,7 +832,7 @@ namespace Barotrauma
{
slot.ParentInventory.DrawSubInventory(spriteBatch, slotIndex);
}
}
}
if (draggingItem != null)
{
@@ -727,7 +849,7 @@ namespace Barotrauma
if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null)
{
var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0];
string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") :
string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") :
Character.Controlled.FocusedItem != null ?
TextManager.GetWithVariable("PutItemIn", "[itemname]", Character.Controlled.FocusedItem.Name, true) :
TextManager.Get("DropItem");
@@ -807,7 +929,7 @@ namespace Barotrauma
{
Rectangle rect = slot.Rect;
rect.Location += slot.DrawOffset.ToPoint();
if (slot.HighlightColor.A > 0)
{
float inflateAmount = (slot.HighlightColor.A / 255.0f) * slot.HighlightScaleUpAmount * 0.5f;
@@ -860,7 +982,7 @@ namespace Barotrauma
Rectangle containedIndicatorArea = new Rectangle(rect.X,
dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight);
containedIndicatorArea.Inflate(-4, 0);
if (itemContainer.ContainedStateIndicator?.Texture == null)
{
containedIndicatorArea.Inflate(0, -2);
@@ -882,7 +1004,7 @@ namespace Barotrauma
}
indicatorSprite.Draw(spriteBatch, containedIndicatorArea.Center.ToVector2(),
(inventory != null && inventory.Locked) ? Color.DarkGray * 0.5f : Color.DarkGray * 0.9f,
(inventory != null && inventory.Locked) ? Color.DarkGray * 0.5f : Color.DarkGray * 0.9f,
origin: indicatorSprite.size / 2,
rotate: 0.0f,
scale: indicatorScale);
@@ -944,19 +1066,19 @@ namespace Barotrauma
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
}
if (inventory != null &&
if (inventory != null &&
!inventory.Locked &&
Character.Controlled?.Inventory == inventory &&
Character.Controlled?.Inventory == inventory &&
slot.QuickUseKey != Keys.None)
{
GUI.DrawString(spriteBatch, rect.Location.ToVector2(),
slot.QuickUseKey.ToString().Substring(1, 1),
item == null || !drawItem ? Color.Gray : Color.White,
GUI.DrawString(spriteBatch, rect.Location.ToVector2(),
slot.QuickUseKey.ToString().Substring(1, 1),
item == null || !drawItem ? Color.Gray : Color.White,
Color.Black * 0.8f);
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
receivedItemIDs = new ushort[capacity];
@@ -1036,7 +1158,7 @@ namespace Barotrauma
receivedItemIDs = null;
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
SharedWrite(msg, extraData);
syncItemsDelay = 1.0f;

View File

@@ -1,7 +1,6 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
@@ -432,23 +431,6 @@ namespace Barotrauma
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
bool ConditionalMatches(PropertyConditional conditional)
{
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
if (!conditional.Matches(this)) { return false; }
}
else
{
foreach (ItemComponent component in components)
{
if (component.Name != conditional.TargetItemComponentName) { continue; }
if (!conditional.Matches(component)) { return false; }
}
}
return true;
}
}
}
}
@@ -656,10 +638,11 @@ namespace Barotrauma
List<Rectangle> disallowedAreas = new List<Rectangle>();
if (GameMain.GameSession?.CrewManager != null && Screen.Selected == GameMain.GameScreen)
{
int disallowedPadding = (int)(50 * GUI.Scale);
disallowedAreas.Add(GameMain.GameSession.CrewManager.GetCharacterListArea());
disallowedAreas.Add(new Rectangle(
HUDLayoutSettings.ChatBoxArea.X - 50, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + 50, HUDLayoutSettings.ChatBoxArea.Height));
HUDLayoutSettings.ChatBoxArea.X - disallowedPadding, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + disallowedPadding, HUDLayoutSettings.ChatBoxArea.Height));
}
GUI.PreventElementOverlap(elementsToMove, disallowedAreas,
@@ -798,7 +781,7 @@ namespace Barotrauma
{
if (ic is Repairable repairable)
{
if (Condition < repairable.ShowRepairUIThreshold)
if (ConditionPercentage < repairable.ShowRepairUIThreshold)
{
color = Color.Cyan;
}
@@ -858,7 +841,7 @@ namespace Barotrauma
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (type == ServerNetObject.ENTITY_POSITION)
{
@@ -868,7 +851,7 @@ namespace Barotrauma
NetEntityEvent.Type eventType =
(NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
switch (eventType)
{
case NetEntityEvent.Type.ComponentState:
@@ -939,7 +922,7 @@ namespace Barotrauma
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
if (extraData == null || extraData.Length == 0 || !(extraData[0] is NetEntityEvent.Type))
{
@@ -947,17 +930,17 @@ namespace Barotrauma
}
NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0];
msg.WriteRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)eventType);
msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)eventType);
switch (eventType)
{
case NetEntityEvent.Type.ComponentState:
int componentIndex = (int)extraData[1];
msg.WriteRangedInteger(0, components.Count - 1, componentIndex);
msg.WriteRangedIntegerDeprecated(0, components.Count - 1, componentIndex);
(components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.InventoryState:
int containerIndex = (int)extraData[1];
msg.WriteRangedInteger(0, components.Count - 1, containerIndex);
msg.WriteRangedIntegerDeprecated(0, components.Count - 1, containerIndex);
(components[containerIndex] as ItemContainer).Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Treatment:
@@ -1010,7 +993,7 @@ namespace Barotrauma
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
}
public void ClientReadPosition(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientReadPosition(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (body == null)
{
@@ -1078,7 +1061,7 @@ namespace Barotrauma
GameMain.Client.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.ComponentState, index });
}
public static Item ReadSpawnData(NetBuffer msg, bool spawn = true)
public static Item ReadSpawnData(IReadMessage msg, bool spawn = true)
{
string itemName = msg.ReadString();
string itemIdentifier = msg.ReadString();
@@ -1140,23 +1123,32 @@ namespace Barotrauma
}
Inventory inventory = null;
var inventoryOwner = FindEntityByID(inventoryId);
if (inventoryOwner != null)
if (inventoryId > 0)
{
if (inventoryOwner is Character)
var inventoryOwner = FindEntityByID(inventoryId);
if (inventoryOwner is Character character)
{
inventory = (inventoryOwner as Character).Inventory;
inventory = character.Inventory;
}
else if (inventoryOwner is Item)
else if (inventoryOwner is Item parentItem)
{
if ((inventoryOwner as Item).components[itemContainerIndex] is ItemContainer container)
if (itemContainerIndex < 0 || itemContainerIndex >= parentItem.components.Count)
{
string errorMsg = "Failed to spawn item \"" + (itemIdentifier ?? "null") +
"\" in the inventory of \"" + parentItem.prefab.Identifier + "\" (component index out of range). Index: " + itemContainerIndex + ", components: " + parentItem.components.Count + ".";
GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:ContainerIndexOutOfRange" + (itemName ?? "null") + (itemIdentifier ?? "null"),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
DebugConsole.ThrowError(errorMsg);
}
else if (parentItem.components[itemContainerIndex] is ItemContainer container)
{
inventory = container.Inventory;
}
}
}
}
var item = new Item(itemPrefab, pos, sub)
{
ID = itemId
@@ -1197,4 +1189,4 @@ namespace Barotrauma
}
}
}
}
}

View File

@@ -1,7 +1,6 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
@@ -528,14 +527,14 @@ namespace Barotrauma
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
msg.Write(FireSources.Count > 0);
if (FireSources.Count > 0)
{
msg.WriteRangedInteger(0, 16, Math.Min(FireSources.Count, 16));
msg.WriteRangedIntegerDeprecated(0, 16, Math.Min(FireSources.Count, 16));
for (int i = 0; i < Math.Min(FireSources.Count, 16); i++)
{
var fireSource = FireSources[i];
@@ -550,7 +549,7 @@ namespace Barotrauma
}
}
public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
{
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Dynamics;
@@ -95,7 +94,7 @@ namespace Barotrauma
renderer.DrawBackground(spriteBatch, cam, levelObjectManager, backgroundCreatureManager);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
foreach (LevelWall levelWall in extraWalls)
{

View File

@@ -1,12 +1,12 @@
using Barotrauma.Lights;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.SpriteDeformations;
using Lidgren.Network;
using System.Linq;
using FarseerPhysics.Dynamics;
@@ -267,7 +267,7 @@ namespace Barotrauma
}
}
public void ClientRead(NetBuffer msg)
public void ClientRead(IReadMessage msg)
{
for (int i = 0; i < Triggers.Count; i++)
{

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -165,7 +164,7 @@ namespace Barotrauma
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int objIndex = msg.ReadRangedInteger(0, objects.Count);
objects[objIndex].ClientRead(msg);

View File

@@ -1,10 +1,10 @@
using Lidgren.Network;
using Barotrauma.Networking;
namespace Barotrauma
{
partial class LevelTrigger
{
public void ClientRead(NetBuffer msg)
public void ClientRead(IReadMessage msg)
{
if (ForceFluctuationStrength > 0.0f)
{

View File

@@ -382,7 +382,7 @@ namespace Barotrauma
}
else
{
selectedList = newSelection;
selectedList = new List<MapEntity>(newSelection);
//selectedList.Clear();
//newSelection.ForEach(e => AddSelection(e));
foreach (var entity in newSelection)

View File

@@ -4,7 +4,6 @@ using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -346,12 +345,23 @@ namespace Barotrauma
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
for (int i = 0; i < Sections.Length; i++)
byte sectionCount = msg.ReadByte();
if (sectionCount != Sections.Length)
{
string errorMsg = $"Error while reading a network event for the structure \"{Name}\". Section count does not match (server: {sectionCount} client: {Sections.Length})";
DebugConsole.NewMessage(errorMsg, Color.Red);
GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
for (int i = 0; i < sectionCount; i++)
{
float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * Health;
SetDamage(i, damage);
if (i < Sections.Length)
{
SetDamage(i, damage);
}
}
}
}

View File

@@ -2,7 +2,6 @@
using Barotrauma.RuinGeneration;
using Barotrauma.Sounds;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -514,7 +513,7 @@ namespace Barotrauma
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
var posInfo = PhysicsBody.ClientRead(type, msg, sendingTime, parentDebugName: Name);
msg.ReadPadBits();

View File

@@ -1,5 +1,4 @@
using Microsoft.Xna.Framework;
using Lidgren.Network;
using System;
using System.Collections.Generic;
@@ -17,7 +16,7 @@ namespace Barotrauma.Networking
}
}
partial class BanList
public partial class BanList
{
private GUIComponent banFrame;
@@ -129,7 +128,7 @@ namespace Barotrauma.Networking
return true;
}
public void ClientAdminRead(NetBuffer incMsg)
public void ClientAdminRead(IReadMessage incMsg)
{
bool hasPermission = incMsg.ReadBoolean();
if (!hasPermission)
@@ -142,8 +141,8 @@ namespace Barotrauma.Networking
incMsg.ReadPadBits();
bannedPlayers.Clear();
Int32 bannedPlayerCount = incMsg.ReadVariableInt32();
for (int i = 0; i < bannedPlayerCount; i++)
UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();
for (int i = 0; i < (int)bannedPlayerCount; i++)
{
string name = incMsg.ReadString();
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
@@ -172,7 +171,7 @@ namespace Barotrauma.Networking
}
}
public void ClientAdminWrite(NetBuffer outMsg)
public void ClientAdminWrite(IWriteMessage outMsg)
{
outMsg.Write((UInt16)localRemovedBans.Count);
foreach (UInt16 uniqueId in localRemovedBans)

View File

@@ -1,12 +1,11 @@
using Lidgren.Network;
using System;
using System;
using System.Linq;
namespace Barotrauma.Networking
{
partial class ChatMessage
{
public virtual void ClientWrite(NetOutgoingMessage msg)
public virtual void ClientWrite(IWriteMessage msg)
{
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
@@ -14,7 +13,7 @@ namespace Barotrauma.Networking
msg.Write(Text);
}
public static void ClientRead(NetIncomingMessage msg)
public static void ClientRead(IReadMessage msg)
{
UInt16 ID = msg.ReadUInt16();
ChatMessageType type = (ChatMessageType)msg.ReadByte();
@@ -37,7 +36,11 @@ namespace Barotrauma.Networking
}
}
if (type == ChatMessageType.Order)
if (type == ChatMessageType.ServerMessageBox)
{
txt = TextManager.GetServerMessage(txt);
}
else if (type == ChatMessageType.Order)
{
int orderIndex = msg.ReadByte();
UInt16 targetCharacterID = msg.ReadUInt16();
@@ -63,17 +66,20 @@ namespace Barotrauma.Networking
}
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
if (order.TargetAllCharacters)
if (GameMain.Client.GameStarted)
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
order.Prefab.FadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
orderOption, senderCharacter);
if (order.TargetAllCharacters)
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
order.Prefab.FadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
orderOption, senderCharacter);
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
@@ -90,7 +96,12 @@ namespace Barotrauma.Networking
switch (type)
{
case ChatMessageType.MessageBox:
new GUIMessageBox("", txt);
case ChatMessageType.ServerMessageBox:
//only show the message box if the text differs from the text in the currently visible box
if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
{
new GUIMessageBox("", txt);
}
break;
case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);

View File

@@ -9,6 +9,7 @@ namespace Barotrauma.Networking
struct TempClient
{
public string Name;
public UInt64 SteamID;
public byte ID;
public UInt16 CharacterID;
public bool Muted;
@@ -76,7 +77,7 @@ namespace Barotrauma.Networking
VoipQueue = null; VoipSound = null;
if (ID == GameMain.Client.ID) return;
VoipQueue = new VoipQueue(ID, false, true);
GameMain.Client.VoipClient.RegisterQueue(VoipQueue);
GameMain.Client?.VoipClient?.RegisterQueue(VoipQueue);
VoipSound = null;
}

View File

@@ -4,7 +4,7 @@ namespace Barotrauma
{
partial class EntitySpawner : Entity, IServerSerializable
{
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer message, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
{
bool remove = message.ReadBoolean();

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
@@ -25,13 +24,13 @@ namespace Barotrauma.Networking
private set;
}
public ulong FileSize
public int FileSize
{
get;
set;
}
public ulong Received
public int Received
{
get;
private set;
@@ -72,15 +71,15 @@ namespace Barotrauma.Networking
private set;
}
public NetConnection Connection
public NetworkConnection Connection
{
get;
private set;
}
public int SequenceChannel;
public int ID;
public FileTransferIn(NetConnection connection, string filePath, FileTransferType fileType)
public FileTransferIn(NetworkConnection connection, string filePath, FileTransferType fileType)
{
FilePath = filePath;
FileName = Path.GetFileName(FilePath);
@@ -105,17 +104,16 @@ namespace Barotrauma.Networking
TimeStarted = Environment.TickCount;
}
public void ReadBytes(NetIncomingMessage inc)
public void ReadBytes(IReadMessage inc, int bytesToRead)
{
int bytesToRead = inc.LengthBytes - inc.PositionInBytes;
if (Received + (ulong)(bytesToRead) > FileSize)
if (Received + bytesToRead > FileSize)
{
//strip out excess bytes
bytesToRead -= (int)((Received + (ulong)bytesToRead) - FileSize);
bytesToRead -= Received + bytesToRead - FileSize;
}
byte[] all = inc.ReadBytes(bytesToRead);
Received += (ulong)all.Length;
Received += all.Length;
WriteStream.Write(all, 0, all.Length);
int passed = Environment.TickCount - TimeStarted;
@@ -162,6 +160,7 @@ namespace Barotrauma.Networking
public TransferInDelegate OnTransferFailed;
private List<FileTransferIn> activeTransfers;
private List<Pair<int, double>> finishedTransfers;
private Dictionary<FileTransferType, string> downloadFolders = new Dictionary<FileTransferType, string>()
{
@@ -177,9 +176,10 @@ namespace Barotrauma.Networking
public FileReceiver()
{
activeTransfers = new List<FileTransferIn>();
finishedTransfers = new List<Pair<int, double>>();
}
public void ReadMessage(NetIncomingMessage inc)
public void ReadMessage(IReadMessage inc)
{
System.Diagnostics.Debug.Assert(!activeTransfers.Any(t =>
t.Status == FileTransferStatus.Error ||
@@ -187,26 +187,38 @@ namespace Barotrauma.Networking
t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed");
byte transferMessageType = inc.ReadByte();
switch (transferMessageType)
{
case (byte)FileTransferMessageType.Initiate:
{
var existingTransfer = activeTransfers.Find(t => t.SequenceChannel == inc.SequenceChannel);
if (existingTransfer != null)
{
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
DebugConsole.ThrowError("File transfer error: file transfer initiated on a sequence channel that's already in use");
return;
}
byte transferId = inc.ReadByte();
var existingTransfer = activeTransfers.Find(t => t.ID == transferId);
finishedTransfers.RemoveAll(t => t.First == transferId);
byte fileType = inc.ReadByte();
ushort chunkLen = inc.ReadUInt16();
ulong fileSize = inc.ReadUInt64();
//ushort chunkLen = inc.ReadUInt16();
int fileSize = inc.ReadInt32();
string fileName = inc.ReadString();
if (existingTransfer != null)
{
if (fileType != (byte)existingTransfer.FileType ||
fileSize != existingTransfer.FileSize ||
fileName != existingTransfer.FileName)
{
GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer error: file transfer initiated with an ID that's already in use");
}
else //resend acknowledgement packet
{
GameMain.Client.UpdateFileTransfer(transferId, 0);
}
return;
}
if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg))
{
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")");
return;
}
@@ -216,7 +228,7 @@ namespace Barotrauma.Networking
DebugConsole.Log("Received file transfer initiation message: ");
DebugConsole.Log(" File: " + fileName);
DebugConsole.Log(" Size: " + fileSize);
DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel);
DebugConsole.Log(" ID: " + transferId);
}
string downloadFolder = downloadFolders[(FileTransferType)fileType];
@@ -233,9 +245,9 @@ namespace Barotrauma.Networking
}
}
FileTransferIn newTransfer = new FileTransferIn(inc.SenderConnection, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType)
FileTransferIn newTransfer = new FileTransferIn(inc.Sender, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType)
{
SequenceChannel = inc.SequenceChannel,
ID = transferId,
Status = FileTransferStatus.Receiving,
FileSize = fileSize
};
@@ -257,7 +269,7 @@ namespace Barotrauma.Networking
else
{
DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red);
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
GameMain.Client.CancelFileTransfer(transferId);
newTransfer.Status = FileTransferStatus.Error;
OnTransferFailed(newTransfer);
return;
@@ -265,10 +277,13 @@ namespace Barotrauma.Networking
}
}
activeTransfers.Add(newTransfer);
GameMain.Client.UpdateFileTransfer(transferId, 0); //send acknowledgement packet
}
break;
case (byte)FileTransferMessageType.TransferOnSameMachine:
{
byte transferId = inc.ReadByte();
byte fileType = inc.ReadByte();
string filePath = inc.ReadString();
@@ -276,19 +291,19 @@ namespace Barotrauma.Networking
{
DebugConsole.Log("Received file transfer message on the same machine: ");
DebugConsole.Log(" File: " + filePath);
DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel);
DebugConsole.Log(" ID: " + transferId);
}
if (!File.Exists(filePath))
{
DebugConsole.ThrowError("File transfer on the same machine failed, file \"" + filePath + "\" not found.");
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
GameMain.Client.CancelFileTransfer(transferId);
return;
}
FileTransferIn directTransfer = new FileTransferIn(inc.SenderConnection, filePath, (FileTransferType)fileType)
FileTransferIn directTransfer = new FileTransferIn(inc.Sender, filePath, (FileTransferType)fileType)
{
SequenceChannel = inc.SequenceChannel,
ID = transferId,
Status = FileTransferStatus.Finished,
FileSize = 0
};
@@ -296,79 +311,104 @@ namespace Barotrauma.Networking
}
break;
case (byte)FileTransferMessageType.Data:
var activeTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == inc.SequenceChannel);
if (activeTransfer == null)
{
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message");
return;
}
byte transferId = inc.ReadByte();
//allow one extra byte at the end for the 0 that identifies non-compressed messages
if (activeTransfer.Received + (ulong)(inc.LengthBytes - inc.PositionInBytes) > activeTransfer.FileSize + 1)
{
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received +
", msg received: " + (inc.LengthBytes - inc.PositionInBytes) +
", msg length: " + inc.LengthBytes +
", msg read: " + inc.PositionInBytes +
", filesize: " + activeTransfer.FileSize);
activeTransfer.Status = FileTransferStatus.Error;
StopTransfer(activeTransfer);
return;
}
try
{
activeTransfer.ReadBytes(inc);
}
catch (Exception e)
{
GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
DebugConsole.ThrowError("File transfer error: " + e.Message);
activeTransfer.Status = FileTransferStatus.Error;
StopTransfer(activeTransfer, true);
return;
}
if (activeTransfer.Status == FileTransferStatus.Finished)
{
activeTransfer.Dispose();
if (ValidateReceivedData(activeTransfer, out string errorMessage))
var activeTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
if (activeTransfer == null)
{
StopTransfer(activeTransfer);
OnFinished(activeTransfer);
//it's possible for the server to send some extra data
//before it acknowledges that the download is finished,
//so let's suppress the error message in that case
finishedTransfers.RemoveAll(t => t.Second + 5.0 < Timing.TotalTime);
if (!finishedTransfers.Any(t => t.First == transferId))
{
GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message");
}
return;
}
else
{
new GUIMessageBox("File transfer aborted", errorMessage);
int offset = inc.ReadInt32();
if (offset != activeTransfer.Received)
{
if (offset < activeTransfer.Received)
{
GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received);
}
return;
}
int bytesToRead = inc.ReadUInt16();
if (activeTransfer.Received + bytesToRead > activeTransfer.FileSize)
{
GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received +
", msg received: " + (inc.LengthBytes - inc.BytePosition) +
", msg length: " + inc.LengthBytes +
", msg read: " + inc.BytePosition +
", filesize: " + activeTransfer.FileSize);
activeTransfer.Status = FileTransferStatus.Error;
StopTransfer(activeTransfer);
return;
}
try
{
activeTransfer.ReadBytes(inc, bytesToRead);
}
catch (Exception e)
{
GameMain.Client.CancelFileTransfer(transferId);
DebugConsole.ThrowError("File transfer error: " + e.Message);
activeTransfer.Status = FileTransferStatus.Error;
StopTransfer(activeTransfer, true);
return;
}
if (activeTransfer.Status == FileTransferStatus.Finished)
{
GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, true);
activeTransfer.Dispose();
if (ValidateReceivedData(activeTransfer, out string errorMessage))
{
finishedTransfers.Add(new Pair<int, double>(transferId, Timing.TotalTime));
StopTransfer(activeTransfer);
OnFinished(activeTransfer);
}
else
{
new GUIMessageBox("File transfer aborted", errorMessage);
activeTransfer.Status = FileTransferStatus.Error;
StopTransfer(activeTransfer, true);
}
}
}
break;
case (byte)FileTransferMessageType.Cancel:
byte sequenceChannel = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == sequenceChannel);
if (matchingTransfer != null)
{
new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\".");
StopTransfer(matchingTransfer);
byte transferId = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
if (matchingTransfer != null)
{
new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\".");
StopTransfer(matchingTransfer);
}
break;
}
break;
}
}
private bool ValidateInitialData(byte type, string fileName, ulong fileSize, out string errorMessage)
private bool ValidateInitialData(byte type, string fileName, int fileSize, out string errorMessage)
{
errorMessage = "";
if (fileSize > MaxFileSize)
{
errorMessage = "File too large (" + MathUtils.GetBytesReadable((long)fileSize) + ")";
errorMessage = "File too large (" + MathUtils.GetBytesReadable(fileSize) + ")";
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
using System;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
@@ -10,6 +10,10 @@ namespace Barotrauma
public void CreateSettingsFrame(GUIComponent parent)
{
CreateLabeledSlider(parent, 0.0f, 40.0f, 1.0f, "KickBanThreshold");
if (TextManager.ContainsTag("Karma.KicksBeforeBan"))
{
CreateLabeledNumberInput(parent, 0, 10, "KicksBeforeBan");
}
CreateLabeledSlider(parent, 0.0f, 50.0f, 1.0f, "HerpesThreshold");
CreateLabeledSlider(parent, 0.0f, 0.5f, 0.01f, "KarmaDecay");
@@ -37,7 +41,7 @@ namespace Barotrauma
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "DamageFriendlyKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 100.0f, 1.0f, "ReactorMeltdownKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, "ReactorOverheatKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 20.0f, 1f, "AllowedWireDisconnectionsPerMinute");
CreateLabeledNumberInput(parent, 0, 20, "AllowedWireDisconnectionsPerMinute");
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, "WireDisconnectionKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, "SpamFilterKarmaDecrease");
}
@@ -74,5 +78,29 @@ namespace Barotrauma
GameMain.NetworkMember.ServerSettings.AssignGUIComponent(propertyName, slider);
slider.OnMoved(slider, slider.BarScroll);
}
private void CreateLabeledNumberInput(GUIComponent parent, int min, int max, string propertyName)
{
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f,
ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip")
};
string labelText = TextManager.Get("Karma." + propertyName);
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.8f), container.RectTransform),
labelText, font: GUI.SmallFont)
{
ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip")
};
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.3f, 0.8f), container.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = min,
MaxValueFloat = max
};
GameMain.NetworkMember.ServerSettings.AssignGUIComponent(propertyName, numInput);
}
}
}

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
namespace Barotrauma.Networking
@@ -75,7 +74,7 @@ namespace Barotrauma.Networking
events.Add(newEvent);
}
public void Write(NetOutgoingMessage msg, NetConnection serverConnection)
public void Write(IWriteMessage msg, NetworkConnection serverConnection)
{
if (events.Count == 0 || serverConnection == null) return;
@@ -97,7 +96,7 @@ namespace Barotrauma.Networking
//find the first event that hasn't been sent in roundtriptime or at all
eventLastSent.TryGetValue(events[i].ID, out float lastSent);
if (lastSent > NetTime.Now - serverConnection.AverageRoundtripTime)
if (lastSent > Lidgren.Network.NetTime.Now - 50) //TODO: reimplement serverConnection.AverageRoundtripTime
{
continue;
}
@@ -109,7 +108,7 @@ namespace Barotrauma.Networking
foreach (NetEntityEvent entityEvent in eventsToSync)
{
eventLastSent[entityEvent.ID] = (float)NetTime.Now;
eventLastSent[entityEvent.ID] = (float)Lidgren.Network.NetTime.Now;
}
msg.Write((byte)ClientNetObject.ENTITY_STATE);
@@ -121,7 +120,7 @@ namespace Barotrauma.Networking
/// <summary>
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
/// </summary>
public bool Read(ServerNetObject type, NetIncomingMessage msg, float sendingTime, List<IServerSerializable> entities)
public bool Read(ServerNetObject type, IReadMessage msg, float sendingTime, List<IServerSerializable> entities)
{
UInt16 unreceivedEntityEventCount = 0;
@@ -176,7 +175,7 @@ namespace Barotrauma.Networking
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
entities.Add(entity);
//skip the event if we've already received it or if the entity isn't found
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
{
@@ -200,11 +199,11 @@ namespace Barotrauma.Networking
return false;
}
msg.Position += msgLength * 8;
msg.BitPosition += msgLength * 8;
}
else
{
long msgPosition = msg.Position;
long msgPosition = msg.BitPosition;
if (GameSettings.VerboseLogging)
{
DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
@@ -231,7 +230,7 @@ namespace Barotrauma.Networking
}
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
msg.Position = msgPosition + msgLength * 8;
msg.BitPosition = (int)(msgPosition + msgLength * 8);
}
}
msg.ReadPadBits();
@@ -239,7 +238,7 @@ namespace Barotrauma.Networking
return true;
}
protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
protected override void WriteEvent(IWriteMessage buffer, NetEntityEvent entityEvent, Client recipient = null)
{
var clientEvent = entityEvent as ClientEntityEvent;
if (clientEvent == null) return;
@@ -248,7 +247,7 @@ namespace Barotrauma.Networking
clientEvent.Sent = true;
}
protected void ReadEvent(NetIncomingMessage buffer, IServerSerializable entity, float sendingTime)
protected void ReadEvent(IReadMessage buffer, IServerSerializable entity, float sendingTime)
{
entity.ClientRead(ServerNetObject.ENTITY_EVENT, buffer, sendingTime);
}

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
namespace Barotrauma.Networking
{
@@ -15,7 +14,7 @@ namespace Barotrauma.Networking
serializable = entity;
}
public void Write(NetBuffer msg)
public void Write(IWriteMessage msg)
{
msg.Write(CharacterStateID);
serializable.ClientWrite(msg, Data);

View File

@@ -5,7 +5,7 @@ namespace Barotrauma.Networking
{
partial class OrderChatMessage : ChatMessage
{
public override void ClientWrite(NetOutgoingMessage msg)
public override void ClientWrite(IWriteMessage msg)
{
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
abstract class ClientPeer
{
public delegate void MessageCallback(IReadMessage message);
public delegate void DisconnectCallback(string msg);
public delegate void PasswordCallback(int salt, int retries);
public delegate void InitializationCompleteCallback();
public MessageCallback OnMessageReceived;
public DisconnectCallback OnDisconnect;
public PasswordCallback OnRequestPassword;
public InitializationCompleteCallback OnInitializationComplete;
public string Name;
public string Version { get; protected set; }
public NetworkConnection ServerConnection { get; protected set; }
public abstract void Start(object endPoint, int ownerKey);
public abstract void Close(string msg = null);
public abstract void Update(float deltaTime);
public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod);
public abstract void SendPassword(string password);
}
}

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Lidgren.Network;
using Facepunch.Steamworks;
using Barotrauma.Steam;
using System.Linq;
namespace Barotrauma.Networking
{
class LidgrenClientPeer : ClientPeer
{
private bool isActive;
private NetClient netClient;
private NetPeerConfiguration netPeerConfiguration;
private ConnectionInitialization initializationStep;
private int ownerKey;
private int passwordSalt;
private Auth.Ticket steamAuthTicket;
List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenClientPeer(string name)
{
ServerConnection = null;
Name = name;
netClient = null;
isActive = false;
}
public override void Start(object endPoint, int ownerKey)
{
if (isActive) { return; }
this.ownerKey = ownerKey;
netPeerConfiguration = new NetPeerConfiguration("barotrauma");
netPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt
| NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error);
netClient = new NetClient(netPeerConfiguration);
if (SteamManager.IsInitialized)
{
steamAuthTicket = SteamManager.GetAuthSessionTicket();
//TODO: wait for GetAuthSessionTicketResponse_t
if (steamAuthTicket == null)
{
throw new Exception("GetAuthSessionTicket returned null");
}
}
incomingLidgrenMessages = new List<NetIncomingMessage>();
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
if (!(endPoint is IPEndPoint ipEndPoint))
{
throw new InvalidCastException("endPoint is not IPEndPoint");
}
if (ServerConnection != null)
{
throw new InvalidOperationException("ServerConnection is not null");
}
netClient.Start();
ServerConnection = new LidgrenConnection("Server", netClient.Connect(ipEndPoint), 0);
ServerConnection.Status = NetworkConnectionStatus.Connected;
isActive = true;
}
public override void Update(float deltaTime)
{
if (!isActive) { return; }
netClient.ReadMessages(incomingLidgrenMessages);
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
{
if (inc.SenderConnection != (ServerConnection as LidgrenConnection).NetConnection) { continue; }
switch (inc.MessageType)
{
case NetIncomingMessageType.Data:
HandleDataMessage(inc);
break;
case NetIncomingMessageType.StatusChanged:
HandleStatusChanged(inc);
break;
}
}
incomingLidgrenMessages.Clear();
}
private void HandleDataMessage(NetIncomingMessage inc)
{
if (!isActive) { return; }
byte incByte = inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
//DebugConsole.NewMessage(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte);
if (isConnectionInitializationStep && initializationStep != ConnectionInitialization.Success)
{
ReadConnectionInitializationStep(inc);
}
else
{
if (initializationStep != ConnectionInitialization.Success)
{
OnInitializationComplete?.Invoke();
initializationStep = ConnectionInitialization.Success;
}
UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Data, isCompressed, inc.PositionInBytes, length, ServerConnection);
OnMessageReceived?.Invoke(msg);
}
}
private void HandleStatusChanged(NetIncomingMessage inc)
{
if (!isActive) { return; }
NetConnectionStatus status = (NetConnectionStatus)inc.ReadByte();
switch (status)
{
case NetConnectionStatus.Disconnected:
string disconnectMsg = inc.ReadString();
Close(disconnectMsg);
break;
}
}
private void ReadConnectionInitializationStep(NetIncomingMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
//DebugConsole.NewMessage(step + " " + initializationStep);
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
NetOutgoingMessage outMsg = netClient.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
outMsg.Write(Name);
outMsg.Write(ownerKey);
outMsg.Write(SteamManager.GetSteamID());
if (steamAuthTicket == null)
{
outMsg.Write((UInt16)0);
}
else
{
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
}
outMsg.Write(GameMain.Version.ToString());
IEnumerable<ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
outMsg.WriteVariableInt32(mpContentPackages.Count());
foreach (ContentPackage contentPackage in mpContentPackages)
{
outMsg.Write(contentPackage.Name);
outMsg.Write(contentPackage.MD5hash.Hash);
}
netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
break;
case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
if (initializationStep != ConnectionInitialization.Password) { return; }
bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
int retries = 0;
if (incomingSalt)
{
passwordSalt = inc.ReadInt32();
}
else
{
retries = inc.ReadInt32();
}
OnRequestPassword?.Invoke(passwordSalt, retries);
break;
}
}
public override void SendPassword(string password)
{
if (!isActive) { return; }
if (initializationStep != ConnectionInitialization.Password) { return; }
NetOutgoingMessage outMsg = netClient.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.Password);
byte[] saltedPw = ServerSettings.SaltPassword(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)), passwordSalt);
outMsg.Write((byte)saltedPw.Length);
outMsg.Write(saltedPw, 0, saltedPw.Length);
netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
}
public override void Close(string msg = null)
{
if (!isActive) { return; }
isActive = false;
netClient.Shutdown(msg ?? TextManager.Get("Disconnecting"));
netClient = null;
steamAuthTicket?.Cancel(); steamAuthTicket = null;
OnDisconnect?.Invoke(msg);
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
{
if (!isActive) { return; }
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch (deliveryMethod)
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
lidgrenMsg.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
lidgrenMsg.Write((UInt16)length);
lidgrenMsg.Write(msgData, 0, length);
netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod);
}
}
}

View File

@@ -0,0 +1,338 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Barotrauma.Steam;
using Facepunch.Steamworks;
using System.Threading;
namespace Barotrauma.Networking
{
class SteamP2PClientPeer : ClientPeer
{
private bool isActive;
private UInt64 hostSteamId;
private ConnectionInitialization initializationStep;
private int passwordSalt;
private Auth.Ticket steamAuthTicket;
private double timeout;
private double heartbeatTimer;
private List<IReadMessage> incomingInitializationMessages;
private List<IReadMessage> incomingDataMessages;
public SteamP2PClientPeer(string name)
{
ServerConnection = null;
Name = name;
isActive = false;
}
public override void Start(object endPoint, int ownerKey)
{
steamAuthTicket = SteamManager.GetAuthSessionTicket();
//TODO: wait for GetAuthSessionTicketResponse_t
if (steamAuthTicket == null)
{
throw new Exception("GetAuthSessionTicket returned null");
}
if (!(endPoint is UInt64 steamIdEndpoint))
{
throw new InvalidCastException("endPoint is not UInt64");
}
hostSteamId = steamIdEndpoint;
Steam.SteamManager.Instance.Networking.OnIncomingConnection = OnIncomingConnection;
Steam.SteamManager.Instance.Networking.OnP2PData = OnP2PData;
Steam.SteamManager.Instance.Networking.SetListenChannel(0, true);
ServerConnection = new SteamP2PConnection("Server", hostSteamId);
incomingInitializationMessages = new List<IReadMessage>();
incomingDataMessages = new List<IReadMessage>();
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ConnectionStarted);
SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Reliable);
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
timeout = 20.0;
heartbeatTimer = 1.0;
isActive = true;
}
private bool OnIncomingConnection(UInt64 steamId)
{
if (!isActive) { return false; }
return steamId == hostSteamId;
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength, int channel)
{
if (!isActive) { return; }
if (steamId != hostSteamId) { return; }
timeout = 20.0;
byte incByte = data[0];
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!isServerMessage) { return; }
if (isConnectionInitializationStep)
{
ulong low = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8);
ulong high = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8+32);
ulong lobbyId = low + (high << 32);
Steam.SteamManager.JoinLobby(lobbyId, false);
IReadMessage inc = new ReadOnlyMessage(data, false, 1+8, dataLength - 9, ServerConnection);
if (initializationStep != ConnectionInitialization.Success)
{
incomingInitializationMessages.Add(inc);
}
}
else if (isHeartbeatMessage)
{
return; //TODO: implement heartbeats
}
else if (isDisconnectMessage)
{
IReadMessage inc = new ReadOnlyMessage(data, false, 1, dataLength - 1, ServerConnection);
string msg = inc.ReadString();
Close(msg);
}
else
{
UInt16 length = data[1];
length |= (UInt16)(((UInt32)data[2]) << 8);
IReadMessage inc = new ReadOnlyMessage(data, isCompressed, 3, length, ServerConnection);
incomingDataMessages.Add(inc);
}
}
public override void Update(float deltaTime)
{
if (!isActive) { return; }
timeout -= deltaTime;
heartbeatTimer -= deltaTime;
if (heartbeatTimer < 0.0)
{
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Unreliable);
outMsg.Write((byte)PacketHeader.IsHeartbeatMessage);
SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Unreliable);
heartbeatTimer = 5.0;
}
if (timeout < 0.0)
{
Close("Timed out");
return;
}
if (initializationStep != ConnectionInitialization.Success)
{
if (incomingDataMessages.Count > 0)
{
OnInitializationComplete?.Invoke();
initializationStep = ConnectionInitialization.Success;
}
else
{
foreach (IReadMessage inc in incomingInitializationMessages)
{
ReadConnectionInitializationStep(inc);
}
}
}
if (initializationStep == ConnectionInitialization.Success)
{
foreach (IReadMessage inc in incomingDataMessages)
{
OnMessageReceived?.Invoke(inc);
}
}
incomingInitializationMessages.Clear();
incomingDataMessages.Clear();
}
private void ReadConnectionInitializationStep(IReadMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
//DebugConsole.NewMessage(step + " " + initializationStep);
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
outMsg.Write(Name);
outMsg.Write(SteamManager.GetSteamID());
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
outMsg.Write(GameMain.Version.ToString());
IEnumerable<ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count());
foreach (ContentPackage contentPackage in mpContentPackages)
{
outMsg.Write(contentPackage.Name);
outMsg.Write(contentPackage.MD5hash.Hash);
}
heartbeatTimer = 5.0;
SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Reliable);
break;
case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
if (initializationStep != ConnectionInitialization.Password) { return; }
bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
int retries = 0;
if (incomingSalt)
{
passwordSalt = inc.ReadInt32();
}
else
{
retries = inc.ReadInt32();
}
OnRequestPassword?.Invoke(passwordSalt, retries);
break;
}
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
{
if (!isActive) { return; }
byte[] buf = new byte[msg.LengthBytes + 4];
buf[0] = (byte)deliveryMethod;
byte[] bufAux = new byte[msg.LengthBytes];
bool isCompressed; int length;
msg.PrepareForSending(ref bufAux, out isCompressed, out length);
buf[1] = (byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None);
buf[2] = (byte)(length & 0xff);
buf[3] = (byte)((length >> 8) & 0xff);
Array.Copy(bufAux, 0, buf, 4, length);
Facepunch.Steamworks.Networking.SendType sendType;
switch (deliveryMethod)
{
case DeliveryMethod.Reliable:
case DeliveryMethod.ReliableOrdered:
//the documentation seems to suggest that the Reliable send type
//enforces packet order (TODO: verify)
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
break;
default:
sendType = Facepunch.Steamworks.Networking.SendType.Unreliable;
break;
}
if (length + 8 >= MsgConstants.MTU)
{
DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + length.ToString() + " bytes)");
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
}
heartbeatTimer = 5.0;
bool successSend = SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, buf, length + 4, sendType);
if (!successSend)
{
if (sendType != Facepunch.Steamworks.Networking.SendType.Reliable)
{
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + length.ToString() + " bytes)");
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
successSend = Steam.SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, buf, length + 4, sendType);
}
if (!successSend)
{
DebugConsole.ThrowError("Failed to send message to remote peer! (" + length.ToString() + " bytes)");
}
}
}
public override void SendPassword(string password)
{
if (!isActive) { return; }
if (initializationStep != ConnectionInitialization.Password) { return; }
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.Password);
byte[] saltedPw = ServerSettings.SaltPassword(Lidgren.Network.NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)), passwordSalt);
outMsg.Write((byte)saltedPw.Length);
outMsg.Write(saltedPw, 0, saltedPw.Length);
heartbeatTimer = 5.0;
SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Reliable);
}
public override void Close(string msg = null)
{
if (!isActive) { return; }
SteamManager.LeaveLobby();
isActive = false;
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsDisconnectMessage);
outMsg.Write(msg ?? "Disconnected");
SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Reliable);
Thread.Sleep(100);
Steam.SteamManager.Instance.Networking.OnIncomingConnection = null;
Steam.SteamManager.Instance.Networking.OnP2PData = null;
Steam.SteamManager.Instance.Networking.SetListenChannel(0, false);
Steam.SteamManager.Instance.Networking.CloseSession(hostSteamId);
steamAuthTicket?.Cancel(); steamAuthTicket = null;
hostSteamId = 0;
OnDisconnect?.Invoke(msg);
}
}
}

View File

@@ -0,0 +1,472 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Lidgren.Network;
using Facepunch.Steamworks;
using Barotrauma.Steam;
using System.Linq;
using System.Threading;
namespace Barotrauma.Networking
{
class SteamP2POwnerPeer : ClientPeer
{
private bool isActive;
private NetClient netClient;
private NetPeerConfiguration netPeerConfiguration;
private ConnectionInitialization initializationStep;
private UInt64 selfSteamID;
List<NetIncomingMessage> incomingLidgrenMessages;
class RemotePeer
{
public UInt64 SteamID;
public double? DisconnectTime;
public bool Authenticating;
public bool Authenticated;
public List<Pair<NetDeliveryMethod, NetOutgoingMessage>> UnauthedMessages;
public RemotePeer(UInt64 steamId)
{
SteamID = steamId;
DisconnectTime = null;
Authenticating = false;
Authenticated = false;
UnauthedMessages = new List<Pair<NetDeliveryMethod, NetOutgoingMessage>>();
}
}
List<RemotePeer> remotePeers;
public SteamP2POwnerPeer(string name)
{
ServerConnection = null;
Name = name;
netClient = null;
isActive = false;
selfSteamID = Steam.SteamManager.GetSteamID();
}
public override void Start(object endPoint, int ownerKey)
{
if (isActive) { return; }
netPeerConfiguration = new NetPeerConfiguration("barotrauma");
netPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt
| NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error);
netClient = new NetClient(netPeerConfiguration);
incomingLidgrenMessages = new List<NetIncomingMessage>();
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Loopback, Steam.SteamManager.STEAMP2P_OWNER_PORT);
netClient.Start();
ServerConnection = new LidgrenConnection("Server", netClient.Connect(ipEndPoint), 0);
ServerConnection.Status = NetworkConnectionStatus.Connected;
remotePeers = new List<RemotePeer>();
Steam.SteamManager.Instance.Networking.OnIncomingConnection = OnIncomingConnection;
Steam.SteamManager.Instance.Networking.OnP2PData = OnP2PData;
Steam.SteamManager.Instance.Networking.SetListenChannel(0, true);
Steam.SteamManager.Instance.Auth.OnAuthChange = OnAuthChange;
isActive = true;
}
private void OnAuthChange(ulong steamID, ulong ownerID, ClientAuthStatus status)
{
RemotePeer remotePeer = remotePeers.Find(p => p.SteamID == steamID);
DebugConsole.NewMessage(steamID + " validation: " + status + ", " + (remotePeer != null));
if (remotePeer == null) { return; }
if (remotePeer.Authenticated)
{
if (status != ClientAuthStatus.OK)
{
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
}
return;
}
if (status == ClientAuthStatus.OK)
{
remotePeer.Authenticated = true;
remotePeer.Authenticating = false;
foreach (var msg in remotePeer.UnauthedMessages)
{
netClient.SendMessage(msg.Second, msg.First);
}
remotePeer.UnauthedMessages.Clear();
}
else
{
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication failed: " + status.ToString());
return;
}
}
private bool OnIncomingConnection(UInt64 steamId)
{
if (!isActive) { return false; }
if (!remotePeers.Any(p => p.SteamID == steamId))
{
remotePeers.Add(new RemotePeer(steamId));
}
return true; //accept all connections, the server will figure things out later
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength, int channel)
{
if (!isActive) { return; }
RemotePeer remotePeer = remotePeers.Find(p => p.SteamID == steamId);
if (remotePeer == null || remotePeer.DisconnectTime != null)
{
return;
}
NetOutgoingMessage outMsg = netClient.CreateMessage();
outMsg.Write(steamId);
outMsg.Write(data, 1, dataLength - 1);
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch ((DeliveryMethod)data[0])
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
byte incByte = data[1];
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (!remotePeer.Authenticated)
{
if (!remotePeer.Authenticating)
{
if (isConnectionInitializationStep)
{
remotePeer.DisconnectTime = null;
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
{
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
ClientStartAuthSessionResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != ClientStartAuthSessionResult.OK)
{
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
}
}
}
}
if (remotePeer.Authenticating)
{
remotePeer.UnauthedMessages.Add(new Pair<NetDeliveryMethod, NetOutgoingMessage>(lidgrenDeliveryMethod, outMsg));
}
else
{
netClient.SendMessage(outMsg, lidgrenDeliveryMethod);
}
}
public override void Update(float deltaTime)
{
if (!isActive) { return; }
for (int i = remotePeers.Count - 1; i >= 0; i--)
{
if (remotePeers[i].DisconnectTime != null && remotePeers[i].DisconnectTime < Timing.TotalTime)
{
ClosePeerSession(remotePeers[i]);
}
}
netClient.ReadMessages(incomingLidgrenMessages);
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
{
if (inc.SenderConnection != (ServerConnection as LidgrenConnection).NetConnection) { continue; }
switch (inc.MessageType)
{
case NetIncomingMessageType.Data:
HandleDataMessage(inc);
break;
case NetIncomingMessageType.StatusChanged:
HandleStatusChanged(inc);
break;
}
}
incomingLidgrenMessages.Clear();
}
private void HandleDataMessage(NetIncomingMessage inc)
{
if (!isActive) { return; }
UInt64 recipientSteamId = inc.ReadUInt64();
int p2pDataStart = inc.PositionInBytes;
byte incByte = inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
if (recipientSteamId != selfSteamID)
{
if (!isServerMessage)
{
DebugConsole.ThrowError("Received non-server message meant for remote peer");
return;
}
RemotePeer peer = remotePeers.Find(p => p.SteamID == recipientSteamId);
if (peer == null) { return; }
if (isDisconnectMessage)
{
DisconnectPeer(peer, inc.ReadString());
return;
}
Facepunch.Steamworks.Networking.SendType sendType;
switch (inc.DeliveryMethod)
{
case NetDeliveryMethod.ReliableUnordered:
case NetDeliveryMethod.ReliableSequenced:
case NetDeliveryMethod.ReliableOrdered:
//the documentation seems to suggest that the Reliable send type
//enforces packet order (TODO: verify)
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
break;
default:
sendType = Facepunch.Steamworks.Networking.SendType.Unreliable;
break;
}
byte[] p2pData;
if (isConnectionInitializationStep)
{
p2pData = new byte[inc.LengthBytes - p2pDataStart + 8];
p2pData[0] = inc.Data[p2pDataStart];
Lidgren.Network.NetBitWriter.WriteUInt64(Steam.SteamManager.Instance.Lobby.CurrentLobby, 64, p2pData, 8);
Array.Copy(inc.Data, p2pDataStart+1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1);
}
else
{
p2pData = new byte[inc.LengthBytes - p2pDataStart];
Array.Copy(inc.Data, p2pDataStart, p2pData, 0, p2pData.Length);
}
if (p2pData.Length + 4 >= MsgConstants.MTU)
{
DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
}
bool successSend = Steam.SteamManager.Instance.Networking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, sendType);
if (!successSend)
{
if (sendType != Facepunch.Steamworks.Networking.SendType.Reliable)
{
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
sendType = Facepunch.Steamworks.Networking.SendType.Reliable;
successSend = Steam.SteamManager.Instance.Networking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, sendType);
}
if (!successSend)
{
DebugConsole.ThrowError("Failed to send message to remote peer! (" + p2pData.Length.ToString() + " bytes)");
}
}
}
else
{
if (isDisconnectMessage)
{
DebugConsole.ThrowError("Received disconnect message from owned server");
return;
}
if (!isServerMessage)
{
DebugConsole.ThrowError("Received non-server message from owned server");
return;
}
if (isHeartbeatMessage)
{
return; //timeout is handled by Lidgren, ignore this message
}
if (isConnectionInitializationStep)
{
NetOutgoingMessage outMsg = netClient.CreateMessage();
outMsg.Write(selfSteamID);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
outMsg.Write(Name);
netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
return;
}
else
{
if (initializationStep != ConnectionInitialization.Success)
{
OnInitializationComplete?.Invoke();
initializationStep = ConnectionInitialization.Success;
}
UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Data, isCompressed, inc.PositionInBytes, length, ServerConnection);
OnMessageReceived?.Invoke(msg);
return;
}
}
}
private void DisconnectPeer(RemotePeer peer, string msg)
{
if (!string.IsNullOrWhiteSpace(msg))
{
if (peer.DisconnectTime == null)
{
peer.DisconnectTime = Timing.TotalTime + 1.0;
}
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)(PacketHeader.IsServerMessage | PacketHeader.IsDisconnectMessage));
outMsg.Write(msg);
Steam.SteamManager.Instance.Networking.SendP2PPacket(peer.SteamID, outMsg.Buffer, outMsg.LengthBytes,
Facepunch.Steamworks.Networking.SendType.Reliable);
}
else
{
ClosePeerSession(peer);
}
}
private void ClosePeerSession(RemotePeer peer)
{
Steam.SteamManager.Instance.Networking.CloseSession(peer.SteamID);
remotePeers.Remove(peer);
}
private void HandleStatusChanged(NetIncomingMessage inc)
{
if (!isActive) { return; }
NetConnectionStatus status = (NetConnectionStatus)inc.ReadByte();
switch (status)
{
case NetConnectionStatus.Disconnected:
string disconnectMsg = inc.ReadString();
Close(disconnectMsg);
break;
}
}
public override void SendPassword(string password)
{
return; //owner doesn't send passwords
}
public override void Close(string msg = null)
{
if (!isActive) { return; }
isActive = false;
for (int i=remotePeers.Count-1;i>=0;i--)
{
DisconnectPeer(remotePeers[i], msg ?? DisconnectReason.ServerShutdown.ToString());
}
Thread.Sleep(100);
for (int i = remotePeers.Count - 1; i >= 0; i--)
{
ClosePeerSession(remotePeers[i]);
}
netClient.Shutdown(msg ?? TextManager.Get("Disconnecting"));
netClient = null;
OnDisconnect?.Invoke(msg);
Steam.SteamManager.Instance.Networking.OnIncomingConnection = null;
Steam.SteamManager.Instance.Networking.OnP2PData = null;
Steam.SteamManager.Instance.Networking.SetListenChannel(0, false);
Steam.SteamManager.Instance.Auth.OnAuthChange = null;
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
{
if (!isActive) { return; }
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch (deliveryMethod)
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
lidgrenMsg.Write(selfSteamID);
lidgrenMsg.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
lidgrenMsg.Write((UInt16)length);
lidgrenMsg.Write(msgData, 0, length);
netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod);
}
}
}

View File

@@ -21,8 +21,7 @@ namespace Barotrauma.Networking
GameMain.Client.AddChatMessage("ServerMessage.ShuttleLeaving", ChatMessageType.Server);
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);

View File

@@ -1,5 +1,6 @@
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,6 +10,9 @@ namespace Barotrauma.Networking
{
public string IP;
public string Port;
public UInt64 LobbyID;
public string ServerName;
public string ServerMessage;
public bool GameStarted;

View File

@@ -5,7 +5,7 @@ using System.Linq;
namespace Barotrauma.Networking
{
partial class ServerLog
public partial class ServerLog
{
public GUIButton LogFrame;
private GUIListBox listBox;

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -31,6 +30,10 @@ namespace Barotrauma.Networking
else if (GUIComponent is GUIScrollBar scrollBar) return scrollBar.BarScrollValue;
else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) return radioButtonGroup.Selected;
else if (GUIComponent is GUIDropDown dropdown) return dropdown.SelectedData;
else if (GUIComponent is GUINumberInput numInput)
{
if (numInput.InputType == GUINumberInput.NumberType.Int) { return numInput.IntValue; } else { return numInput.FloatValue; }
}
return null;
}
set
@@ -38,9 +41,30 @@ namespace Barotrauma.Networking
if (GUIComponent == null) return;
else if (GUIComponent is GUITickBox tickBox) tickBox.Selected = (bool)value;
else if (GUIComponent is GUITextBox textBox) textBox.Text = (string)value;
else if (GUIComponent is GUIScrollBar scrollBar) scrollBar.BarScrollValue = (float)value;
else if (GUIComponent is GUIScrollBar scrollBar)
{
if (value.GetType() == typeof(int))
{
scrollBar.BarScrollValue = (int)value;
}
else
{
scrollBar.BarScrollValue = (float)value;
}
}
else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) radioButtonGroup.Selected = (Enum)value;
else if (GUIComponent is GUIDropDown dropdown) dropdown.SelectItem(value);
else if (GUIComponent is GUINumberInput numInput)
{
if (numInput.InputType == GUINumberInput.NumberType.Int)
{
numInput.IntValue = (int)value;
}
else
{
numInput.FloatValue = (float)value;
}
}
}
}
@@ -68,7 +92,7 @@ namespace Barotrauma.Networking
}
}
public void ClientAdminRead(NetBuffer incMsg)
public void ClientAdminRead(IReadMessage incMsg)
{
int count = incMsg.ReadUInt16();
for (int i = 0; i < count; i++)
@@ -91,7 +115,7 @@ namespace Barotrauma.Networking
else
{
UInt32 size = incMsg.ReadVariableUInt32();
incMsg.Position += 8 * size;
incMsg.BitPosition += (int)(8 * size);
}
}
@@ -100,10 +124,14 @@ namespace Barotrauma.Networking
Whitelist.ClientAdminRead(incMsg);
}
public void ClientRead(NetBuffer incMsg)
public void ClientRead(IReadMessage incMsg)
{
ServerName = incMsg.ReadString();
ServerMessageText = incMsg.ReadString();
MaxPlayers = incMsg.ReadByte();
HasPassword = incMsg.ReadBoolean();
isPublic = incMsg.ReadBoolean();
incMsg.ReadPadBits();
TickRate = incMsg.ReadRangedInteger(1, 60);
GameMain.NetworkMember.TickRate = TickRate;
@@ -123,7 +151,7 @@ namespace Barotrauma.Networking
{
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
NetOutgoingMessage outMsg = GameMain.NetworkMember.NetPeer.CreateMessage();
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)ClientPacketHeader.SERVER_SETTINGS);
@@ -191,7 +219,7 @@ namespace Barotrauma.Networking
outMsg.Write(GameMain.NetLobbyScreen.SeedBox.Text);
}
(GameMain.NetworkMember.NetPeer as NetClient).SendMessage(outMsg, NetDeliveryMethod.ReliableOrdered);
GameMain.Client.ClientPeer.Send(outMsg, DeliveryMethod.Reliable);
}
//GUI stuff
@@ -508,7 +536,7 @@ namespace Barotrauma.Networking
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
/*var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
var traitorRatioSlider = slider;
@@ -553,7 +581,7 @@ namespace Barotrauma.Networking
GetPropertyData("TraitorRatio").AssignGUIComponent(traitorRatioSlider);
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
traitorRatioBox.OnSelected(traitorRatioBox);
traitorRatioBox.OnSelected(traitorRatioBox);*/
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
{
@@ -829,4 +857,4 @@ namespace Barotrauma.Networking
return false;
}
}
}
}

View File

@@ -6,70 +6,269 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.Steam
{
partial class SteamManager
{
private static List<string> initializationErrors = new List<string>();
public static IEnumerable<string> InitializationErrors
{
get { return initializationErrors; }
}
public Facepunch.Steamworks.Networking Networking => client?.Networking;
public Facepunch.Steamworks.User User => client?.User;
public Facepunch.Steamworks.Friends Friends => client?.Friends;
public Facepunch.Steamworks.Overlay Overlay => client?.Overlay;
public Facepunch.Steamworks.Auth Auth => client?.Auth;
public Facepunch.Steamworks.Lobby Lobby => client?.Lobby;
public Facepunch.Steamworks.LobbyList LobbyList => client?.LobbyList;
private SteamManager()
{
client = null;
isInitialized = InitializeClient();
}
private bool InitializeClient()
{
if (client != null) { return true; }
bool clientInitialized = false;
try
{
client = new Facepunch.Steamworks.Client(AppID);
isInitialized = client.IsSubscribed && client.IsValid;
clientInitialized = client.IsSubscribed && client.IsValid;
if (isInitialized)
if (clientInitialized)
{
DebugConsole.Log("Logged in as " + client.Username + " (SteamID " + client.SteamId + ")");
DebugConsole.NewMessage("Logged in as " + client.Username + " (SteamID " + SteamIDUInt64ToString(client.SteamId) + ")");
}
}
catch (DllNotFoundException)
{
isInitialized = false;
clientInitialized = false;
initializationErrors.Add("SteamDllNotFound");
}
catch (Exception)
{
isInitialized = false;
clientInitialized = false;
initializationErrors.Add("SteamClientInitFailed");
}
if (!isInitialized)
if (!clientInitialized)
{
try
{
Facepunch.Steamworks.Client.Instance.Dispose();
}
catch (Exception e)
{
if (GameSettings.VerboseLogging) DebugConsole.ThrowError("Disposing Steam client failed.", e);
}
client = null;
}
return clientInitialized;
}
private enum LobbyState
{
NotConnected,
Creating,
Owner,
Joining,
Joined
}
private static UInt64 lobbyID = 0;
private static LobbyState lobbyState = LobbyState.NotConnected;
private static string lobbyIP = "";
private static Thread lobbyIPRetrievalThread;
private static void RetrieveLobbyIP()
{
//TODO: set up our own server for IP retrieval?
Server tempServer = null;
try
{
var serverInit = new ServerInit("Barotrauma", "Barotrauma IP Retrieval")
{
GamePort = (ushort)27015,
QueryPort = (ushort)27016
};
tempServer = new Server(AppID, serverInit, false);
if (!tempServer.IsValid)
{
tempServer.Dispose();
tempServer = null;
DebugConsole.ThrowError("Failed to retrieve public IP: Initializing Steam server failed.");
return;
}
tempServer.LogOnAnonymous();
lobbyIP = "";
string error = "Timed out.";
for (int i = 0; i < 30*60; i++)
{
if (instance.client.Lobby.CurrentLobby == 0)
{
error = "";
break;
}
tempServer.Update();
tempServer.ForceHeartbeat();
if (tempServer.PublicIp != null)
{
if (instance.client.Lobby.CurrentLobby != 0)
{
lobbyIP = tempServer.PublicIp.ToString();
DebugConsole.NewMessage("Successfully retrieved public IP: " + lobbyIP, Microsoft.Xna.Framework.Color.Lime);
instance.client.Lobby.CurrentLobbyData.SetData("hostipaddress", lobbyIP);
}
else
{
error = "";
lobbyIP = "";
}
break;
}
Thread.Sleep(16);
}
tempServer.Dispose();
tempServer = null;
if (string.IsNullOrWhiteSpace(lobbyIP) && !string.IsNullOrWhiteSpace(error))
{
DebugConsole.ThrowError("Failed to retrieve public IP: "+error);
}
}
catch
{
tempServer?.Dispose();
tempServer = null;
}
}
public static ulong GetSteamID()
public static void CreateLobby(ServerSettings serverSettings)
{
if (instance == null || !instance.isInitialized)
instance.client.Lobby.OnLobbyJoined = null;
instance.client.Lobby.OnLobbyCreated = (success) =>
{
return 0;
if (!success)
{
DebugConsole.ThrowError("Failed to create Steam lobby!");
lobbyState = LobbyState.NotConnected;
return;
}
DebugConsole.NewMessage("Lobby created!", Microsoft.Xna.Framework.Color.Lime);
lobbyIPRetrievalThread?.Abort();
lobbyIPRetrievalThread?.Join();
lobbyIPRetrievalThread = null;
lobbyIPRetrievalThread = new Thread(new ThreadStart(RetrieveLobbyIP));
lobbyIPRetrievalThread.IsBackground = true;
lobbyIPRetrievalThread.Start();
lobbyState = LobbyState.Owner;
lobbyID = instance.client.Lobby.CurrentLobby;
UpdateLobby(serverSettings);
};
if (lobbyState != LobbyState.NotConnected) { return; }
lobbyState = LobbyState.Creating;
instance.client.Lobby.Create(serverSettings.isPublic ? Lobby.Type.Public : Lobby.Type.FriendsOnly, serverSettings.MaxPlayers+10);
instance.client.Lobby.Joinable = true;
}
public static void UpdateLobby(ServerSettings serverSettings)
{
if (GameMain.Client == null)
{
LeaveLobby();
}
return instance.client.SteamId;
if (lobbyState == LobbyState.NotConnected)
{
CreateLobby(serverSettings);
}
if (lobbyState != LobbyState.Owner)
{
return;
}
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
instance.client.Lobby.Name = serverSettings.ServerName;
instance.client.Lobby.Owner = Steam.SteamManager.GetSteamID();
instance.client.Lobby.MaxMembers = serverSettings.MaxPlayers+10;
instance.client.Lobby.CurrentLobbyData.SetData("playercount", (GameMain.Client?.ConnectedClients?.Count??0).ToString());
instance.client.Lobby.CurrentLobbyData.SetData("maxplayernum", serverSettings.MaxPlayers.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("hostipaddress", lobbyIP);
//instance.client.Lobby.CurrentLobbyData.SetData("connectsteamid", Steam.SteamManager.GetSteamID().ToString());
instance.client.Lobby.CurrentLobbyData.SetData("haspassword", serverSettings.HasPassword.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("message", serverSettings.ServerMessageText);
instance.client.Lobby.CurrentLobbyData.SetData("version", GameMain.Version.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
instance.client.Lobby.CurrentLobbyData.SetData("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
instance.client.Lobby.CurrentLobbyData.SetData("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
instance.client.Lobby.CurrentLobbyData.SetData("usingwhitelist", (serverSettings.Whitelist != null && serverSettings.Whitelist.Enabled).ToString());
instance.client.Lobby.CurrentLobbyData.SetData("modeselectionmode", serverSettings.ModeSelectionMode.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("subselectionmode", serverSettings.SubSelectionMode.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("voicechatenabled", serverSettings.VoiceChatEnabled.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("allowspectating", serverSettings.AllowSpectating.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("allowrespawn", serverSettings.AllowRespawn.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("traitors", serverSettings.TraitorsEnabled.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("gamestarted", GameMain.Client.GameStarted.ToString());
instance.client.Lobby.CurrentLobbyData.SetData("gamemode", serverSettings.GameModeIdentifier);
DebugConsole.NewMessage("Lobby updated!", Microsoft.Xna.Framework.Color.Lime);
}
public static string GetUsername()
public static void LeaveLobby()
{
if (instance == null || !instance.isInitialized)
if (lobbyState != LobbyState.NotConnected)
{
return "";
lobbyIPRetrievalThread?.Abort();
lobbyIPRetrievalThread?.Join();
lobbyIPRetrievalThread = null;
instance.client.Lobby.Leave();
lobbyID = 0;
lobbyIP = "";
lobbyState = LobbyState.NotConnected;
instance.client.Lobby.OnLobbyJoined = null;
}
return instance.client.Username;
}
public static void JoinLobby(UInt64 id, bool joinServer)
{
if (instance.client.Lobby.CurrentLobby == id) { return; }
if (lobbyID == id) { return; }
instance.client.Lobby.OnLobbyJoined = (success) =>
{
try
{
if (!success)
{
DebugConsole.ThrowError("Failed to join Steam lobby: "+id.ToString());
return;
}
lobbyState = LobbyState.Joined;
lobbyID = instance.client.Lobby.CurrentLobby;
if (joinServer)
{
GameMain.Instance.ConnectLobby = 0;
GameMain.Instance.ConnectName = instance.client.Lobby.Name;
GameMain.Instance.ConnectEndpoint = instance.client.Lobby.Owner.ToString();
}
}
finally
{
instance.client.Lobby.OnLobbyJoined = null;
}
};
lobbyState = LobbyState.Joining;
lobbyID = id;
instance.client.Lobby.Join(id);
}
public static ulong GetWorkshopItemIDFromUrl(string url)
@@ -112,13 +311,16 @@ namespace Barotrauma.Steam
//the response is queried using the server's query port, not the game port,
//so it may be possible to play on the server even if it doesn't respond to server list queries
var query = instance.client.ServerList.Internet(filter);
query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnUpdate = () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnFinished = onFinished;
var localQuery = instance.client.ServerList.Local(filter);
localQuery.OnUpdate += () => { UpdateServerQuery(localQuery, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
localQuery.OnUpdate = () => { UpdateServerQuery(localQuery, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
localQuery.OnFinished = onFinished;
instance.client.LobbyList.OnLobbiesUpdated = () => { UpdateLobbyQuery(onServerFound, onServerRulesReceived, onFinished); };
instance.client.LobbyList.Refresh();
return true;
}
@@ -141,7 +343,7 @@ namespace Barotrauma.Steam
//the response is queried using the server's query port, not the game port,
//so it may be possible to play on the server even if it doesn't respond to server list queries
var query = instance.client.ServerList.Favourites(filter);
query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnUpdate = () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnFinished = onFinished;
return true;
@@ -166,12 +368,71 @@ namespace Barotrauma.Steam
//the response is queried using the server's query port, not the game port,
//so it may be possible to play on the server even if it doesn't respond to server list queries
var query = instance.client.ServerList.History(filter);
query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnUpdate = () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); };
query.OnFinished = onFinished;
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.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 = "",
IP = ip,
PlayerCount = currPlayers,
MaxPlayers = maxPlayers,
HasPassword = hasPassword,
RespondedToSteamQuery = true,
LobbyID = lobby.LobbyID
};
serverInfo.PingChecked = false;
serverInfo.ServerMessage = lobby.GetData("message");
serverInfo.GameVersion = lobby.GetData("version");
serverInfo.ContentPackageNames.AddRange(lobby.GetData("contentpackage").Split(','));
serverInfo.ContentPackageHashes.AddRange(lobby.GetData("contentpackagehash").Split(','));
serverInfo.ContentPackageWorkshopUrls.AddRange(lobby.GetData("contentpackageurl").Split(','));
serverInfo.UsingWhiteList = lobby.GetData("usingwhitelist") == "True";
SelectionMode selectionMode;
if (Enum.TryParse(lobby.GetData("modeselectionmode"), out selectionMode)) serverInfo.ModeSelectionMode = selectionMode;
if (Enum.TryParse(lobby.GetData("subselectionmode"), out selectionMode)) serverInfo.SubSelectionMode = selectionMode;
serverInfo.AllowSpectating = lobby.GetData("allowspectating") == "True";
serverInfo.AllowRespawn = lobby.GetData("allowrespawn") == "True";
serverInfo.VoipEnabled = lobby.GetData("voicechatenabled") == "True";
if (Enum.TryParse(lobby.GetData("traitors"), out YesNoMaybe traitorsEnabled)) serverInfo.TraitorsEnabled = traitorsEnabled;
serverInfo.GameStarted = lobby.GetData("gamestarted") == "True";
serverInfo.GameMode = lobby.GetData("gamemode");
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
{
//invalid contentpackage info
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
}
onServerFound(serverInfo);
//onServerRulesReceived(serverInfo);
}
onFinished();
}
private static void UpdateServerQuery(ServerList.Request query, Action<Networking.ServerInfo> onServerFound, Action<Networking.ServerInfo> onServerRulesReceived, bool includeUnresponsive)
{
IEnumerable<ServerList.Server> servers = includeUnresponsive ?
@@ -191,6 +452,9 @@ namespace Barotrauma.Steam
{
DebugConsole.Log(s.Name + " did not respond to server query.");
}
if (s.Description == "Barotrauma IP Retrieval") { continue; }
var serverInfo = new ServerInfo()
{
ServerName = s.Name,
@@ -203,6 +467,7 @@ namespace Barotrauma.Steam
};
serverInfo.PingChecked = true;
serverInfo.Ping = s.Ping;
serverInfo.LobbyID = 0;
if (responded)
{
s.FetchRules();
@@ -210,7 +475,7 @@ namespace Barotrauma.Steam
s.OnReceivedRules += (bool rulesReceived) =>
{
if (!rulesReceived || s.Rules == null) { return; }
if (s.Rules.ContainsKey("message")) serverInfo.ServerMessage = s.Rules["message"];
if (s.Rules.ContainsKey("version")) serverInfo.GameVersion = s.Rules["version"];
@@ -270,6 +535,7 @@ namespace Barotrauma.Steam
return true;
}
private static Auth.Ticket currentTicket = null;
public static Auth.Ticket GetAuthSessionTicket()
{
if (instance == null || !instance.isInitialized)
@@ -277,17 +543,39 @@ namespace Barotrauma.Steam
return null;
}
return instance.client.Auth.GetAuthSessionTicket();
currentTicket?.Cancel();
currentTicket = instance.client.Auth.GetAuthSessionTicket();
return currentTicket;
}
public static ClientStartAuthSessionResult StartAuthSession(byte[] authTicketData, ulong clientSteamID)
{
if (instance == null || !instance.isInitialized || instance.client == null) return ClientStartAuthSessionResult.ServerNotConnectedToSteam;
DebugConsole.NewMessage("SteamManager authenticating Steam client " + clientSteamID);
ClientStartAuthSessionResult startResult = instance.client.Auth.StartSession(authTicketData, clientSteamID);
if (startResult != ClientStartAuthSessionResult.OK)
{
DebugConsole.NewMessage("Authentication failed: failed to start auth session (" + startResult.ToString() + ")");
}
return startResult;
}
public static void StopAuthSession(ulong clientSteamID)
{
if (instance == null || !instance.isInitialized || instance.client == null) return;
DebugConsole.NewMessage("SteamManager ending auth session with Steam client " + clientSteamID);
instance.client.Auth.EndSession(clientSteamID);
}
#endregion
#region Workshop
public const string WorkshopItemStagingFolder = "NewWorkshopItem";
public const string WorkshopItemPreviewImageFolder = "Workshop";
public const string PreviewImageName = "PreviewImage.png";
private const string MetadataFileName = "filelist.xml";
public const string DefaultPreviewImagePath = "Content/DefaultWorkshopPreviewImage.png";
private Sprite defaultPreviewImage;
@@ -404,54 +692,45 @@ namespace Barotrauma.Steam
item.Subscribe();
item.Download();
}
/// <summary>
/// Creates a new folder, copies the specified files there and creates a metadata file with install instructions.
/// </summary>
public static void CreateWorkshopItemStaging(List<ContentFile> contentFiles, out Workshop.Editor itemEditor, out ContentPackage contentPackage)
public static void CreateWorkshopItemStaging(ContentPackage contentPackage, out Workshop.Editor itemEditor)
{
var stagingFolder = new DirectoryInfo(WorkshopItemStagingFolder);
if (stagingFolder.Exists)
itemEditor = instance.client.Workshop.CreateItem(Workshop.ItemType.Community);
itemEditor.Visibility = Workshop.Editor.VisibilityType.Public;
itemEditor.WorkshopUploadAppId = AppID;
itemEditor.Folder = Path.GetFullPath(Path.GetDirectoryName(contentPackage.Path));
string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));
if (!Directory.Exists(itemEditor.Folder)) { Directory.CreateDirectory(itemEditor.Folder); }
if (!File.Exists(previewImagePath))
{
SaveUtil.ClearFolder(stagingFolder.FullName);
File.Copy("Content/DefaultWorkshopPreviewImage.png", previewImagePath);
}
else
{
stagingFolder.Create();
}
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Submarines"));
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Mods"));
Directory.CreateDirectory(Path.Combine(WorkshopItemStagingFolder, "Mods", "ModName"));
}
/// <summary>
/// Creates a new empty content package
/// </summary>
public static void CreateWorkshopItemStaging(string itemName, out Workshop.Editor itemEditor, out ContentPackage contentPackage)
{
string dirPath = Path.Combine("Mods", ToolBox.RemoveInvalidFileNameChars(itemName));
Directory.CreateDirectory("Mods");
Directory.CreateDirectory(dirPath);
itemEditor = instance.client.Workshop.CreateItem(Workshop.ItemType.Community);
itemEditor.Visibility = Workshop.Editor.VisibilityType.Public;
itemEditor.WorkshopUploadAppId = AppID;
itemEditor.Folder = stagingFolder.FullName;
itemEditor.Folder = dirPath;
string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));
File.Copy("Content/DefaultWorkshopPreviewImage.png", previewImagePath);
//copy content files to the staging folder
List<string> copiedFilePaths = new List<string>();
foreach (ContentFile file in contentFiles)
string previewImagePath = Path.GetFullPath(Path.Combine(dirPath, PreviewImageName));
if (!File.Exists(previewImagePath))
{
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(file.Path), Environment.CurrentDirectory);
string destinationPath = Path.Combine(stagingFolder.FullName, relativePath);
//make sure the directory exists
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
File.Copy(file.Path, destinationPath);
copiedFilePaths.Add(destinationPath);
File.Copy("Content/DefaultWorkshopPreviewImage.png", previewImagePath);
}
System.Diagnostics.Debug.Assert(copiedFilePaths.Count == contentFiles.Count);
//create a new content package and include the copied files in it
contentPackage = ContentPackage.CreatePackage("ContentPackage", Path.Combine(itemEditor.Folder, MetadataFileName), false);
for (int i = 0; i < copiedFilePaths.Count; i++)
{
contentPackage.AddFile(copiedFilePaths[i], contentFiles[i].Type);
}
contentPackage.Save(Path.Combine(stagingFolder.FullName, MetadataFileName));
contentPackage = ContentPackage.CreatePackage(itemName, Path.Combine(dirPath, MetadataFileName), false);
contentPackage.Save(Path.Combine(dirPath, MetadataFileName));
}
/// <summary>
@@ -467,23 +746,67 @@ namespace Barotrauma.Steam
return;
}
var stagingFolder = new DirectoryInfo(WorkshopItemStagingFolder);
if (stagingFolder.Exists)
{
SaveUtil.ClearFolder(stagingFolder.FullName);
}
else
{
stagingFolder.Create();
}
itemEditor = instance.client.Workshop.EditItem(existingItem.Id);
itemEditor.Visibility = Workshop.Editor.VisibilityType.Public;
itemEditor.Title = existingItem.Title;
itemEditor.Tags = existingItem.Tags.ToList();
itemEditor.Description = existingItem.Description;
itemEditor.WorkshopUploadAppId = AppID;
itemEditor.Folder = stagingFolder.FullName;
if (!CheckWorkshopItemEnabled(existingItem, checkContentFiles: false))
{
if (!EnableWorkShopItem(existingItem, false, out string errorMsg))
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { existingItem.Title, errorMsg }));
itemEditor = null;
contentPackage = null;
return;
}
}
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem.Directory.FullName, MetadataFileName));
string installedContentPackagePath = Path.GetFullPath(GetWorkshopItemContentPackagePath(tempContentPackage));
contentPackage = ContentPackage.List.Find(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath);
if (tempContentPackage.GameVersion > new Version(0, 9, 1, 0))
{
itemEditor.Folder = Path.GetDirectoryName(installedContentPackagePath);
}
else //legacy support
{
try
{
tempContentPackage.GameVersion = GameMain.Version;
string newPath = GetWorkshopItemContentPackagePath(tempContentPackage);
string newDir = Path.GetDirectoryName(newPath);
contentPackage.Path = newPath;
itemEditor.Folder = newDir;
if (!Directory.Exists(newDir)) { Directory.CreateDirectory(newDir); }
File.Move(installedContentPackagePath, newPath);
//move all files inside the Mods folder
foreach (ContentFile cf in contentPackage.Files)
{
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(cf.Path), Path.GetFullPath(newDir));
if (relativePath.StartsWith(".."))
{
string destinationPath = Path.Combine(newDir, cf.Path);
if (!Directory.Exists(Path.GetDirectoryName(destinationPath))) { Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); }
File.Move(cf.Path, destinationPath);
cf.Path = destinationPath;
}
}
contentPackage.Save(contentPackage.Path);
}
catch (Exception e)
{
string errorMsg = TextManager.GetWithVariable("WorkshopErrorOnEnable", "[itemname]", TextManager.EnsureUTF8(existingItem.Title));
new GUIMessageBox(TextManager.Get("Error"), errorMsg);
DebugConsole.ThrowError(errorMsg, e);
return;
}
}
string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));
itemEditor.PreviewImage = previewImagePath;
@@ -512,46 +835,6 @@ namespace Barotrauma.Steam
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CreateWorkshopItemStaging:WriteAllBytesFailed" + previewImagePath,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message);
}
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem.Directory.FullName, MetadataFileName));
//item already installed, copy it from the game folder
if (existingItem != null && CheckWorkshopItemEnabled(existingItem, checkContentFiles: false))
{
string installedItemPath = GetWorkshopItemContentPackagePath(tempContentPackage);
if (File.Exists(installedItemPath))
{
tempContentPackage = new ContentPackage(installedItemPath);
}
}
if (File.Exists(tempContentPackage.Path))
{
string newContentPackagePath = Path.Combine(WorkshopItemStagingFolder, MetadataFileName);
File.Copy(tempContentPackage.Path, newContentPackagePath, overwrite: true);
contentPackage = new ContentPackage(newContentPackagePath);
foreach (ContentFile contentFile in tempContentPackage.Files)
{
string sourceFile;
if (contentFile.Type == ContentType.Submarine && File.Exists(contentFile.Path))
{
sourceFile = contentFile.Path;
}
else
{
sourceFile = Path.Combine(existingItem.Directory.FullName, contentFile.Path);
}
if (!File.Exists(sourceFile)) { continue; }
//make sure the destination directory exists
string destinationPath = Path.Combine(WorkshopItemStagingFolder, contentFile.Path);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
File.Copy(sourceFile, destinationPath, overwrite: true);
contentPackage.AddFile(contentFile.Path, contentFile.Type);
}
}
else
{
contentPackage = ContentPackage.CreatePackage(existingItem.Title, Path.Combine(WorkshopItemStagingFolder, MetadataFileName), false);
contentPackage.Save(contentPackage.Path);
}
}
public static void StartPublishItem(ContentPackage contentPackage, Workshop.Editor item)
@@ -568,12 +851,11 @@ namespace Barotrauma.Steam
DebugConsole.ThrowError("Cannot publish workshop item \"" + item.Title + "\" - folder not set.");
return;
}
contentPackage.Name = item.Title;
contentPackage.GameVersion = GameMain.Version;
contentPackage.Save(Path.Combine(WorkshopItemStagingFolder, MetadataFileName));
contentPackage.Save(contentPackage.Path);
if (File.Exists(PreviewImageName)) File.Delete(PreviewImageName);
if (File.Exists(PreviewImageName)) { File.Delete(PreviewImageName); }
//move the preview image out of the staging folder, it does not need to be included in the folder sent to Workshop
File.Move(Path.GetFullPath(Path.Combine(item.Folder, PreviewImageName)), PreviewImageName);
item.PreviewImage = Path.GetFullPath(PreviewImageName);
@@ -604,18 +886,7 @@ namespace Barotrauma.Steam
{
DebugConsole.NewMessage("Publishing workshop item " + item.Title + " failed. " + item.Error, Microsoft.Xna.Framework.Color.Red);
}
SaveUtil.ClearFolder(WorkshopItemStagingFolder);
File.Delete(PreviewImageName);
try
{
Directory.Delete(WorkshopItemStagingFolder);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to delete Workshop item staging folder.", e);
}
yield return CoroutineStatus.Success;
}
@@ -632,6 +903,14 @@ namespace Barotrauma.Steam
}
string metaDataFilePath = Path.Combine(item.Directory.FullName, MetadataFileName);
if (!File.Exists(metaDataFilePath))
{
errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item.Title);
DebugConsole.ThrowError(errorMsg);
return false;
}
ContentPackage contentPackage = new ContentPackage(metaDataFilePath);
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
@@ -644,11 +923,52 @@ namespace Barotrauma.Steam
if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes))
{
errorMsg = TextManager.GetWithVariables("ContentPackageMissingCoreFiles", new string[2] { "[packagename]", "[missingfiletypes]" },
errorMsg = TextManager.GetWithVariables("ContentPackageMissingCoreFiles", new string[2] { "[packagename]", "[missingfiletypes]" },
new string[2] { contentPackage.Name, string.Join(", ", missingContentTypes) }, new bool[2] { false, true });
return false;
}
if (contentPackage.GameVersion > new Version(0, 9, 1, 0))
{
SaveUtil.CopyFolder(item.Directory.FullName, Path.GetDirectoryName(GetWorkshopItemContentPackagePath(contentPackage)), copySubDirs: true, overwriteExisting: true);
}
else //legacy support
{
EnableWorkShopItemLegacy(item, contentPackage, newContentPackagePath, metaDataFilePath, allowFileOverwrite, out errorMsg);
}
var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath)
{
SteamWorkshopUrl = item.Url,
InstallTime = item.Modified > item.Created ? item.Modified : item.Created
};
if (!Directory.Exists(Path.GetDirectoryName(newContentPackagePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (newPackage.CorePackage)
{
//if enabling a core package, disable all other core packages
GameMain.Config.SelectedContentPackages.RemoveWhere(cp => cp.CorePackage);
}
GameMain.Config.SelectedContentPackages.Add(newPackage);
GameMain.Config.SaveNewPlayerConfig();
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine))
{
Submarine.RefreshSavedSubs();
}
errorMsg = "";
return true;
}
private static bool EnableWorkShopItemLegacy(Workshop.Item item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath, bool allowFileOverwrite, out string errorMsg)
{
errorMsg = "";
var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories);
List<string> nonContentFiles = new List<string>();
foreach (string file in allPackageFiles)
@@ -748,27 +1068,6 @@ namespace Barotrauma.Steam
return false;
}
var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath)
{
SteamWorkshopUrl = item.Url,
InstallTime = item.Modified > item.Created ? item.Modified : item.Created
};
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (newPackage.CorePackage)
{
//if enabling a core package, disable all other core packages
GameMain.Config.SelectedContentPackages.RemoveWhere(cp => cp.CorePackage);
}
GameMain.Config.SelectedContentPackages.Add(newPackage);
GameMain.Config.SaveNewPlayerConfig();
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine))
{
Submarine.RefreshSavedSubs();
}
errorMsg = "";
return true;
}
@@ -894,7 +1193,14 @@ namespace Barotrauma.Steam
public static bool CheckWorkshopItemEnabled(Workshop.Item item, bool checkContentFiles = true)
{
if (!item.Installed) return false;
if (!item.Installed) { return false; }
if (!Directory.Exists(item.Directory.FullName))
{
DebugConsole.ThrowError("Workshop item \"" + item.Title + "\" has been installed but the install directory cannot be found. Attempting to redownload...");
item.ForceDownload();
return false;
}
string metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName);
if (!File.Exists(metaDataPath))
@@ -1004,10 +1310,11 @@ namespace Barotrauma.Steam
public static string GetWorkshopItemContentPackagePath(ContentPackage contentPackage)
{
string fileName = contentPackage.Name + ".xml";
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalidChars) fileName = fileName.Replace(c.ToString(), "");
return Path.Combine("Data", "ContentPackages", fileName);
string fileName = contentPackage.Name;
string invalidChars = ToolBox.RemoveInvalidFileNameChars(fileName);
return contentPackage.GameVersion > new Version(0, 9, 1, 0) ?
Path.Combine("Mods", fileName, MetadataFileName) :
Path.Combine("Data", "ContentPackages", fileName + ".xml"); //legacy support
}
#endregion

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using OpenAL;
using System;
using System.Linq;
@@ -97,7 +96,8 @@ namespace Barotrauma.Networking
UserData = "capturedevicenotfound"
};
}
GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpen(" + deviceName + ") failed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,"Error code: "+errorCode);
GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Alc.CaptureDeviceOpen(" + deviceName + ") failed. Error code: " + errorCode);
GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled;
Instance?.Dispose();
Instance = null;
@@ -234,7 +234,7 @@ namespace Barotrauma.Networking
}
}
public override void Write(NetBuffer msg)
public override void Write(IWriteMessage msg)
{
lock (buffers)
{

View File

@@ -1,5 +1,4 @@
using Barotrauma.Sounds;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,13 +12,13 @@ namespace Barotrauma.Networking
class VoipClient : IDisposable
{
private GameClient gameClient;
private NetClient netClient;
private ClientPeer netClient;
private DateTime lastSendTime;
private List<VoipQueue> queues;
private UInt16 storedBufferID = 0;
public VoipClient(GameClient gClient,NetClient nClient)
public VoipClient(GameClient gClient,ClientPeer nClient)
{
gameClient = gClient;
netClient = nClient;
@@ -59,19 +58,19 @@ namespace Barotrauma.Networking
if (DateTime.Now >= lastSendTime + VoipConfig.SEND_INTERVAL)
{
NetOutgoingMessage msg = netClient.CreateMessage();
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.VOICE);
msg.Write((byte)VoipCapture.Instance.QueueID);
VoipCapture.Instance.Write(msg);
netClient.SendMessage(msg, NetDeliveryMethod.Unreliable);
netClient.Send(msg, DeliveryMethod.Unreliable);
lastSendTime = DateTime.Now;
}
}
public void Read(NetBuffer msg)
public void Read(IReadMessage msg)
{
byte queueId = msg.ReadByte();
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
@@ -95,7 +94,7 @@ namespace Barotrauma.Networking
client.VoipSound = new VoipSound(GameMain.SoundManager, client.VoipQueue);
}
if (client.Character != null && !client.Character.IsDead && !client.Character.IsDead && client.Character.SpeechImpediment <= 100.0f)
if (client.Character != null && !client.Character.IsDead && client.Character.SpeechImpediment <= 100.0f)
{
var messageType = ChatMessage.CanUseRadio(client.Character, out WifiComponent radio) ? ChatMessageType.Radio : ChatMessageType.Default;
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
@@ -114,8 +113,27 @@ namespace Barotrauma.Networking
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
}
}
GameMain.NetLobbyScreen.SetPlayerSpeaking(client);
GameMain.NetLobbyScreen?.SetPlayerSpeaking(client);
GameMain.GameSession?.CrewManager?.SetClientSpeaking(client);
if (client.VoipSound.CurrentAmplitude > 0.1f) //TODO: might need to tweak
{
if (client.Character != null)
{
Vector3 clientPos = new Vector3(client.Character.WorldPosition.X, client.Character.WorldPosition.Y, 0.0f);
Vector3 listenerPos = GameMain.SoundManager.ListenerPosition;
float attenuationDist = client.VoipSound.Near * 1.125f;
if (Vector3.DistanceSquared(clientPos, listenerPos) < attenuationDist * attenuationDist)
{
GameMain.SoundManager.VoipAttenuatedGain = 0.5f;
}
}
else
{
GameMain.SoundManager.VoipAttenuatedGain = 0.5f;
}
}
}
}

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
@@ -105,7 +104,7 @@ namespace Barotrauma
}
}
public void ClientWrite(NetBuffer msg, VoteType voteType, object data)
public void ClientWrite(IWriteMessage msg, VoteType voteType, object data)
{
msg.Write((byte)voteType);
@@ -141,7 +140,7 @@ namespace Barotrauma
msg.WritePadBits();
}
public void ClientRead(NetBuffer inc)
public void ClientRead(IReadMessage inc)
{
AllowSubVoting = inc.ReadBoolean();
if (allowSubVoting)

View File

@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -177,7 +176,7 @@ namespace Barotrauma.Networking
return true;
}
public void ClientAdminRead(NetBuffer incMsg)
public void ClientAdminRead(IReadMessage incMsg)
{
bool hasPermission = incMsg.ReadBoolean();
if (!hasPermission)
@@ -192,8 +191,8 @@ namespace Barotrauma.Networking
incMsg.ReadPadBits();
whitelistedPlayers.Clear();
Int32 bannedPlayerCount = incMsg.ReadVariableInt32();
for (int i = 0; i < bannedPlayerCount; i++)
UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();
for (int i = 0; i < (int)bannedPlayerCount; i++)
{
string name = incMsg.ReadString();
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
@@ -216,7 +215,7 @@ namespace Barotrauma.Networking
}
}
public void ClientAdminWrite(NetBuffer outMsg)
public void ClientAdminWrite(IWriteMessage outMsg)
{
outMsg.Write(localEnabled);
outMsg.WritePadBits();

View File

@@ -144,7 +144,7 @@ namespace Barotrauma
1.0f / bodyShapeTextureScale, SpriteEffects.None, 0.0f);
}
public PosInfo ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime, string parentDebugName)
public PosInfo ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime, string parentDebugName)
{
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
@@ -156,8 +156,8 @@ namespace Barotrauma
float? newAngularVelocity = null;
newPosition = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
msg.ReadSingle(),
msg.ReadSingle());
awake = msg.ReadBoolean();
bool fixedRotation = msg.ReadBoolean();

View File

@@ -599,7 +599,8 @@ namespace Barotrauma
if (!tb.Selected) { return false; }
RefreshMissionTab(tb.UserData as Mission);
Campaign.Map.OnMissionSelected?.Invoke(connection, mission);
if (GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign))
{
GameMain.Client?.SendCampaignState();
}

View File

@@ -97,7 +97,7 @@ namespace Barotrauma
SoundPlayer.OverrideMusicType = "none";
SoundPlayer.OverrideMusicDuration = null;
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f, 0);
GUI.ForceMouseOn(null);
CalculateSpritesheetPosition();
@@ -195,7 +195,7 @@ namespace Barotrauma
base.Deselect();
SoundPlayer.OverrideMusicType = null;
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameMain.Config.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameMain.Config.SoundVolume, 0);
GUI.ForceMouseOn(null);
if (isEndlessRunner)
@@ -1467,6 +1467,7 @@ namespace Barotrauma
private void ShowWearables()
{
if (character.Inventory == null) { return; }
foreach (var item in character.Inventory.Items)
{
if (item == null) { continue; }
@@ -1478,7 +1479,7 @@ namespace Barotrauma
private void HideWearables()
{
character.Inventory.Items.ForEachMod(i => i?.Unequip(character));
character.Inventory?.Items.ForEachMod(i => i?.Unequip(character));
}
#endregion

View File

@@ -23,8 +23,8 @@ namespace Barotrauma
private CampaignSetupUI campaignSetupUI;
private GUITextBox serverNameBox, portBox, queryPortBox, passwordBox, maxPlayersBox;
private GUITickBox isPublicBox, useUpnpBox;
private GUITextBox serverNameBox, /*portBox, queryPortBox,*/ passwordBox, maxPlayersBox;
private GUITickBox isPublicBox/*, useUpnpBox*/;
private GUIButton joinServerButton, hostServerButton, steamWorkshopButton;
@@ -203,10 +203,6 @@ namespace Barotrauma
UserData = Tab.SteamWorkshop,
OnClicked = SelectTab
};
/*#if OSX && !DEBUG
steamWorkshopButton.Text += " (Not yet available on MacOS)";
#endif*/
}
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
@@ -256,7 +252,7 @@ namespace Barotrauma
UserData = Tab.Settings,
OnClicked = SelectTab
};
//TODO: translate
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("CreditsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{
ForceUpperCase = true,
@@ -323,7 +319,7 @@ namespace Barotrauma
StartNewGame = StartGame
};
var hostServerScale = new Vector2(0.7f, 1.0f);
var hostServerScale = new Vector2(0.7f, 0.6f);
menuTabs[(int)Tab.HostServer] = new GUIFrame(new RectTransform(
Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale)) { RelativeOffset = relativeSpacing });
@@ -703,7 +699,7 @@ namespace Barotrauma
return false;
}
if (!int.TryParse(portBox.Text, out int port) || port < 0 || port > 65535)
/*if (!int.TryParse(portBox.Text, out int port) || port < 0 || port > 65535)
{
portBox.Text = NetConfig.DefaultPort.ToString();
portBox.Flash();
@@ -720,7 +716,7 @@ namespace Barotrauma
portBox.Flash();
return false;
}
}
}*/
GameMain.NetLobbyScreen = new NetLobbyScreen();
try
@@ -732,16 +728,22 @@ namespace Barotrauma
exeName = "DedicatedServer.exe";
}
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
" -port " + port.ToString() +
" -queryport " + queryPort.ToString() +
" -public " + isPublicBox.Selected.ToString() +
" -password \"" + passwordBox.Text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" +
" -upnp " + useUpnpBox.Selected +
" -maxplayers " + maxPlayersBox.Text +
" -ownerkey " + ownerKey.ToString();
" -maxplayers " + maxPlayersBox.Text;
int ownerKey = 0;
if (Steam.SteamManager.GetSteamID()!=0)
{
arguments += " -steamid " + Steam.SteamManager.GetSteamID();
}
else
{
ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
arguments += " -ownerkey " + ownerKey;
}
string filename = exeName;
#if LINUX || OSX
@@ -752,13 +754,15 @@ namespace Barotrauma
FileName = filename,
Arguments = arguments,
#if !DEBUG
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
#endif
};
GameMain.ServerChildProcess = Process.Start(processInfo);
Thread.Sleep(1000); //wait until the server is ready before connecting
GameMain.Client = new GameClient(name, "127.0.0.1:" + port.ToString(), name, ownerKey);
GameMain.Client = new GameClient(name, System.Net.IPAddress.Loopback.ToString(), Steam.SteamManager.GetSteamID(), name, ownerKey, true);
}
catch (Exception e)
{
@@ -798,7 +802,10 @@ namespace Barotrauma
#else
joinServerButton.Enabled = true;
hostServerButton.Enabled = true;
steamWorkshopButton.Enabled = true;
if (Steam.SteamManager.USE_STEAM)
{
steamWorkshopButton.Enabled = true;
}
#endif
}
@@ -976,6 +983,7 @@ namespace Barotrauma
OverflowClip = true
};
/* TODO: allow lidgren servers from client?
label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("ServerPort"), textAlignment: textAlignment);
portBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), textAlignment: textAlignment)
{
@@ -991,7 +999,7 @@ namespace Barotrauma
Text = queryPort.ToString(),
ToolTip = TextManager.Get("ServerQueryPortToolTip")
};
}
}*/
var maxPlayersLabel = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("MaxPlayers"), textAlignment: textAlignment);
var buttonContainer = new GUILayoutGroup(new RectTransform(textFieldSize, maxPlayersLabel.RectTransform, Anchor.CenterRight), isHorizontal: true)
@@ -1027,10 +1035,11 @@ namespace Barotrauma
ToolTip = TextManager.Get("PublicServerToolTip")
};
/* TODO: remove UPnP altogether?
useUpnpBox = new GUITickBox(new RectTransform(tickBoxSize, parent.RectTransform), TextManager.Get("AttemptUPnP"))
{
ToolTip = TextManager.Get("AttemptUPnPToolTip")
};
};*/
new GUIButton(new RectTransform(new Vector2(0.4f, 0.1f), menuTabs[(int)Tab.HostServer].RectTransform, Anchor.BottomRight)
{

View File

@@ -1383,7 +1383,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ClosePlayerFrame(btn, userdata); return true; }
};
Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.24f, .34f) : new Vector2(.24f, .14f);
Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.24f, .44f) : new Vector2(.24f, .24f);
var playerFrameInner = new GUIFrame(new RectTransform(frameSize, playerFrame.RectTransform, Anchor.Center));
var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), playerFrameInner.RectTransform, Anchor.Center))
@@ -1530,13 +1530,29 @@ namespace Barotrauma
}
var buttonAreaUpper = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true);
var buttonAreaMiddle = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true);
var buttonAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true);
if (selectedClient.SteamID != 0 && Steam.SteamManager.IsInitialized)
{
var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.8f, 1.0f), buttonAreaUpper.RectTransform, Anchor.TopCenter),
TextManager.Get("ViewSteamProfile"))
{
UserData = selectedClient
};
viewSteamProfileButton.OnClicked = (bt, userdata) =>
{
Steam.SteamManager.Instance.Overlay.OpenUrl("https://steamcommunity.com/profiles/" + selectedClient.SteamID.ToString());
return true;
};
}
if (!myClient)
{
if (GameMain.Client.HasPermission(ClientPermissions.Ban))
{
var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform),
var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaMiddle.RectTransform),
TextManager.Get("Ban"))
{
UserData = selectedClient
@@ -1544,7 +1560,7 @@ namespace Barotrauma
banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; };
banButton.OnClicked += ClosePlayerFrame;
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform),
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaMiddle.RectTransform),
TextManager.Get("BanRange"))
{
UserData = selectedClient
@@ -1578,7 +1594,7 @@ namespace Barotrauma
kickButton.OnClicked += ClosePlayerFrame;
}
new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaUpper.RectTransform, Anchor.TopRight),
new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaMiddle.RectTransform, Anchor.TopRight),
TextManager.Get("Mute"))
{
IgnoreLayoutGroups = true,

View File

@@ -10,6 +10,7 @@ using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Barotrauma
@@ -45,7 +46,7 @@ namespace Barotrauma
private readonly GUITickBox filterEmpty;
private string sortedBy;
private readonly GUIButton serverPreviewToggleButton;
//a timer for preventing the client from spamming the refresh button faster than AllowedRefreshInterval
@@ -332,7 +333,14 @@ namespace Barotrauma
{
if (ipBox.UserData is ServerInfo selectedServer)
{
JoinServer(selectedServer.IP + ":" + selectedServer.Port, selectedServer.ServerName);
if (selectedServer.LobbyID == 0)
{
JoinServer(selectedServer.IP + ":" + selectedServer.Port, selectedServer.ServerName);
}
else
{
Steam.SteamManager.JoinLobby(selectedServer.LobbyID, true);
}
}
else if (!string.IsNullOrEmpty(ipBox.Text))
{
@@ -474,6 +482,15 @@ namespace Barotrauma
RefreshServers();
}
public override void Deselect()
{
base.Deselect();
if (SteamManager.IsInitialized && SteamManager.Instance.LobbyList != null)
{
SteamManager.Instance.LobbyList.OnLobbiesUpdated = null;
}
}
private void FilterServers()
{
serverList.Content.RemoveChild(serverList.Content.FindChild("noresults"));
@@ -671,16 +688,24 @@ namespace Barotrauma
private void AddToServerList(ServerInfo serverInfo)
{
var serverFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), serverList.Content.RectTransform) { MinSize = new Point(0, 35) },
var serverFrame = serverList.Content.FindChild(d => (d.UserData is ServerInfo info) &&
(info.LobbyID==serverInfo.LobbyID && info.IP==serverInfo.IP && info.Port==serverInfo.Port));
if (serverFrame == null)
{
serverFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), serverList.Content.RectTransform) { MinSize = new Point(0, 35) },
style: "InnerFrame", color: Color.White * 0.5f)
{
UserData = serverInfo
};
new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 1.0f), serverFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
//RelativeSpacing = 0.02f
};
{
UserData = serverInfo
};
new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 1.0f), serverFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
//RelativeSpacing = 0.02f
};
}
serverFrame.UserData = serverInfo;
UpdateServerInfo(serverInfo);
SortList(sortedBy, toggle: false);
@@ -901,13 +926,17 @@ namespace Barotrauma
return true;
}
private IEnumerable<object> ConnectToServer(string ip, string serverName)
private IEnumerable<object> ConnectToServer(string endpoint, string serverName)
{
string serverIP = null;
UInt64 serverSteamID = SteamManager.SteamIDStringToUInt64(endpoint);
if (serverSteamID == 0) { serverIP = endpoint; }
#if !DEBUG
try
{
#endif
GameMain.Client = new GameClient(clientNameBox.Text, ip, serverName);
GameMain.Client = new GameClient(clientNameBox.Text, serverIP, serverSteamID, serverName);
#if !DEBUG
}
catch (Exception e)
@@ -961,7 +990,7 @@ namespace Barotrauma
public void PingServer(ServerInfo serverInfo, int timeOut)
{
if (serverInfo?.IP == null)
if (string.IsNullOrWhiteSpace(serverInfo?.IP))
{
serverInfo.PingChecked = true;
serverInfo.Ping = -1;
@@ -969,7 +998,8 @@ namespace Barotrauma
}
long rtt = -1;
IPAddress address = IPAddress.Parse(serverInfo.IP);
IPAddress address = null;
IPAddress.TryParse(serverInfo.IP, out address);
if (address != null)
{
//don't attempt to ping if the address is IPv6 and it's not supported

View File

@@ -159,6 +159,7 @@ namespace Barotrauma
OnSelected = (component, userdata) =>
{
if (GUI.MouseOn is GUIButton || GUI.MouseOn?.Parent is GUIButton) { return false; }
if (GUI.MouseOn is GUITickBox || GUI.MouseOn?.Parent is GUITickBox) { return false; }
myItemList.Deselect();
if (userdata is Facepunch.Steamworks.Workshop.Item item)
{
@@ -190,16 +191,6 @@ namespace Barotrauma
}
};
new GUIButton(new RectTransform(new Vector2(0.5f, 0.05f), leftColumn.RectTransform), TextManager.Get("CreateWorkshopItem"))
{
OnClicked = (btn, userData) =>
{
CreateWorkshopItem();
ShowCreateItemFrame();
return true;
}
};
createItemFrame = new GUIFrame(new RectTransform(new Vector2(0.58f, 1.0f), tabs[(int)Tab.Publish].RectTransform, Anchor.TopRight), style: "InnerFrame");
buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.08f), container.RectTransform), childAnchor: Anchor.CenterLeft);
@@ -269,7 +260,11 @@ namespace Barotrauma
});
SteamManager.GetPopularWorkshopItems((items) => { OnItemsReceived(items, topItemList); }, 20);
SteamManager.GetPublishedWorkshopItems((items) => { OnItemsReceived(items, publishedItemList); });
RefreshMyItemList();
}
private void RefreshMyItemList()
{
myItemList.ClearChildren();
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), myItemList.Content.RectTransform), TextManager.Get("WorkshopLabelSubmarines"), textAlignment: Alignment.Center, font: GUI.LargeFont)
{
@@ -278,12 +273,28 @@ namespace Barotrauma
foreach (Submarine sub in Submarine.SavedSubmarines)
{
if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; }
//ignore subs that are part of some content package
string subPath = Path.GetFullPath(sub.FilePath);
if (ContentPackage.List.Any(cp => cp.Files.Any(f => f.Type == ContentType.Submarine && Path.GetFullPath(f.Path) == subPath)))
//ignore subs that are part of the vanilla content package
if (GameMain.VanillaContent != null &&
GameMain.VanillaContent.GetFilesOfType(ContentType.Submarine).Any(s => Path.GetFullPath(s) == subPath))
{
continue;
}
//ignore subs that are part of a workshop content package
if (ContentPackage.List.Any(cp => !string.IsNullOrEmpty(cp.SteamWorkshopUrl) &&
cp.Files.Any(f => f.Type == ContentType.Submarine && Path.GetFullPath(f.Path) == subPath)))
{
continue;
}
//ignore subs that are defined in a content package with more files than just the sub
//(these will be listed in the "content packages" section)
if (ContentPackage.List.Any(cp => cp.Files.Count > 1 &&
cp.Files.Any(f => f.Type == ContentType.Submarine && Path.GetFullPath(f.Path) == subPath)))
{
continue;
}
CreateMyItemFrame(sub, myItemList);
}
@@ -294,6 +305,8 @@ namespace Barotrauma
foreach (ContentPackage contentPackage in ContentPackage.List)
{
if (!string.IsNullOrEmpty(contentPackage.SteamWorkshopUrl) || contentPackage.HideInWorkshopMenu) { continue; }
//don't list content packages that only define one sub (they're visible in the "Submarines" section)
if (contentPackage.Files.Count == 1 && contentPackage.Files[0].Type == ContentType.Submarine) { continue; }
CreateMyItemFrame(contentPackage, myItemList);
}
}
@@ -459,6 +472,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) =>
{
item.UnSubscribe();
subscribedItemList.RemoveChild(subscribedItemList.Content.GetChildByUserData(item));
return true;
}
};
@@ -787,34 +801,40 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), modificationDate.RectTransform, Anchor.CenterRight), item.Modified.ToString("dd.MM.yyyy"), textAlignment: Alignment.TopRight);
}
private void CreateWorkshopItem()
/*private void CreateWorkshopItem()
{
SteamManager.CreateWorkshopItemStaging(new List<ContentFile>(), out itemEditor, out itemContentPackage);
}
SteamManager.CreateWorkshopItemStaging("ModName", out itemEditor, out itemContentPackage);
}*/
private void CreateWorkshopItem(Submarine sub)
{
SteamManager.CreateWorkshopItemStaging(new List<ContentFile>(), out itemEditor, out itemContentPackage);
string destinationFolder = Path.Combine("Mods", sub.Name);
itemContentPackage = ContentPackage.CreatePackage(sub.Name, Path.Combine(destinationFolder, SteamManager.MetadataFileName), corePackage: false);
SteamManager.CreateWorkshopItemStaging(itemContentPackage, out itemEditor);
string destinationPath = Path.Combine(SteamManager.WorkshopItemStagingFolder, "Submarines", Path.GetFileName(sub.FilePath));
try
string submarineDir = Path.GetDirectoryName(sub.FilePath);
if (submarineDir != Path.GetDirectoryName(destinationFolder))
{
File.Copy(sub.FilePath, destinationPath);
string destinationPath = Path.Combine(destinationFolder, Path.GetFileName(sub.FilePath));
if (!File.Exists(destinationPath))
{
File.Move(sub.FilePath, destinationPath);
}
sub.FilePath = destinationPath;
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to copy submarine file \"" + sub.FilePath + "\" to the Workshop item staging folder.", e);
return;
}
itemContentPackage.AddFile(Path.Combine("Submarines", Path.GetFileName(sub.FilePath)), ContentType.Submarine);
itemContentPackage.AddFile(sub.FilePath, ContentType.Submarine);
itemContentPackage.Name = sub.Name;
itemContentPackage.Save(itemContentPackage.Path);
ContentPackage.List.Add(itemContentPackage);
GameMain.Config.SelectedContentPackages.Add(itemContentPackage);
itemEditor.Title = sub.Name;
itemEditor.Tags.Add("Submarine");
itemEditor.Description = sub.Description;
if (sub.PreviewImage != null)
{
string previewImagePath = Path.GetFullPath(Path.Combine(SteamManager.WorkshopItemStagingFolder, SteamManager.PreviewImageName));
string previewImagePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(itemContentPackage.Path), SteamManager.PreviewImageName));
try
{
using (Stream s = File.Create(previewImagePath))
@@ -837,8 +857,13 @@ namespace Barotrauma
}
private void CreateWorkshopItem(ContentPackage contentPackage)
{
SteamManager.CreateWorkshopItemStaging(new List<ContentFile>(), out itemEditor, out itemContentPackage);
string modDirectory = "";
//SteamManager.CreateWorkshopItemStaging(new List<ContentFile>(), out itemEditor, out itemContentPackage);
itemContentPackage = contentPackage;
SteamManager.CreateWorkshopItemStaging(itemContentPackage, out itemEditor);
itemEditor.Title = contentPackage.Name;
/*string modDirectory = "";
foreach (ContentFile file in contentPackage.Files)
{
itemContentPackage.AddFile(file.Path, file.Type);
@@ -858,12 +883,8 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(modDirectory))
{
SaveUtil.CopyFolder(Path.Combine("Mods", modDirectory), Path.Combine(SteamManager.WorkshopItemStagingFolder, "Mods", modDirectory), copySubDirs: true);
}
}*/
itemContentPackage.CorePackage = contentPackage.CorePackage;
itemContentPackage.Name = contentPackage.Name;
itemContentPackage.Save(itemContentPackage.Path);
itemEditor.Title = contentPackage.Name;
}
private void CreateWorkshopItem(Facepunch.Steamworks.Workshop.Item item)
{
@@ -879,8 +900,16 @@ namespace Barotrauma
private void ShowCreateItemFrame()
{
createItemFrame.ClearChildren();
if (itemEditor == null) { return; }
if (itemEditor == null) return;
if (itemContentPackage == null)
{
string errorMsg = "Failed to edit workshop item (content package null)\n" + Environment.StackTrace;
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.ShowCreateItemFrame:ContentPackageNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
var createItemContent = new GUILayoutGroup(new RectTransform(new Vector2(0.92f, 0.92f), createItemFrame.RectTransform, Anchor.Center))
{
@@ -987,7 +1016,7 @@ namespace Barotrauma
Barotrauma.OpenFileDialog ofd = new Barotrauma.OpenFileDialog()
{
Multiselect = true,
InitialDirectory = Path.GetFullPath(SteamManager.WorkshopItemStagingFolder),
InitialDirectory = Path.GetFullPath(Path.GetDirectoryName(itemContentPackage.Path)),
Filter = TextManager.Get("WorkshopItemPreviewImage") + "|*.png",
Title = TextManager.Get("WorkshopItemPreviewImageDialogTitle")
};
@@ -1018,9 +1047,19 @@ namespace Barotrauma
return true;
}
};
//if preview image has not been set, but there's a PreviewImage file inside the mod folder, use that by default
if (string.IsNullOrEmpty(itemEditor.PreviewImage))
{
string previewImagePath = Path.Combine(Path.GetDirectoryName(itemContentPackage.Path), SteamManager.PreviewImageName);
if (File.Exists(previewImagePath))
{
itemEditor.PreviewImage = Path.GetFullPath(previewImagePath);
}
}
if (!string.IsNullOrEmpty(itemEditor.PreviewImage))
{
itemEditor.PreviewImage = Path.GetFullPath(itemEditor.PreviewImage);
if (itemPreviewSprites.ContainsKey(itemEditor.PreviewImage))
{
itemPreviewSprites[itemEditor.PreviewImage].Remove();
@@ -1068,7 +1107,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), fileListTitle.RectTransform, Anchor.CenterRight), TextManager.Get("WorkshopItemShowFolder"))
{
IgnoreLayoutGroups = true,
OnClicked = (btn, userdata) => { System.Diagnostics.Process.Start(Path.GetFullPath(SteamManager.WorkshopItemStagingFolder)); return true; }
OnClicked = (btn, userdata) => { System.Diagnostics.Process.Start(Path.GetFullPath(Path.GetDirectoryName(itemContentPackage.Path))); return true; }
};
createItemFileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.35f), createItemContent.RectTransform));
RefreshCreateItemFileList();
@@ -1077,11 +1116,11 @@ namespace Barotrauma
{
RelativeSpacing = 0.05f
};
new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform, Anchor.TopRight), TextManager.Get("WorkshopItemRefreshFileList"))
{
ToolTip = TextManager.Get("WorkshopItemRefreshFileListTooltip"),
OnClicked = (btn, userdata) =>
OnClicked = (btn, userdata) =>
{
itemContentPackage = new ContentPackage(itemContentPackage.Path);
RefreshCreateItemFileList();
@@ -1096,26 +1135,27 @@ namespace Barotrauma
{
Barotrauma.OpenFileDialog ofd = new Barotrauma.OpenFileDialog()
{
InitialDirectory = Path.GetFullPath(SteamManager.WorkshopItemStagingFolder),
InitialDirectory = Path.GetFullPath(Path.GetDirectoryName(itemContentPackage.Path)),
Title = TextManager.Get("workshopitemaddfiles"),
Multiselect = true
};
if (ofd.ShowDialog() == DialogResult.OK)
{
OnAddFilesSelected(ofd.FileNames);
RefreshMyItemList();
}
}
catch
{
//use a custom prompt if OpenFileDialog fails (Linux/Mac)
var msgBox = new GUIMessageBox(TextManager.Get("workshopitemaddfiles"), "", relativeSize: new Vector2(0.4f, 0.2f),
buttons: new string[] { TextManager.Get("Cancel"), TextManager.Get("OK") });
buttons: new string[] { TextManager.Get("Cancel"), TextManager.Get("OK") });
var pathBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.5f), msgBox.Content.RectTransform, Anchor.Center) { MinSize = new Point(0, 25) });
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += (btn2, userdata2) =>
msgBox.Buttons[1].OnClicked += (btn2, userdata2) =>
{
if (string.IsNullOrEmpty(pathBox?.Text)) { return true; }
string[] filePaths = pathBox.Text.Split(',');
@@ -1131,6 +1171,7 @@ namespace Barotrauma
}
};
//the item has been already published if it has a non-zero ID -> allow adding a changenote
if (itemEditor.Id > 0)
{
@@ -1181,6 +1222,9 @@ namespace Barotrauma
itemEditor = null;
SelectTab(Tab.Browse);
deleteVerification.Close();
createItemFrame.ClearChildren();
itemContentPackage.SteamWorkshopUrl = "";
itemContentPackage.Save(itemContentPackage.Path);
return true;
};
deleteVerification.Buttons[1].OnClicked = deleteVerification.Close;
@@ -1221,7 +1265,7 @@ namespace Barotrauma
private void OnPreviewImageSelected(GUIImage previewImageElement, string filePath)
{
string previewImagePath = Path.GetFullPath(Path.Combine(SteamManager.WorkshopItemStagingFolder, SteamManager.PreviewImageName));
string previewImagePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(itemContentPackage.Path), SteamManager.PreviewImageName));
if (new FileInfo(filePath).Length > 1024 * 1024)
{
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("WorkshopItemPreviewImageTooLarge"));
@@ -1248,33 +1292,36 @@ namespace Barotrauma
if (fileNames == null) { return; }
for (int i = 0; i < fileNames.Length; i++)
{
string file = fileNames[i];
if (string.IsNullOrEmpty(file)) { continue; }
file = file.Trim();
if (!File.Exists(file)) { continue; }
string file = fileNames[i]?.Trim();
if (string.IsNullOrEmpty(file) || !File.Exists(file)) { continue; }
string filePathRelativeToStagingFolder = UpdaterUtil.GetRelativePath(file, Path.Combine(Environment.CurrentDirectory, SteamManager.WorkshopItemStagingFolder));
string filePathRelativeToBaseFolder = UpdaterUtil.GetRelativePath(file, Environment.CurrentDirectory);
//file is not inside the staging folder
if (filePathRelativeToStagingFolder.StartsWith(".."))
string modFolder = Path.GetDirectoryName(itemContentPackage.Path);
string filePathRelativeToModFolder = UpdaterUtil.GetRelativePath(file, Path.Combine(Environment.CurrentDirectory, modFolder));
string destinationPath = Path.Combine(modFolder, Path.GetFileName(file));
//file is not inside the mod folder, we need to move it
if (filePathRelativeToModFolder.StartsWith("..") ||
Path.GetPathRoot(Environment.CurrentDirectory) != Path.GetPathRoot(file))
{
//submarines can be included in the content package directly
string basePath = Path.GetDirectoryName(filePathRelativeToBaseFolder.Replace("..", ""));
if (basePath == "Submarines")
string tryPath = destinationPath;
//add a number to the filename if a file with the same name already exists
i = 2;
while (File.Exists(destinationPath))
{
destinationPath = Path.Combine(modFolder, $"{Path.GetFileNameWithoutExtension(file)} ({i}){Path.GetExtension(file)}");
i++;
}
try
{
string destinationPath = Path.Combine(SteamManager.WorkshopItemStagingFolder, "Submarines", Path.GetFileName(file));
File.Copy(file, destinationPath);
itemContentPackage.AddFile(filePathRelativeToBaseFolder, ContentType.Submarine);
}
else
catch (Exception e)
{
itemContentPackage.AddFile(filePathRelativeToBaseFolder, ContentType.None);
DebugConsole.ThrowError("Copying the file \""+file+"\" to the mod folder failed.", e);
return;
}
}
else
{
itemContentPackage.AddFile(filePathRelativeToStagingFolder, ContentType.None);
}
itemContentPackage.AddFile(destinationPath, ContentType.None);
}
itemContentPackage.Save(itemContentPackage.Path);
RefreshCreateItemFileList();
@@ -1289,9 +1336,9 @@ namespace Barotrauma
foreach (ContentFile contentFile in itemContentPackage.Files)
{
bool illegalPath = !ContentPackage.IsModFilePathAllowed(contentFile);
string pathInStagingFolder = Path.Combine(SteamManager.WorkshopItemStagingFolder, contentFile.Path);
bool fileInStagingFolder = File.Exists(pathInStagingFolder);
bool fileExists = illegalPath ? File.Exists(contentFile.Path) : fileInStagingFolder;
//string pathInStagingFolder = Path.Combine(SteamManager.WorkshopItemStagingFolder, contentFile.Path);
//bool fileInStagingFolder = File.Exists(pathInStagingFolder);
bool fileExists = File.Exists(contentFile.Path);
var fileFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.12f), createItemFileList.Content.RectTransform) { MinSize = new Point(0, 20) },
style: "ListBoxElement")
@@ -1309,7 +1356,7 @@ namespace Barotrauma
{
Selected = fileExists && !illegalPath,
Enabled = false,
ToolTip = TextManager.Get(fileInStagingFolder ? "WorkshopItemFileIncluded" : "WorkshopItemFileNotIncluded")
ToolTip = TextManager.Get(illegalPath ? "WorkshopItemFileNotIncluded" : "WorkshopItemFileIncluded")
};
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), content.RectTransform, Anchor.CenterLeft), contentFile.Path, font: GUI.SmallFont)
@@ -1352,6 +1399,7 @@ namespace Barotrauma
itemContentPackage.RemoveFile(contentFile);
itemContentPackage.Save(itemContentPackage.Path);
RefreshCreateItemFileList();
RefreshMyItemList();
return true;
}
};
@@ -1408,8 +1456,8 @@ namespace Barotrauma
if (errorMsg == null)
{
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", TextManager.EnsureUTF8(item.Title)) + item.Error);
TextManager.Get("Error"),
TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", TextManager.EnsureUTF8(item.Title)) + " " + item.Error);
}
else
{
@@ -1418,6 +1466,7 @@ namespace Barotrauma
}
createItemFrame.ClearChildren();
RefreshItemLists();
SelectTab(Tab.Browse);
}

View File

@@ -646,8 +646,8 @@ namespace Barotrauma
SoundPlayer.OverrideMusicType = "none";
SoundPlayer.OverrideMusicDuration = null;
GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f, 0);
linkedSubBox.ClearChildren();
foreach (Submarine sub in Submarine.SavedSubmarines)
@@ -688,8 +688,8 @@ namespace Barotrauma
if (WiringMode) SetWiringMode(false);
SoundPlayer.OverrideMusicType = null;
GameMain.SoundManager.SetCategoryGainMultiplier("default", GameMain.Config.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameMain.Config.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("default", GameMain.Config.SoundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameMain.Config.SoundVolume, 0);
if (dummyCharacter != null)
{
@@ -1000,7 +1000,8 @@ namespace Barotrauma
submarineDescriptionCharacterCount = new GUITextBlock(new RectTransform(new Vector2(.5f, 1f), descriptionHeaderGroup.RectTransform), string.Empty, textAlignment: Alignment.TopRight);
var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), leftColumn.RectTransform));
descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform, Anchor.Center), font: GUI.SmallFont, wrap: true);
descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform, Anchor.Center), font: GUI.SmallFont, wrap: true, textAlignment: Alignment.TopLeft);
descriptionBox.Padding = new Vector4(10 * GUI.Scale);
descriptionBox.OnTextChanged += (textBox, text) =>
{
@@ -1189,11 +1190,13 @@ namespace Barotrauma
var contentPackList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f - contentPackagesLabel.RectTransform.RelativeSize.Y),
horizontalArea.RectTransform, Anchor.BottomRight));
List<string> contentPacks = Submarine.MainSub.RequiredContentPackages.ToList();
foreach (ContentPackage contentPack in ContentPackage.List)
{
if (!contentPacks.Contains(contentPack.Name)) contentPacks.Add(contentPack.Name);
{
//don't show content packages that only define submarine files
//(it doesn't make sense to require another sub to be installed to install this one)
if (contentPack.Files.All(cp => cp.Type == ContentType.Submarine)) { continue; }
if (!contentPacks.Contains(contentPack.Name)) { contentPacks.Add(contentPack.Name); }
}
foreach (string contentPackageName in contentPacks)
@@ -2371,11 +2374,7 @@ namespace Barotrauma
sub.UpdateTransform();
}
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
null, null, null, null,
cam.Transform);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, transformMatrix: cam.Transform);
graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f));
if (GameMain.DebugDraw)
{
@@ -2383,7 +2382,12 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, new Vector2(cam.WorldView.X, -Submarine.MainSub.HiddenSubPosition.Y), new Vector2(cam.WorldView.Right, -Submarine.MainSub.HiddenSubPosition.Y), Color.White * 0.5f, 1.0f, (int)(2.0f / cam.Zoom));
}
Submarine.Draw(spriteBatch, true);
Submarine.DrawBack(spriteBatch, editing: true);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, transformMatrix: cam.Transform);
Submarine.DrawFront(spriteBatch, editing: true);
if (!CharacterMode && !WiringMode && GUI.MouseOn == null)
{

View File

@@ -12,6 +12,9 @@ namespace Barotrauma.Sounds
//key = sample rate, value = filter
private static Dictionary<int, BiQuad> muffleFilters = new Dictionary<int, BiQuad>();
private static List<float> playbackAmplitude;
private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
public OggSound(SoundManager owner, string filename, bool stream) : base(owner, filename, stream, true)
{
if (!ToolBox.IsProperFilenameCase(filename))
@@ -32,6 +35,18 @@ namespace Barotrauma.Sounds
short[] shortBuffer = new short[bufferSize];
int readSamples = reader.ReadSamples(floatBuffer, 0, bufferSize);
playbackAmplitude = new List<float>();
for (int i=0;i<bufferSize;i+=reader.Channels*AMPLITUDE_SAMPLE_COUNT)
{
float maxAmplitude = 0.0f;
for (int j=i;j<i+reader.Channels*AMPLITUDE_SAMPLE_COUNT;j++)
{
if (j >= bufferSize) { break; }
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(floatBuffer[j]));
}
playbackAmplitude.Add(maxAmplitude);
}
CastBuffer(floatBuffer, shortBuffer, readSamples);
@@ -61,6 +76,15 @@ namespace Barotrauma.Sounds
}
}
public override float GetAmplitudeAtPlaybackPos(int playbackPos)
{
if (playbackAmplitude == null || playbackAmplitude.Count == 0) { return 0.0f; }
int index = playbackPos / AMPLITUDE_SAMPLE_COUNT;
if (index < 0) { return 0.0f; }
if (index >= playbackAmplitude.Count) { index = playbackAmplitude.Count - 1; }
return playbackAmplitude[index];
}
public override int FillStreamBuffer(int samplePos, short[] buffer)
{
if (!Stream) throw new Exception("Called FillStreamBuffer on a non-streamed sound!");

View File

@@ -179,6 +179,8 @@ namespace Barotrauma.Sounds
public abstract int FillStreamBuffer(int samplePos, short[] buffer);
public abstract float GetAmplitudeAtPlaybackPos(int playbackPos);
public virtual void Dispose()
{
if (disposed) { return; }

View File

@@ -2,6 +2,7 @@
using OpenAL;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Threading;
namespace Barotrauma.Sounds
{
@@ -79,7 +80,7 @@ namespace Barotrauma.Sounds
public class SoundChannel : IDisposable
{
private const int STREAM_BUFFER_SIZE = 65536;
private const int STREAM_BUFFER_SIZE = 8820;
private short[] streamShortBuffer;
private Vector3? position;
@@ -282,6 +283,35 @@ namespace Barotrauma.Sounds
}
}
private float streamAmplitude;
public float CurrentAmplitude
{
get
{
if (!IsPlaying) { return 0.0f; }
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
if (!IsStream)
{
int playbackPos; Al.GetSourcei(alSource, Al.SampleOffset, out playbackPos);
int alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to get source's playback position: " + Al.GetErrorString(alError));
}
return Sound.GetAmplitudeAtPlaybackPos(playbackPos);
}
else
{
float retVal = -1.0f;
Monitor.Enter(mutex);
retVal = streamAmplitude;
Monitor.Exit(mutex);
return retVal;
}
}
}
private string category;
public string Category
{
@@ -311,10 +341,12 @@ namespace Barotrauma.Sounds
private set;
}
private int streamSeekPos;
private bool startedPlaying;
private int buffersToRequeue;
private bool reachedEndSample;
private int queueStartIndex;
private readonly uint[] streamBuffers;
private readonly List<uint> emptyBuffers;
private uint[] unqueuedBuffers;
private float[] streamBufferAmplitudes;
private object mutex;
@@ -346,12 +378,17 @@ namespace Barotrauma.Sounds
FilledByNetwork = sound is VoipSound;
decayTimer = 0;
streamSeekPos = 0; reachedEndSample = false;
startedPlaying = true;
mutex = new object();
buffersToRequeue = 4;
muffled = muffle;
lock (mutex)
if (IsStream)
{
mutex = new object();
}
try
{
if (mutex != null) { Monitor.Enter(mutex); }
if (sound.Owner.CountPlayingInstances(sound) < sound.MaxSimultaneousInstances)
{
ALSourceIndex = sound.Owner.AssignFreeSourceToChannel(this);
@@ -390,7 +427,8 @@ namespace Barotrauma.Sounds
}
else
{
Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)sound.ALBuffer);
uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer;
Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer);
int alError = Al.GetError();
if (alError != Al.NoError)
{
@@ -407,7 +445,8 @@ namespace Barotrauma.Sounds
streamShortBuffer = new short[STREAM_BUFFER_SIZE];
streamBuffers = new uint[4];
emptyBuffers = new List<uint>();
unqueuedBuffers = new uint[4];
streamBufferAmplitudes = new float[4];
for (int i = 0; i < 4; i++)
{
Al.GenBuffer(out streamBuffers[i]);
@@ -423,18 +462,27 @@ namespace Barotrauma.Sounds
throw new Exception("Generated streamBuffer[" + i.ToString() + "] is invalid!");
}
}
Sound.Owner.InitStreamThread();
}
}
this.Position = position;
this.Gain = gain;
this.Looping = false;
this.Near = near;
this.Far = far;
this.Category = category;
}
}
catch
{
throw;
}
finally
{
if (mutex != null) { Monitor.Exit(mutex); }
}
Sound.Owner.Update();
}
public bool FadingOutAndDisposing;
@@ -445,8 +493,9 @@ namespace Barotrauma.Sounds
public void Dispose()
{
lock (mutex)
try
{
if (mutex != null) { Monitor.Enter(mutex); }
if (ALSourceIndex >= 0)
{
Al.SourceStop(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex));
@@ -467,19 +516,17 @@ namespace Barotrauma.Sounds
throw new Exception("Failed to stop streamed source: " + Al.GetErrorString(alError));
}
int buffersToUnqueue = 0;
uint[] unqueuedBuffers = null;
buffersToUnqueue = 0;
Al.GetSourcei(alSource, Al.BuffersProcessed, out buffersToUnqueue);
int buffersToRequeue = 0;
buffersToRequeue = 0;
Al.GetSourcei(alSource, Al.BuffersProcessed, out buffersToRequeue);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to determine processed buffers from streamed source: " + Al.GetErrorString(alError));
}
unqueuedBuffers = new uint[buffersToUnqueue];
Al.SourceUnqueueBuffers(alSource, buffersToUnqueue, unqueuedBuffers);
Al.SourceUnqueueBuffers(alSource, buffersToRequeue, unqueuedBuffers);
alError = Al.GetError();
if (alError != Al.NoError)
{
@@ -518,14 +565,19 @@ namespace Barotrauma.Sounds
ALSourceIndex = -1;
}
}
finally
{
if (mutex != null) { Monitor.Exit(mutex); }
}
}
public void UpdateStream()
{
if (!IsStream) throw new Exception("Called UpdateStream on a non-streamed sound channel!");
lock (mutex)
try
{
Monitor.Enter(mutex);
if (!reachedEndSample)
{
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
@@ -538,48 +590,38 @@ namespace Barotrauma.Sounds
{
throw new Exception("Failed to determine playing state from streamed source: " + Al.GetErrorString(alError));
}
int buffersToUnqueue = 0;
uint[] unqueuedBuffers = null;
if (!startedPlaying)
{
buffersToUnqueue = 0;
Al.GetSourcei(alSource, Al.BuffersProcessed, out buffersToUnqueue);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to determine processed buffers from streamed source: " + Al.GetErrorString(alError));
}
unqueuedBuffers = new uint[buffersToUnqueue+emptyBuffers.Count];
Al.SourceUnqueueBuffers(alSource, buffersToUnqueue, unqueuedBuffers);
for (int i = 0; i < emptyBuffers.Count; i++)
{
unqueuedBuffers[buffersToUnqueue + i] = emptyBuffers[i];
}
buffersToUnqueue += emptyBuffers.Count;
emptyBuffers.Clear();
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to unqueue buffers from streamed source: " + Al.GetErrorString(alError));
}
}
else
int unqueuedBufferCount;
Al.GetSourcei(alSource, Al.BuffersProcessed, out unqueuedBufferCount);
alError = Al.GetError();
if (alError != Al.NoError)
{
startedPlaying = false;
buffersToUnqueue = 4;
unqueuedBuffers = new uint[4];
for (int i = 0; i < 4; i++)
{
unqueuedBuffers[i] = streamBuffers[i];
}
throw new Exception("Failed to determine processed buffers from streamed source: " + Al.GetErrorString(alError));
}
Al.SourceUnqueueBuffers(alSource, unqueuedBufferCount, unqueuedBuffers);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to unqueue buffers from streamed source: " + Al.GetErrorString(alError));
}
for (int i = 0; i < buffersToUnqueue; i++)
buffersToRequeue += unqueuedBufferCount;
int iterCount = buffersToRequeue;
for (int k = 0; k < iterCount; k++)
{
int index = queueStartIndex;
short[] buffer = streamShortBuffer;
int readSamples = Sound.FillStreamBuffer(streamSeekPos, buffer);
float readAmplitude = 0.0f;
for (int i=0;i<Math.Min(readSamples, buffer.Length);i++)
{
float sampleF = ((float)buffer[i]) / ((float)short.MaxValue);
readAmplitude = Math.Max(readAmplitude, Math.Abs(sampleF));
}
if (FilledByNetwork)
{
if (Sound is VoipSound voipSound)
@@ -589,6 +631,7 @@ namespace Barotrauma.Sounds
if (readSamples <= 0)
{
streamAmplitude *= 0.5f;
decayTimer++;
if (decayTimer > 120) //TODO: replace magic number
{
@@ -618,38 +661,54 @@ namespace Barotrauma.Sounds
if (readSamples > 0)
{
Al.BufferData<short>(unqueuedBuffers[i], Sound.ALFormat, buffer, readSamples, Sound.SampleRate);
streamBufferAmplitudes[index] = readAmplitude;
Al.BufferData<short>(streamBuffers[index], Sound.ALFormat, buffer, readSamples, Sound.SampleRate);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to assign data to stream buffer: " +
Al.GetErrorString(alError) + ": " + unqueuedBuffers[i].ToString() + "/" + unqueuedBuffers.Length + ", readSamples: " + readSamples);
Al.GetErrorString(alError) + ": " + streamBuffers[index].ToString() + "/" + streamBuffers.Length + ", readSamples: " + readSamples);
}
Al.SourceQueueBuffer(alSource, unqueuedBuffers[i]);
Al.SourceQueueBuffer(alSource, streamBuffers[index]);
queueStartIndex = (queueStartIndex + 1) % 4;
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to queue buffer[" + i.ToString() + "] to stream: " + Al.GetErrorString(alError));
throw new Exception("Failed to queue streamBuffer[" + index.ToString() + "] to stream: " + Al.GetErrorString(alError));
}
}
else if (readSamples < 0)
{
reachedEndSample = true;
}
else
{
emptyBuffers.Add((uint)unqueuedBuffers[i]);
if (readSamples < 0)
{
reachedEndSample = true;
}
break;
}
buffersToRequeue--;
}
streamAmplitude = streamBufferAmplitudes[queueStartIndex];
Al.GetSourcei(alSource, Al.SourceState, out state);
if (state != Al.Playing)
{
Al.SourcePlay(alSource);
}
}
if (reachedEndSample)
{
streamAmplitude = 0.0f;
}
}
finally
{
Monitor.Exit(mutex);
}
}
}

View File

@@ -100,6 +100,47 @@ namespace Barotrauma.Sounds
}
}
}
public float PlaybackAmplitude
{
get
{
float aggregateAmplitude = 0.0f;
//NOTE: this is obviously not entirely accurate;
//It assumes a linear falloff model, and assumes that audio
//is simply added together to produce the final result.
//Adjustments may be needed under certain scenarios.
for (int i=0;i<2;i++)
{
foreach (SoundChannel soundChannel in playingChannels[i].Where(ch => ch != null))
{
float amplitude = soundChannel.CurrentAmplitude;
amplitude *= soundChannel.Gain;
float dist = Vector3.Distance(ListenerPosition, soundChannel.Position ?? ListenerPosition);
if (dist > soundChannel.Near)
{
amplitude *= 1.0f - Math.Min(1.0f, (dist - soundChannel.Near) / (soundChannel.Far - soundChannel.Near));
}
aggregateAmplitude += amplitude;
}
}
return aggregateAmplitude;
}
}
public float CompressionDynamicRangeGain { get; private set; }
private float voipAttenuatedGain;
private double lastAttenuationTime;
public float VoipAttenuatedGain
{
get { return voipAttenuatedGain; }
set
{
lastAttenuationTime = Timing.TotalTime;
voipAttenuatedGain = value;
}
}
public int LoadedSoundCount
{
@@ -110,7 +151,43 @@ namespace Barotrauma.Sounds
get { return loadedSounds.Select(s => s.Filename).Distinct().Count(); }
}
private Dictionary<string, Pair<float, bool>> categoryModifiers;
private class CategoryModifier
{
public float[] GainMultipliers;
public bool Muffle;
public CategoryModifier(int gainMultiplierIndex, float gain, bool muffle)
{
Muffle = muffle;
GainMultipliers = new float[gainMultiplierIndex+1];
for (int i=0;i<GainMultipliers.Length;i++)
{
if (i==gainMultiplierIndex)
{
GainMultipliers[i] = gain;
}
else
{
GainMultipliers[i] = 1.0f;
}
}
}
public void SetGainMultiplier(int index, float gain)
{
if (GainMultipliers.Length < index+1)
{
int oldLength = GainMultipliers.Length;
Array.Resize(ref GainMultipliers, index + 1);
for (int i=oldLength;i<GainMultipliers.Length;i++)
{
GainMultipliers[i] = 1.0f;
}
}
GainMultipliers[index] = gain;
}
}
private Dictionary<string, CategoryModifier> categoryModifiers;
public SoundManager()
{
@@ -190,6 +267,8 @@ namespace Barotrauma.Sounds
ListenerPosition = Vector3.Zero;
ListenerTargetVector = new Vector3(0.0f, 0.0f, 1.0f);
ListenerUpVector = new Vector3(0.0f, -1.0f, 0.0f);
CompressionDynamicRangeGain = 1.0f;
}
public Sound LoadSound(string filename, bool stream = false)
@@ -257,11 +336,12 @@ namespace Barotrauma.Sounds
{
if (Disabled) { return -1; }
lock (playingChannels)
//remove a channel that has stopped
//or hasn't even been assigned
int poolIndex = (int)newChannel.Sound.SourcePoolIndex;
lock (playingChannels[poolIndex])
{
//remove a channel that has stopped
//or hasn't even been assigned
int poolIndex = (int)newChannel.Sound.SourcePoolIndex;
for (int i = 0; i < playingChannels[poolIndex].Length; i++)
{
if (playingChannels[poolIndex][i] == null || !playingChannels[poolIndex][i].IsPlaying)
@@ -271,10 +351,10 @@ namespace Barotrauma.Sounds
return i;
}
}
//we couldn't get a free source to assign to this channel!
return -1;
}
//we couldn't get a free source to assign to this channel!
return -1;
}
#if DEBUG
@@ -291,7 +371,7 @@ namespace Barotrauma.Sounds
public bool IsPlaying(Sound sound)
{
if (Disabled) { return false; }
lock (playingChannels)
lock (playingChannels[(int)sound.SourcePoolIndex])
{
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
{
@@ -309,7 +389,7 @@ namespace Barotrauma.Sounds
{
if (Disabled) { return 0; }
int count = 0;
lock (playingChannels)
lock (playingChannels[(int)sound.SourcePoolIndex])
{
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
{
@@ -326,7 +406,7 @@ namespace Barotrauma.Sounds
public SoundChannel GetChannelFromSound(Sound sound)
{
if (Disabled) { return null; }
lock (playingChannels)
lock (playingChannels[(int)sound.SourcePoolIndex])
{
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
{
@@ -343,7 +423,7 @@ namespace Barotrauma.Sounds
public void KillChannels(Sound sound)
{
if (Disabled) { return; }
lock (playingChannels)
lock (playingChannels[(int)sound.SourcePoolIndex])
{
for (int i = 0; i < playingChannels[(int)sound.SourcePoolIndex].Length; i++)
{
@@ -371,22 +451,23 @@ namespace Barotrauma.Sounds
}
}
public void SetCategoryGainMultiplier(string category, float gain)
public void SetCategoryGainMultiplier(string category, float gain, int index=0)
{
if (Disabled) { return; }
category = category.ToLower();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, Pair<float, bool>>();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, CategoryModifier>();
if (!categoryModifiers.ContainsKey(category))
{
categoryModifiers.Add(category, new Pair<float, bool>(gain, false));
categoryModifiers.Add(category, new CategoryModifier(index, gain, false));
}
else
{
categoryModifiers[category].First = gain;
categoryModifiers[category].SetGainMultiplier(index, gain);
}
lock (playingChannels)
for (int i = 0; i < playingChannels.Length; i++)
{
for (int i = 0; i < playingChannels.Length; i++)
lock (playingChannels[i])
{
for (int j = 0; j < playingChannels[i].Length; j++)
{
@@ -399,12 +480,24 @@ namespace Barotrauma.Sounds
}
}
public float GetCategoryGainMultiplier(string category)
public float GetCategoryGainMultiplier(string category, int index=-1)
{
if (Disabled) { return 0.0f; }
category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) return 1.0f;
return categoryModifiers[category].First;
if (index < 0)
{
float accumulatedMultipliers = 1.0f;
for (int i=0;i<categoryModifiers[category].GainMultipliers.Length;i++)
{
accumulatedMultipliers *= categoryModifiers[category].GainMultipliers[i];
}
return accumulatedMultipliers;
}
else
{
return categoryModifiers[category].GainMultipliers[index];
}
}
public void SetCategoryMuffle(string category,bool muffle)
@@ -413,23 +506,26 @@ namespace Barotrauma.Sounds
category = category.ToLower();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, Pair<float, bool>>();
if (categoryModifiers == null) categoryModifiers = new Dictionary<string, CategoryModifier>();
if (!categoryModifiers.ContainsKey(category))
{
categoryModifiers.Add(category, new Pair<float, bool>(1.0f, muffle));
categoryModifiers.Add(category, new CategoryModifier(0, 1.0f, muffle));
}
else
{
categoryModifiers[category].Second = muffle;
categoryModifiers[category].Muffle = muffle;
}
for (int i = 0; i < playingChannels.Length; i++)
{
for (int j = 0; j < playingChannels[i].Length; j++)
lock (playingChannels[i])
{
if (playingChannels[i][j] != null && playingChannels[i][j].IsPlaying)
for (int j = 0; j < playingChannels[i].Length; j++)
{
if (playingChannels[i][j].Category.ToLower() == category) playingChannels[i][j].Muffled = muffle;
if (playingChannels[i][j] != null && playingChannels[i][j].IsPlaying)
{
if (playingChannels[i][j].Category.ToLower() == category) playingChannels[i][j].Muffled = muffle;
}
}
}
}
@@ -441,7 +537,45 @@ namespace Barotrauma.Sounds
category = category.ToLower();
if (categoryModifiers == null || !categoryModifiers.ContainsKey(category)) return false;
return categoryModifiers[category].Second;
return categoryModifiers[category].Muffle;
}
public void Update()
{
if (GameMain.Client != null && GameMain.Config.VoipAttenuationEnabled)
{
if (Timing.TotalTime > lastAttenuationTime+0.2)
{
voipAttenuatedGain = voipAttenuatedGain * 0.9f + 0.1f;
}
}
else
{
voipAttenuatedGain = 1.0f;
}
SetCategoryGainMultiplier("default", VoipAttenuatedGain, 1);
SetCategoryGainMultiplier("ui", VoipAttenuatedGain, 1);
SetCategoryGainMultiplier("waterambience", VoipAttenuatedGain, 1);
SetCategoryGainMultiplier("music", VoipAttenuatedGain, 1);
if (GameMain.Config.DynamicRangeCompressionEnabled)
{
float targetGain = (Math.Min(1.0f, 1.0f / PlaybackAmplitude) - 1.0f) * 0.5f + 1.0f;
if (targetGain < CompressionDynamicRangeGain)
{
//if the target gain is lower than the current gain, lower the current gain immediately to prevent clipping
CompressionDynamicRangeGain = targetGain;
}
else
{
//otherwise, let it rise back smoothly
CompressionDynamicRangeGain = (targetGain) * 0.05f + CompressionDynamicRangeGain * 0.95f;
}
}
else
{
CompressionDynamicRangeGain = 1.0f;
}
}
public void InitStreamThread()
@@ -464,9 +598,10 @@ namespace Barotrauma.Sounds
while (areStreamsPlaying)
{
areStreamsPlaying = false;
lock (playingChannels)
for (int i = 0; i < playingChannels.Length; i++)
{
for (int i = 0; i < playingChannels.Length; i++)
lock (playingChannels[i])
{
for (int j = 0; j < playingChannels[i].Length; j++)
{
@@ -497,14 +632,14 @@ namespace Barotrauma.Sounds
Thread.Sleep(10); //TODO: use a separate thread for network audio?
}
}
public void Dispose()
{
if (Disabled) { return; }
lock (playingChannels)
for (int i = 0; i < playingChannels.Length; i++)
{
for (int i = 0; i < playingChannels.Length; i++)
lock (playingChannels[i])
{
for (int j = 0; j < playingChannels[i].Length; j++)
{
@@ -512,6 +647,7 @@ namespace Barotrauma.Sounds
}
}
}
streamingThread?.Join();
for (int i = loadedSounds.Count - 1; i >= 0; i--)
{

View File

@@ -393,19 +393,19 @@ namespace Barotrauma
if (Math.Abs(diff.X) < FireSoundRange && Math.Abs(diff.Y) < FireSoundRange)
{
Vector2 diffLeft = (fs.WorldPosition + new Vector2(fs.Size.X, fs.Size.Y / 2)) - listenerPos;
if (diff.X < fs.Size.X / 2.0f) diff.X = 0.0f;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffLeft.X = 0.0f; }
if (diffLeft.X <= 0)
{
float distFallOffLeft = diffLeft.Length() / FireSoundRange;
if (distFallOffLeft < 0.99f)
{
fireVolumeLeft[0] += (1.0f - distFallOffLeft) * (fs.Size.X / FireSoundLargeLimit);
fireVolumeLeft[0] += (1.0f - distFallOffLeft);
if (fs.Size.X > FireSoundLargeLimit) fireVolumeLeft[1] += (1.0f - distFallOffLeft) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit);
}
}
Vector2 diffRight = (fs.WorldPosition + new Vector2(0.0f, fs.Size.Y / 2)) - listenerPos;
if (diff.X < fs.Size.X / 2.0f) diff.X = 0.0f;
if (Math.Abs(diff.X) < fs.Size.X / 2.0f) { diffRight.X = 0.0f; }
if (diffRight.X >= 0)
{
float distFallOffRight = diffRight.Length() / FireSoundRange;

View File

@@ -32,6 +32,11 @@ namespace Barotrauma.Sounds
video = vid;
}
public override float GetAmplitudeAtPlaybackPos(int playbackPos)
{
throw new NotImplementedException();
}
public override bool IsPlaying()
{
bool retVal = false;
@@ -65,8 +70,15 @@ namespace Barotrauma.Sounds
SoundChannel chn = null;
lock (mutex)
{
if (soundChannel != null) soundChannel.Dispose();
chn = new SoundChannel(this, gain, null, 1.0f, 3.0f, "video", false);
if (soundChannel != null)
{
soundChannel.Dispose();
soundChannel = null;
}
}
chn = new SoundChannel(this, gain, null, 1.0f, 3.0f, "video", false);
lock (mutex)
{
soundChannel = chn;
}
return chn;

View File

@@ -25,7 +25,7 @@ namespace Barotrauma.Sounds
}
private VoipQueue queue;
public int bufferID = 0;
private int bufferID = 0;
private SoundChannel soundChannel;
@@ -54,6 +54,11 @@ namespace Barotrauma.Sounds
}
}
public float CurrentAmplitude
{
get { return soundChannel?.CurrentAmplitude ?? 0.0f; }
}
public VoipSound(SoundManager owner, VoipQueue q) : base(owner, "voip", true, true)
{
VoipConfig.SetupEncoding();
@@ -70,6 +75,11 @@ namespace Barotrauma.Sounds
soundChannel = chn;
}
public override float GetAmplitudeAtPlaybackPos(int playbackPos)
{
throw new NotImplementedException(); //TODO: implement?
}
public void SetPosition(Vector3? pos)
{
soundChannel.Position = pos;

View File

@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
namespace Barotrauma
{
@@ -75,14 +76,13 @@ namespace Barotrauma
}
}
public void ReloadTexture()
{
var sprites = LoadedSprites.Where(s => s.Texture == texture).ToList();
texture.Dispose();
texture = null;
public void ReloadTexture(bool updateAllSprites = false) => ReloadTexture(updateAllSprites ? LoadedSprites.Where(s => s.Texture == texture) : new Sprite[] { this });
public void ReloadTexture(IEnumerable<Sprite> spritesToUpdate)
{
texture.Dispose();
texture = TextureLoader.FromFile(FilePath, preMultipliedAlpha);
foreach (Sprite sprite in sprites)
foreach (Sprite sprite in spritesToUpdate)
{
sprite.texture = texture;
}

View File

@@ -100,15 +100,19 @@ namespace Barotrauma
return Color.Lerp(gradient[(int)scaledT], gradient[(int)Math.Min(scaledT + 1, gradient.Length - 1)], (scaledT - (int)scaledT));
}
public static string WrapText(string text, float lineLength, ScalableFont font, float textScale = 1.0f) //TODO: could integrate this into the ScalableFont class directly
public static string WrapText(string text, float lineLength, ScalableFont font, float textScale = 1.0f, bool playerInput = false) //TODO: could integrate this into the ScalableFont class directly
{
Vector2 textSize = font.MeasureString(text);
if (textSize.X < lineLength) { return text; }
text = text.Replace("\n", " \n ");
if (!playerInput)
{
text = text.Replace("\n", " \n ");
}
List<string> words = new List<string>();
string currWord = "";
for (int i = 0; i < text.Length; i++)
{
if (TextManager.IsCJK(text[i].ToString()))
@@ -127,6 +131,7 @@ namespace Barotrauma
words.Add(currWord);
currWord = "";
}
words.Add(string.Empty);
}
else
{
@@ -144,69 +149,128 @@ namespace Barotrauma
Vector2 spaceSize = font.MeasureString(" ") * textScale;
for (int i = 0; i < words.Count; ++i)
{
if (words[i].Length == 0)
string currentWord = words[i];
if (currentWord.Length == 0)
{
//space
// space
currentWord = " ";
}
else if (string.IsNullOrWhiteSpace(words[i]) && words[i] != "\n")
else if (string.IsNullOrWhiteSpace(currentWord) && currentWord != "\n")
{
continue;
}
Vector2 size = words[i].Length == 0 ? spaceSize : font.MeasureString(words[i]) * textScale;
Vector2 size = words[i].Length == 0 ? spaceSize : font.MeasureString(currentWord) * textScale;
if (size.X > lineLength)
{
if (linePos == 0.0f)
float splitSize = 0.0f;
List<string> splitWord = new List<string>() { string.Empty };
int k = 0;
for (int j = 0; j < currentWord.Length; j++)
{
wrappedText.AppendLine(words[i]);
}
else
{
do
splitWord[k] += currentWord[j];
splitSize += (font.MeasureString(currentWord[j].ToString()) * textScale).X;
if (splitSize + linePos > lineLength)
{
if (words[i].Length == 0) break;
wrappedText.Append(words[i][0]);
words[i] = words[i].Remove(0, 1);
linePos += size.X;
} while (words[i].Length > 0 && (size = font.MeasureString((words[i][0]).ToString()) * textScale).X + linePos < lineLength);
wrappedText.Append("\n");
linePos = 0.0f;
i--;
linePos = splitSize = 0.0f;
splitWord[k] = splitWord[k].Remove(splitWord[k].Length - 1) + "\n";
j--;
splitWord.Add(string.Empty);
k++;
}
}
continue;
}
if (linePos + size.X < lineLength)
{
wrappedText.Append(words[i]);
if (words[i] == "\n")
for (int j = 0; j < splitWord.Count; j++)
{
linePos = 0.0f;
}
else
{
linePos += size.X + spaceSize.X;
wrappedText.Append(splitWord[j]);
}
linePos = splitSize;
}
else
{
wrappedText.Append("\n");
wrappedText.Append(words[i]);
if (linePos + size.X < lineLength)
{
wrappedText.Append(currentWord);
if (currentWord == "\n")
{
linePos = 0.0f;
}
else
{
linePos += size.X;
}
}
else
{
wrappedText.Append("\n");
wrappedText.Append(currentWord);
linePos = size.X + spaceSize.X;
}
if (i < words.Count - 1 && !TextManager.IsCJK(words[i]) && !TextManager.IsCJK(words[i + 1]))
{
wrappedText.Append(" ");
linePos = size.X;
}
}
}
return wrappedText.ToString().Replace(" \n ", "\n");
}
if (!playerInput)
{
return wrappedText.ToString().Replace(" \n ", "\n");
}
else
{
return wrappedText.ToString();
}
}
public static void ParseConnectCommand(string[] args, out string name, out string endpoint, out UInt64 lobbyId)
{
name = null; endpoint = null; lobbyId = 0;
for (int i = 0; i < args.Length - 1; i++)
{
if (i < args.Length-2 && args[i].Trim().ToLower().Equals("-connect", StringComparison.InvariantCultureIgnoreCase))
{
int j = i + 2;
name = "";
if (args[i + 1].Trim()[0] == '"')
{
name = args[i + 1].Trim().Substring(1);
if (!(name[name.Length - 1] == '"' && (name.Length < 2 || name[name.Length - 1] != '\\')))
{
for (; j < args.Length - 1; j++)
{
name += " " + args[j].Trim();
if (name[name.Length - 1] == '"' && (name.Length < 2 || name[name.Length - 1] != '\\'))
{
name = name.Substring(0, name.Length - 1).Replace("\\\"", "\"");
j++;
break;
}
}
}
else
{
name = name.Substring(0, name.Length - 1);
}
}
else
{
name = args[i + 1].Trim();
}
endpoint = args[j].Trim();
break;
}
else if (args[i].Trim().ToLower().Equals("+connect_lobby", StringComparison.InvariantCultureIgnoreCase))
{
UInt64.TryParse(args[i + 1].Trim(), out lobbyId);
endpoint = null;
name = null;
break;
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More