v0.10.5.1

This commit is contained in:
Juan Pablo Arce
2020-09-22 11:31:56 -03:00
parent 44032d0ae0
commit 0002ad2c50
343 changed files with 12276 additions and 5023 deletions
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,12 +8,13 @@ namespace Barotrauma.Networking
{
partial class BannedPlayer
{
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string ip, ulong steamID)
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID)
{
this.Name = name;
this.EndPoint = endPoint;
this.SteamID = steamID;
ParseEndPointAsSteamId();
this.IsRangeBan = isRangeBan;
this.IP = ip;
this.UniqueIdentifier = uniqueIdentifier;
}
}
@@ -66,13 +68,13 @@ namespace Barotrauma.Networking
RelativeSpacing = 0.02f
};
string ip = bannedPlayer.IP;
if (localRangeBans.Contains(bannedPlayer.UniqueIdentifier)) ip = ToRange(ip);
string endPoint = bannedPlayer.EndPoint;
if (localRangeBans.Contains(bannedPlayer.UniqueIdentifier)) endPoint = ToRange(endPoint);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), topArea.RectTransform),
bannedPlayer.Name + " (" + ip + ")");
bannedPlayer.Name + " (" + endPoint + ")");
textBlock.RectTransform.MinSize = new Point(textBlock.Rect.Width, 0);
if (bannedPlayer.IP.IndexOf(".x") <= -1)
if (bannedPlayer.EndPoint.IndexOf(".x") <= -1)
{
var rangeBanButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.4f), topArea.RectTransform),
TextManager.Get("BanRange"), style: "GUIButtonSmall")
@@ -156,19 +158,19 @@ namespace Barotrauma.Networking
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits();
string ip = "";
string endPoint = "";
UInt64 steamID = 0;
if (isOwner)
{
ip = incMsg.ReadString();
endPoint = incMsg.ReadString();
steamID = incMsg.ReadUInt64();
}
else
{
ip = "IP concealed by host";
endPoint = "Endpoint concealed by host";
steamID = 0;
}
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, ip, steamID));
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID));
}
if (banFrame != null)
@@ -520,11 +520,13 @@ namespace Barotrauma.Networking
msgBox.Content.Parent.RectTransform.MinSize = new Point(0, (int)(msgBox.Content.RectTransform.MinSize.Y / msgBox.Content.RectTransform.RelativeSize.Y));
var okButton = msgBox.Buttons[0];
okButton.OnClicked += msgBox.Close;
var cancelButton = msgBox.Buttons[1];
cancelButton.OnClicked += msgBox.Close;
okButton.OnClicked += (GUIButton button, object obj) =>
{
clientPeer.SendPassword(passwordBox.Text);
clientPeer?.SendPassword(passwordBox.Text);
requiresPw = false;
return true;
};
@@ -855,22 +857,6 @@ namespace Barotrauma.Networking
traitorResults.Add(new TraitorMissionResult(inc));
}
if (GameMain.GameSession?.GameMode is CampaignMode mpCampaign)
{
if (inc.ReadBoolean())
{
Dictionary<string, int> clientUpgrades = UpgradeManager.GetMetadataLevels(mpCampaign.CampaignMetadata);
Dictionary<string, int> serverUpgrades = new Dictionary<string, int>();
int length = inc.ReadUInt16();
for (int i = 0; i < length; i++)
{
serverUpgrades.Add(inc.ReadString(), inc.ReadByte());
}
UpgradeManager.CompareUpgrades(clientUpgrades, serverUpgrades);
}
}
roundInitStatus = RoundInitStatus.Interrupted;
CoroutineManager.StartCoroutine(EndGame(endMessage, traitorResults, transitionType), "EndGame");
break;
@@ -934,7 +920,7 @@ namespace Barotrauma.Networking
private void ReadStartGameFinalize(IReadMessage inc)
{
TaskPool.ListTasks(null);
TaskPool.ListTasks();
ushort contentToPreloadCount = inc.ReadUInt16();
List<ContentFile> contentToPreload = new List<ContentFile>();
for (int i = 0; i < contentToPreloadCount; i++)
@@ -1006,8 +992,19 @@ namespace Barotrauma.Networking
}
private void OnDisconnect()
private void OnDisconnect(bool disableReconnect)
{
CoroutineManager.StopCoroutines("WaitForStartingInfo");
reconnectBox?.Close();
reconnectBox = null;
GameMain.Config.RestoreBackupPackages();
GUI.ClearCursorWait();
if (disableReconnect) { allowReconnect = false; }
if (!this.allowReconnect) { CancelConnect(); }
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
@@ -1107,8 +1104,9 @@ namespace Barotrauma.Networking
reconnectBox?.Close();
reconnectBox = new GUIMessageBox(
TextManager.Get("ConnectionLost"),
msg, new string[0]);
TextManager.Get("ConnectionLost"), msg,
new string[] { TextManager.Get("Cancel") });
reconnectBox.Buttons[0].OnClicked += (btn, userdata) => { CancelConnect(); return true; };
connected = false;
ConnectToServer(serverEndpoint, serverName);
}
@@ -1385,6 +1383,7 @@ namespace Barotrauma.Networking
if (gameMode == null)
{
DebugConsole.ThrowError("Game mode \"" + modeIdentifier + "\" not found!");
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1415,11 +1414,13 @@ namespace Barotrauma.Networking
int missionIndex = inc.ReadInt16();
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
if (!GameMain.NetLobbyScreen.TrySelectSub(shuttleName, shuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox))
{
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Success;
}
@@ -1448,6 +1449,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.Select();
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectSub" + subName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
if (GameMain.NetLobbyScreen.SelectedShuttle == null ||
@@ -1459,6 +1461,7 @@ namespace Barotrauma.Networking
string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ").";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectShuttle" + shuttleName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1487,6 +1490,7 @@ namespace Barotrauma.Networking
gameStarted = true;
DebugConsole.ThrowError(errorMsg);
GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
else if (campaign.Map == null)
@@ -1495,6 +1499,7 @@ namespace Barotrauma.Networking
gameStarted = true;
DebugConsole.ThrowError(errorMsg);
GameMain.NetLobbyScreen.Select();
roundInitStatus = RoundInitStatus.Interrupted;
yield return CoroutineStatus.Failure;
}
@@ -1515,6 +1520,11 @@ namespace Barotrauma.Networking
}
}
if (GameMain.Client?.ServerSettings?.Voting != null)
{
GameMain.Client.ServerSettings.Voting.ResetVotes(GameMain.Client.ConnectedClients);
}
if (loadTask != null)
{
while (!loadTask.IsCompleted && !loadTask.IsFaulted && !loadTask.IsCanceled)
@@ -1626,7 +1636,10 @@ namespace Barotrauma.Networking
}
}
if (respawnAllowed) { respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); }
if (respawnAllowed)
{
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && gameMode != GameModePreset.MultiPlayerCampaign ? GameMain.NetLobbyScreen.SelectedShuttle : null);
}
gameStarted = true;
ServerSettings.ServerDetailsChanged = true;
@@ -1645,6 +1658,17 @@ namespace Barotrauma.Networking
public IEnumerable<object> EndGame(string endMessage, List<TraitorMissionResult> traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{
//round starting up, wait for it to finish
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 60);
while (TaskPool.IsTaskRunning("AsyncCampaignStartRound"))
{
if (DateTime.Now > timeOut)
{
throw new Exception("Failed to end a round (async campaign round start timed out).");
}
yield return new WaitForSeconds(1.0f);
}
if (!gameStarted)
{
GameMain.NetLobbyScreen.Select();
@@ -2086,7 +2110,6 @@ namespace Barotrauma.Networking
{
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
{
bool eventReadFailed = false;
switch (objHeader)
{
case ServerNetObject.SYNC_IDS:
@@ -2110,7 +2133,13 @@ namespace Barotrauma.Networking
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
var entity = Entity.FindEntityByID(id) as IServerSerializable;
if (entity != null)
if (msgEndPos > inc.LengthBits)
{
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
return;
}
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
{
entity.ClientRead(objHeader.Value, inc, sendingTime);
}
@@ -2127,8 +2156,7 @@ namespace Barotrauma.Networking
case ServerNetObject.ENTITY_EVENT_INITIAL:
if (!entityEventManager.Read(objHeader.Value, inc, sendingTime, entities))
{
eventReadFailed = true;
break;
return;
}
break;
case ServerNetObject.CHAT_MESSAGE:
@@ -2138,16 +2166,11 @@ namespace Barotrauma.Networking
throw new Exception($"Unknown object header \"{objHeader}\"!)");
}
prevBitLength = inc.BitPosition - prevBitPos;
prevByteLength = inc.BytePosition - prevByteLength;
prevByteLength = inc.BytePosition - prevBytePos;
prevObjHeader = objHeader;
prevBitPos = inc.BitPosition;
prevBytePos = inc.BytePosition;
if (eventReadFailed)
{
break;
}
}
}
@@ -2618,7 +2641,7 @@ namespace Barotrauma.Networking
public void VoteForKick(Client votedClient)
{
if (votedClient == null) { return; }
votedClient.AddKickVote(ConnectedClients.First(c => c.ID == ID));
votedClient.AddKickVote(ConnectedClients.FirstOrDefault(c => c.ID == myID));
Vote(VoteType.Kick, votedClient);
}
@@ -164,6 +164,19 @@ namespace Barotrauma.Networking
for (int i = 0; i < eventCount; i++)
{
//16 = entity ID, 8 = msg length
if (msg.BitPosition + 16 + 8 > msg.LengthBits)
{
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
errorMsg += "\nPrevious entities:";
for (int j = entities.Count - 1; j >= 0; j--)
{
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
}
DebugConsole.ThrowError(errorMsg);
return false;
}
UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
UInt16 entityID = msg.ReadUInt16();
@@ -176,7 +189,7 @@ namespace Barotrauma.Networking
}
msg.ReadPadBits();
entities.Add(null);
if (thisEventID == (UInt16)(lastReceivedID + 1)) lastReceivedID++;
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
continue;
}
@@ -248,10 +261,8 @@ namespace Barotrauma.Networking
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
}
if (GameSettings.VerboseLogging)
{
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
}
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
msg.BitPosition = (int)(msgPosition + msgLength * 8);
@@ -1,13 +1,56 @@
using System;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Barotrauma.Networking
{
abstract class ClientPeer
{
protected class ServerContentPackage
{
public string Name;
public string Hash;
public UInt64 WorkshopId;
public ContentPackage RegularPackage
{
get
{
return ContentPackage.RegularPackages.Find(p => p.MD5hash.Hash.Equals(Hash));
}
}
public ContentPackage CorePackage
{
get
{
return ContentPackage.CorePackages.Find(p => p.MD5hash.Hash.Equals(Hash));
}
}
public ServerContentPackage(string name, string hash, UInt64 workshopId)
{
Name = name;
Hash = hash;
WorkshopId = workshopId;
}
}
protected string GetPackageStr(ContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + contentPackage.MD5hash.ShortHash + ")";
}
protected string GetPackageStr(ServerContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + Md5Hash.GetShortHash(contentPackage.Hash) + ")";
}
public delegate void MessageCallback(IReadMessage message);
public delegate void DisconnectCallback();
public delegate void DisconnectCallback(bool disableReconnect);
public delegate void DisconnectMessageCallback(string message);
public delegate void PasswordCallback(int salt, int retries);
public delegate void InitializationCompleteCallback();
@@ -25,11 +68,150 @@ namespace Barotrauma.Networking
public NetworkConnection ServerConnection { get; protected set; }
public abstract void Start(object endPoint, int ownerKey);
public abstract void Close(string msg = null);
public abstract void Close(string msg = null, bool disableReconnect = false);
public abstract void Update(float deltaTime);
public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod);
public abstract void SendPassword(string password);
protected abstract void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg);
protected ConnectionInitialization initializationStep;
protected bool contentPackageOrderReceived;
protected int ownerKey = 0;
protected int passwordSalt;
protected Steamworks.AuthTicket steamAuthTicket;
protected void ReadConnectionInitializationStep(IReadMessage inc)
{
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
IWriteMessage outMsg;
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
outMsg = new WriteOnlyMessage();
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());
outMsg.Write(GameMain.Config.Language);
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
string serverName = inc.ReadString();
UInt32 cpCount = inc.ReadVariableUInt32();
ServerContentPackage corePackage = null;
List<ServerContentPackage> regularPackages = new List<ServerContentPackage>();
List<ServerContentPackage> missingPackages = new List<ServerContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string name = inc.ReadString();
string hash = inc.ReadString();
UInt64 workshopId = inc.ReadUInt64();
var pkg = new ServerContentPackage(name, hash, workshopId);
if (pkg.CorePackage != null)
{
corePackage = pkg;
}
else if (pkg.RegularPackage != null)
{
regularPackages.Add(pkg);
}
else
{
missingPackages.Add(pkg);
}
}
if (missingPackages.Count > 0)
{
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
if (nonDownloadable.Any())
{
string disconnectMsg;
if (nonDownloadable.Count() == 1)
{
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
}
else
{
List<string> packageStrs = new List<string>();
nonDownloadable.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}";
}
Close(disconnectMsg, disableReconnect: true);
}
else
{
Close(disableReconnect: true);
string missingModNames = "\n- " + string.Join("\n\n- ", missingPackages.Select(p => GetPackageStr(p))) + "\n\n";
var msgBox = new GUIMessageBox(
TextManager.Get("WorkshopItemDownloadTitle"),
TextManager.GetWithVariable("WorkshopItemDownloadPrompt", "[items]", missingModNames),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
{
GameMain.ServerListScreen.Select();
GameMain.ServerListScreen.DownloadWorkshopItems(missingPackages.Select(p => p.WorkshopId), serverName, ServerConnection.EndPointString);
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
return;
}
if (!contentPackageOrderReceived)
{
GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList());
contentPackageOrderReceived = true;
}
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
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;
}
}
#if DEBUG
public abstract void ForceTimeOut();
#endif
@@ -14,11 +14,6 @@ namespace Barotrauma.Networking
private NetClient netClient;
private NetPeerConfiguration netPeerConfiguration;
private ConnectionInitialization initializationStep;
private bool contentPackageOrderReceived;
private int ownerKey;
private int passwordSalt;
private Steamworks.AuthTicket steamAuthTicket;
List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenClientPeer(string name)
@@ -128,7 +123,7 @@ namespace Barotrauma.Networking
if (isConnectionInitializationStep && initializationStep != ConnectionInitialization.Success)
{
ReadConnectionInitializationStep(inc);
ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
}
else
{
@@ -158,99 +153,6 @@ namespace Barotrauma.Networking
}
}
private void ReadConnectionInitializationStep(NetIncomingMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
//Console.WriteLine(step + " " + initializationStep);
NetOutgoingMessage outMsg; NetSendResult result;
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
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);
}
result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send "+initializationStep.ToString()+" message to host: " + result);
}
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = netClient.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
Int32 cpCount = inc.ReadVariableInt32();
List<ContentPackage> serverContentPackages = new List<ContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string hash = inc.ReadString();
serverContentPackages.Add(GameMain.Config.SelectedContentPackages.Find(cp => cp.MD5hash.Hash == hash));
}
if (!contentPackageOrderReceived)
{
GameMain.Config.ReorderSelectedContentPackages(cp => serverContentPackages.Contains(cp) ?
serverContentPackages.IndexOf(cp) :
serverContentPackages.Count + GameMain.Config.SelectedContentPackages.IndexOf(cp));
contentPackageOrderReceived = true;
}
result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send " + initializationStep.ToString() + " message to host: " + result);
}
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; }
@@ -269,7 +171,7 @@ namespace Barotrauma.Networking
}
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -278,7 +180,7 @@ namespace Barotrauma.Networking
netClient.Shutdown(msg ?? TextManager.Get("Disconnecting"));
netClient = null;
steamAuthTicket?.Cancel(); steamAuthTicket = null;
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
}
public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod)
@@ -320,6 +222,18 @@ namespace Barotrauma.Networking
}
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
NetSendResult result = netClient.SendMessage(lidgrenMsg, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
{
DebugConsole.NewMessage("Failed to send message to host: " + result + "\n" + Environment.StackTrace);
}
}
#if DEBUG
public override void ForceTimeOut()
{
@@ -12,10 +12,6 @@ namespace Barotrauma.Networking
{
private bool isActive;
private UInt64 hostSteamId;
private ConnectionInitialization initializationStep;
private bool contentPackageOrderReceived;
private int passwordSalt;
private Steamworks.AuthTicket steamAuthTicket;
private double timeout;
private double heartbeatTimer;
private double connectionStatusTimer;
@@ -89,6 +85,7 @@ namespace Barotrauma.Networking
Steamworks.SteamNetworking.AcceptP2PSessionWithUser(steamId);
}
else if (initializationStep != ConnectionInitialization.Password &&
initializationStep != ConnectionInitialization.ContentPackageOrder &&
initializationStep != ConnectionInitialization.Success)
{
DebugConsole.ThrowError($"Connection from incorrect SteamID was rejected: "+
@@ -105,7 +102,7 @@ namespace Barotrauma.Networking
OnDisconnectMessageReceived?.Invoke($"SteamP2P connection failed: {error}");
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength, int channel)
private void OnP2PData(ulong steamId, byte[] data, int dataLength)
{
if (!isActive) { return; }
if (steamId != hostSteamId) { return; }
@@ -165,6 +162,7 @@ namespace Barotrauma.Networking
heartbeatTimer -= deltaTime;
if (initializationStep != ConnectionInitialization.Password &&
initializationStep != ConnectionInitialization.ContentPackageOrder &&
initializationStep != ConnectionInitialization.Success)
{
connectionStatusTimer -= deltaTime;
@@ -194,7 +192,7 @@ namespace Barotrauma.Networking
var packet = Steamworks.SteamNetworking.ReadP2PPacket();
if (packet.HasValue)
{
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0);
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0);
receivedBytes += packet?.Data.Length ?? 0;
}
}
@@ -249,88 +247,6 @@ namespace Barotrauma.Networking
incomingDataMessages.Clear();
}
private void ReadConnectionInitializationStep(IReadMessage inc)
{
if (!isActive) { return; }
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
IWriteMessage outMsg;
//DebugConsole.NewMessage(step + " " + initializationStep);
switch (step)
{
case ConnectionInitialization.SteamTicketAndVersion:
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
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;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
break;
case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
outMsg = new WriteOnlyMessage();
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
UInt32 cpCount = inc.ReadVariableUInt32();
List<ContentPackage> serverContentPackages = new List<ContentPackage>();
for (int i = 0; i < cpCount; i++)
{
string hash = inc.ReadString();
serverContentPackages.Add(GameMain.Config.SelectedContentPackages.Find(cp => cp.MD5hash.Hash == hash));
}
if (!contentPackageOrderReceived)
{
GameMain.Config.ReorderSelectedContentPackages(cp => serverContentPackages.Contains(cp) ?
serverContentPackages.IndexOf(cp) :
serverContentPackages.Count + GameMain.Config.SelectedContentPackages.IndexOf(cp));
contentPackageOrderReceived = true;
}
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
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; }
@@ -339,8 +255,7 @@ namespace Barotrauma.Networking
buf[0] = (byte)deliveryMethod;
byte[] bufAux = new byte[msg.LengthBytes];
bool isCompressed; int length;
msg.PrepareForSending(ref bufAux, out isCompressed, out length);
msg.PrepareForSending(ref bufAux, out bool isCompressed, out int length);
buf[1] = (byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None);
@@ -426,7 +341,7 @@ namespace Barotrauma.Networking
sentBytes += outMsg.LengthBytes;
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -451,7 +366,7 @@ namespace Barotrauma.Networking
steamAuthTicket?.Cancel(); steamAuthTicket = null;
hostSteamId = 0;
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
}
~SteamP2PClientPeer()
@@ -460,6 +375,31 @@ namespace Barotrauma.Networking
Close();
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
Steamworks.P2PSend 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 = Steamworks.P2PSend.Reliable;
break;
default:
sendType = Steamworks.P2PSend.Unreliable;
break;
}
IWriteMessage msgToSend = new WriteOnlyMessage();
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
heartbeatTimer = 5.0;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, msgToSend.Buffer, msgToSend.LengthBytes, 0, sendType);
sentBytes += msg.LengthBytes;
}
#if DEBUG
public override void ForceTimeOut()
{
@@ -1,8 +1,6 @@
using System;
using Barotrauma.Steam;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Barotrauma.Steam;
using System.Linq;
using System.Threading;
@@ -12,7 +10,6 @@ namespace Barotrauma.Networking
{
private bool isActive;
private ConnectionInitialization initializationStep;
private readonly UInt64 selfSteamID;
private long sentBytes, receivedBytes;
@@ -61,7 +58,7 @@ namespace Barotrauma.Networking
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
ServerConnection = new PipeConnection();
ServerConnection = new PipeConnection(selfSteamID);
ServerConnection.Status = NetworkConnectionStatus.Connected;
remotePeers = new List<RemotePeer>();
@@ -161,6 +158,7 @@ namespace Barotrauma.Networking
remotePeer.Authenticating = true;
authMsg.ReadString(); //skip name
authMsg.ReadInt32(); //skip owner key
authMsg.ReadUInt64(); //skip steamid
UInt16 ticketLength = authMsg.ReadUInt16();
byte[] ticket = authMsg.ReadBytes(ticketLength);
@@ -395,7 +393,7 @@ namespace Barotrauma.Networking
return; //owner doesn't send passwords
}
public override void Close(string msg = null)
public override void Close(string msg = null, bool disableReconnect = false)
{
if (!isActive) { return; }
@@ -415,7 +413,7 @@ namespace Barotrauma.Networking
ChildServerRelay.ClosePipes();
OnDisconnect?.Invoke();
OnDisconnect?.Invoke(disableReconnect);
SteamManager.LeaveLobby();
Steamworks.SteamNetworking.ResetActions();
@@ -445,6 +443,12 @@ namespace Barotrauma.Networking
Close();
}
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
{
//not currently used by SteamP2POwnerPeer
throw new NotImplementedException();
}
#if DEBUG
public override void ForceTimeOut()
{
@@ -73,26 +73,25 @@ namespace Barotrauma.Networking
get;
private set;
} = new List<string>();
public List<string> ContentPackageWorkshopUrls
public List<ulong> ContentPackageWorkshopIds
{
get;
private set;
} = new List<string>();
} = new List<ulong>();
public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages)
public bool ContentPackagesMatch()
{
var myContentPackages = ContentPackage.AllPackages;
//make sure we have all the packages the server requires
foreach (string hash in ContentPackageHashes)
if (ContentPackageHashes.Count != ContentPackageWorkshopIds.Count) { return false; }
for (int i = 0; i < ContentPackageWorkshopIds.Count; i++)
{
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash)) { return false; }
}
//make sure the server isn't missing any of our packages that cause multiplayer incompatibility
foreach (ContentPackage myPackage in myContentPackages)
{
if (myPackage.HasMultiplayerIncompatibleContent)
string hash = ContentPackageHashes[i];
UInt64 id = ContentPackageWorkshopIds[i];
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash))
{
if (!ContentPackageHashes.Any(hash => hash == myPackage.MD5hash.Hash)) { return false; }
if (myContentPackages.Any(p => p.SteamWorkshopId == id)) { return false; }
if (id == 0) { return false; }
}
}
@@ -145,19 +144,22 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion));
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
bool hidePlaystyleBanner = previewContainer.Rect.Height < 380 || !PlayStyle.HasValue;
if (!hidePlaystyleBanner)
{
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
playStyleBannerSprite, null, true);
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
playStyleBannerSprite, null, true);
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
}
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
{
@@ -207,8 +209,13 @@ namespace Barotrauma.Networking
TextManager.Get(string.IsNullOrEmpty(GameMode) ? "Unknown" : "GameMode." + GameMode, returnNull: true) ?? GameMode,
textAlignment: Alignment.Right);
/*var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), bodyContainer.RectTransform), TextManager.Get("Traitors"));
new GUITextBlock(new RectTransform(Vector2.One, traitors.RectTransform), TextManager.Get(!TraitorsEnabled.HasValue ? "Unknown" : TraitorsEnabled.Value.ToString()), textAlignment: Alignment.Right);*/
GUITextBlock playStyleText = null;
if (hidePlaystyleBanner && PlayStyle.HasValue)
{
PlayStyle playStyle = PlayStyle.Value;
playStyleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("serverplaystyle"));
new GUITextBlock(new RectTransform(Vector2.One, playStyleText.RectTransform), TextManager.Get("servertag." + playStyle), textAlignment: Alignment.Right);
}
var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("ServerListSubSelection"));
new GUITextBlock(new RectTransform(Vector2.One, subSelection.RectTransform), TextManager.Get(!SubSelectionMode.HasValue ? "Unknown" : SubSelectionMode.Value.ToString()), textAlignment: Alignment.Right);
@@ -222,6 +229,10 @@ namespace Barotrauma.Networking
{
gameMode.Font = subSelection.Font = modeSelection.Font = GUI.SmallFont;
gameMode.GetChild<GUITextBlock>().Font = subSelection.GetChild<GUITextBlock>().Font = modeSelection.GetChild<GUITextBlock>().Font = GUI.SmallFont;
if (playStyleText != null)
{
playStyleText.Font = playStyleText.GetChild<GUITextBlock>().Font = GUI.SmallFont;
}
}
var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), content.RectTransform), TextManager.Get("ServerListAllowSpectating"))
@@ -279,7 +290,6 @@ namespace Barotrauma.Networking
}
else
{
List<string> availableWorkshopUrls = new List<string>();
for (int i = 0; i < ContentPackageNames.Count; i++)
{
var packageText = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
@@ -289,22 +299,15 @@ namespace Barotrauma.Networking
};
if (i < ContentPackageHashes.Count)
{
if (GameMain.Config.SelectedContentPackages.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
if (ContentPackage.AllPackages.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
{
packageText.Selected = true;
continue;
}
//matching content package found, but it hasn't been enabled
if (ContentPackage.List.Any(cp => cp.MD5hash.Hash == ContentPackageHashes[i]))
{
packageText.TextColor = GUI.Style.Orange;
packageText.ToolTip = TextManager.GetWithVariable("ServerListContentPackageNotEnabled", "[contentpackage]", ContentPackageNames[i]);
}
//workshop download link found
else if (i < ContentPackageWorkshopUrls.Count && !string.IsNullOrEmpty(ContentPackageWorkshopUrls[i]))
if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0)
{
availableWorkshopUrls.Add(ContentPackageWorkshopUrls[i]);
packageText.TextColor = Color.Yellow;
packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", ContentPackageNames[i]);
}
@@ -316,21 +319,6 @@ namespace Barotrauma.Networking
}
}
}
if (availableWorkshopUrls.Count > 0)
{
var workshopBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), content.RectTransform), TextManager.Get("ServerListSubscribeMissingPackages"))
{
ToolTip = TextManager.Get(SteamManager.IsInitialized ? "ServerListSubscribeMissingPackagesTooltip" : "ServerListSubscribeMissingPackagesTooltipNoSteam"),
Enabled = SteamManager.IsInitialized,
OnClicked = (btn, userdata) =>
{
GameMain.SteamWorkshopScreen.SubscribeToPackages(availableWorkshopUrls);
GameMain.SteamWorkshopScreen.Select();
return true;
}
};
workshopBtn.TextBlock.AutoScaleHorizontal = true;
}
}
// -----------------------------------------------------------------------------
@@ -391,7 +379,7 @@ namespace Barotrauma.Networking
if (bool.TryParse(element.GetAttributeString("UsingWhiteList", ""), out bool whitelistTemp)) { info.UsingWhiteList = whitelistTemp; }
if (Enum.TryParse(element.GetAttributeString("TraitorsEnabled", ""), out YesNoMaybe traitorsTemp)) { info.TraitorsEnabled = traitorsTemp; }
if (Enum.TryParse(element.GetAttributeString("SubSelectionMode", ""), out SelectionMode subSelectionTemp)) { info.SubSelectionMode = subSelectionTemp; }
if (Enum.TryParse(element.GetAttributeString("ModeSelectionMode", ""), out SelectionMode modeSelectionTemp)) { info.ModeSelectionMode = subSelectionTemp; }
if (Enum.TryParse(element.GetAttributeString("ModeSelectionMode", ""), out SelectionMode modeSelectionTemp)) { info.ModeSelectionMode = modeSelectionTemp; }
if (bool.TryParse(element.GetAttributeString("VoipEnabled", ""), out bool voipTemp)) { info.VoipEnabled = voipTemp; }
if (bool.TryParse(element.GetAttributeString("KarmaEnabled", ""), out bool karmaTemp)) { info.KarmaEnabled = karmaTemp; }
if (bool.TryParse(element.GetAttributeString("FriendlyFireEnabled", ""), out bool friendlyFireTemp)) { info.FriendlyFireEnabled = friendlyFireTemp; }
@@ -477,10 +477,6 @@ namespace Barotrauma.Networking
GUITextBlock.AutoScaleAndNormalize(playStyleTickBoxes.Select(t => t.TextBlock));
playstyleList.RectTransform.MinSize = new Point(0, (int)(playstyleList.Content.Children.First().Rect.Height * 2.0f + playstyleList.Padding.Y + playstyleList.Padding.W));
var endBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundWhenDestReached"));
GetPropertyData("EndRoundAtLevelEnd").AssignGUIComponent(endBox);
var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundVoting"));
GetPropertyData("AllowEndVoting").AssignGUIComponent(endVoteBox);
@@ -569,6 +565,8 @@ namespace Barotrauma.Networking
};
slider.OnMoved(slider, slider.BarScroll);
var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip");
GetPropertyData("TraitorsMinPlayerCount").AssignGUIComponent(traitorsMinPlayerCount);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
@@ -576,53 +574,6 @@ namespace Barotrauma.Networking
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
GetPropertyData("DisableBotConversations").AssignGUIComponent(disableBotConversationsBox);
/*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;
traitorRatioBox.OnSelected = (GUITickBox) =>
{
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
return true;
};
if (TraitorUseRatio)
{
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
}
else
{
traitorRatioSlider.Range = new Vector2(1.0f, maxPlayers);
}
string traitorRatioLabel = TextManager.Get("ServerSettingsTraitorRatio") + " ";
string traitorCountLabel = TextManager.Get("ServerSettingsTraitorCount") + " ";
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
if (traitorRatioBox.Selected)
{
scrollBar.Step = 0.01f;
scrollBar.Range = new Vector2(0.1f, 1.0f);
traitorText.Text = traitorRatioLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 1.0f) + " %";
}
else
{
scrollBar.Step = 1f / (maxPlayers - 1);
scrollBar.Range = new Vector2(1.0f, maxPlayers);
traitorText.Text = traitorCountLabel + scrollBar.BarScrollValue;
}
return true;
};
GetPropertyData("TraitorUseRatio").AssignGUIComponent(traitorRatioBox);
GetPropertyData("TraitorRatio").AssignGUIComponent(traitorRatioSlider);
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
traitorRatioBox.OnSelected(traitorRatioBox);*/
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
{
Stretch = true,
@@ -919,7 +870,7 @@ namespace Barotrauma.Networking
slider.UserData = label;
}
private GUINumberInput CreateLabeledNumberInput(GUIComponent parent, string labelTag, int min, int max)
private GUINumberInput CreateLabeledNumberInput(GUIComponent parent, string labelTag, int min, int max, string toolTipTag = null)
{
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
{
@@ -928,11 +879,15 @@ namespace Barotrauma.Networking
ToolTip = TextManager.Get(labelTag)
};
new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
TextManager.Get(labelTag), textAlignment: Alignment.CenterLeft, font: GUI.SmallFont)
{
AutoScaleHorizontal = true
};
if (!string.IsNullOrEmpty(toolTipTag))
{
label.ToolTip = TextManager.Get(toolTipTag);
}
var input = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = min,
@@ -1,22 +1,18 @@
using Barotrauma.Networking;
using Barotrauma.IO;
using Barotrauma.Networking;
using RestSharp;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using RestSharp.Contrib;
using System.Xml.Linq;
using Color = Microsoft.Xna.Framework.Color;
using System.Runtime.InteropServices;
using NLog.Fluent;
namespace Barotrauma.Steam
{
static partial class SteamManager
{
private static Dictionary<Steamworks.Data.PublishedFileId, Task> modCopiesInProgress = new Dictionary<Steamworks.Data.PublishedFileId, Task>();
private static readonly Dictionary<Steamworks.Data.PublishedFileId, Task> modCopiesInProgress = new Dictionary<Steamworks.Data.PublishedFileId, Task>();
private static void InitializeProjectSpecific()
{
@@ -49,6 +45,9 @@ namespace Barotrauma.Steam
}
catch (Exception e)
{
#if !DEBUG
DebugConsole.ThrowError("SteamManager initialization threw an exception", e);
#endif
isInitialized = false;
initializationErrors.Add("SteamClientInitFailed");
}
@@ -85,28 +84,6 @@ namespace Barotrauma.Steam
}
}
private static void UpdateProjectSpecific(float deltaTime)
{
if (ugcSubscriptionTasks != null)
{
var ugcSubscriptionKeys = ugcSubscriptionTasks.Keys.ToList();
foreach (var key in ugcSubscriptionKeys)
{
var task = ugcSubscriptionTasks[key];
if (task.IsCompleted)
{
if (!task.IsCompletedSuccessfully)
{
DebugConsole.ThrowError("Failed to subscribe to a Steam Workshop item with id " + key.ToString() + ": TaskStatus = " + task.Status.ToString());
}
ugcSubscriptionTasks.Remove(key);
}
}
}
}
public static async Task InitRelayNetworkAccess()
{
if (!IsInitialized) { return; }
@@ -209,13 +186,13 @@ namespace Barotrauma.Steam
return;
}
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
var contentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
currentLobby?.SetData("name", serverSettings.ServerName);
currentLobby?.SetData("playercount", (GameMain.Client?.ConnectedClients?.Count ?? 0).ToString());
currentLobby?.SetData("maxplayernum", serverSettings.MaxPlayers.ToString());
//currentLobby?.SetData("hostipaddress", lobbyIP);
string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation.ToString();
string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation?.ToString();
currentLobby?.SetData("pinglocation", pingLocation ?? "");
currentLobby?.SetData("lobbyowner", SteamIDUInt64ToString(GetSteamID()));
currentLobby?.SetData("haspassword", serverSettings.HasPassword.ToString());
@@ -225,7 +202,7 @@ namespace Barotrauma.Steam
currentLobby?.SetData("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
currentLobby?.SetData("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
currentLobby?.SetData("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
currentLobby?.SetData("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
currentLobby?.SetData("usingwhitelist", (serverSettings.Whitelist != null && serverSettings.Whitelist.Enabled).ToString());
currentLobby?.SetData("modeselectionmode", serverSettings.ModeSelectionMode.ToString());
currentLobby?.SetData("subselectionmode", serverSettings.SubSelectionMode.ToString());
@@ -280,9 +257,8 @@ namespace Barotrauma.Steam
{
if (!isInitialized) { return false; }
int doneTasks = 0;
Action taskDone = () =>
void taskDone()
{
doneTasks++;
if (doneTasks >= 2)
@@ -290,52 +266,64 @@ namespace Barotrauma.Steam
serverQueryFinished?.Invoke();
serverQueryFinished = null;
}
};
}
//TODO: find a better strategy to fetch all lobbies, this is gonna take forever if we actually have 10000 lobbies
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide().WithMaxResults(10000);
Steamworks.Dispatch.OnDebugCallback = (callbackType, contents, isServer) =>
{
DebugConsole.NewMessage($"{callbackType}: " + contents, Color.Yellow);
};
TaskPool.Add("LobbyQueryRequest", lobbyQuery.RequestAsync(),
(t) =>
{
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result;
foreach (var lobby in lobbies)
{
if (string.IsNullOrEmpty(lobby.GetData("name"))) { continue; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = lobby.GetData("name");
serverInfo.OwnerID = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
serverInfo.LobbyID = lobby.Id;
bool.TryParse(lobby.GetData("haspassword"), out serverInfo.HasPassword);
serverInfo.PlayerCount = int.TryParse(lobby.GetData("playercount"), out int playerCount) ? playerCount : 0;
serverInfo.MaxPlayers = int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers) ? maxPlayers : 1;
serverInfo.RespondedToSteamQuery = true;
AssignLobbyDataToServerInfo(lobby, serverInfo);
addToServerList(serverInfo);
}
taskDone();
Steamworks.Dispatch.OnDebugCallback = null;
if (t.Status == TaskStatus.Faulted)
{
TaskPool.PrintTaskExceptions(t, "Failed to retrieve SteamP2P lobbies");
taskDone();
return;
}
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result;
if (lobbies != null)
{
foreach (var lobby in lobbies)
{
if (string.IsNullOrEmpty(lobby.GetData("name"))) { continue; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = lobby.GetData("name");
serverInfo.OwnerID = SteamIDStringToUInt64(lobby.GetData("lobbyowner"));
serverInfo.LobbyID = lobby.Id;
bool.TryParse(lobby.GetData("haspassword"), out serverInfo.HasPassword);
serverInfo.PlayerCount = int.TryParse(lobby.GetData("playercount"), out int playerCount) ? playerCount : 0;
serverInfo.MaxPlayers = int.TryParse(lobby.GetData("maxplayernum"), out int maxPlayers) ? maxPlayers : 1;
serverInfo.RespondedToSteamQuery = true;
AssignLobbyDataToServerInfo(lobby, serverInfo);
addToServerList(serverInfo);
}
}
taskDone();
});
Steamworks.ServerList.Internet serverQuery = new Steamworks.ServerList.Internet();
Action<Steamworks.Data.ServerInfo, bool> onServer = (Steamworks.Data.ServerInfo info, bool responsive) =>
void onServer(Steamworks.Data.ServerInfo info, bool responsive)
{
if (string.IsNullOrEmpty(info.Name)) { return; }
ServerInfo serverInfo = new ServerInfo();
serverInfo.ServerName = info.Name;
serverInfo.IP = info.Address.ToString();
serverInfo.Port = info.ConnectionPort.ToString();
serverInfo.PlayerCount = info.Players;
serverInfo.MaxPlayers = info.MaxPlayers;
serverInfo.RespondedToSteamQuery = responsive;
ServerInfo serverInfo = new ServerInfo
{
ServerName = info.Name,
HasPassword = info.Passworded,
IP = info.Address.ToString(),
Port = info.ConnectionPort.ToString(),
PlayerCount = info.Players,
MaxPlayers = info.MaxPlayers,
RespondedToSteamQuery = responsive
};
if (responsive)
{
@@ -344,11 +332,11 @@ namespace Barotrauma.Steam
{
if (t.Status == TaskStatus.Faulted)
{
TaskPool.PrintTaskExceptions(t, "Failed to retrieve rules for "+info.Name);
TaskPool.PrintTaskExceptions(t, "Failed to retrieve rules for " + info.Name);
return;
}
var rules = ((Task<Dictionary<string,string>>)t).Result;
var rules = ((Task<Dictionary<string, string>>)t).Result;
AssignServerRulesToServerInfo(rules, serverInfo);
CrossThread.RequestExecutionOnMainThread(() =>
@@ -365,7 +353,7 @@ namespace Barotrauma.Steam
});
}
};
}
serverQuery.OnResponsiveServer += (info) => onServer(info, true);
serverQuery.OnUnresponsiveServer += (info) => onServer(info, false);
@@ -393,11 +381,20 @@ namespace Barotrauma.Steam
serverInfo.ContentPackageNames.AddRange(lobby.GetData("contentpackage").Split(','));
serverInfo.ContentPackageHashes.AddRange(lobby.GetData("contentpackagehash").Split(','));
serverInfo.ContentPackageWorkshopUrls.AddRange(lobby.GetData("contentpackageurl").Split(','));
string workshopIdData = lobby.GetData("contentpackageid");
if (!string.IsNullOrEmpty(workshopIdData))
{
serverInfo.ContentPackageWorkshopIds.AddRange(ParseWorkshopIds(workshopIdData));
}
else
{
string[] workshopUrls = lobby.GetData("contentpackageurl").Split(',');
serverInfo.ContentPackageWorkshopIds.AddRange(WorkshopUrlsToIds(workshopUrls));
}
serverInfo.UsingWhiteList = getLobbyBool("usingwhitelist");
SelectionMode selectionMode;
if (Enum.TryParse(lobby.GetData("modeselectionmode"), out selectionMode)) { serverInfo.ModeSelectionMode = selectionMode; }
if (Enum.TryParse(lobby.GetData("modeselectionmode"), out SelectionMode selectionMode)) { serverInfo.ModeSelectionMode = selectionMode; }
if (Enum.TryParse(lobby.GetData("subselectionmode"), out selectionMode)) { serverInfo.SubSelectionMode = selectionMode; }
serverInfo.AllowSpectating = getLobbyBool("allowspectating");
@@ -412,11 +409,12 @@ namespace Barotrauma.Steam
if (Enum.TryParse(lobby.GetData("playstyle"), out PlayStyle playStyle)) serverInfo.PlayStyle = playStyle;
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopIds.Count)
{
//invalid contentpackage info
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
}
string pingLocation = lobby.GetData("pinglocation");
@@ -449,10 +447,18 @@ namespace Barotrauma.Steam
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopUrls.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
if (rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(rules["contentpackage"].Split(','));
if (rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(rules["contentpackagehash"].Split(','));
if (rules.ContainsKey("contentpackageurl")) serverInfo.ContentPackageWorkshopUrls.AddRange(rules["contentpackageurl"].Split(','));
if (rules.ContainsKey("contentpackageid"))
{
serverInfo.ContentPackageWorkshopIds.AddRange(ParseWorkshopIds(rules["contentpackageid"]));
}
else if (rules.ContainsKey("contentpackageurl"))
{
string[] workshopUrls = rules["contentpackageurl"].Split(',');
serverInfo.ContentPackageWorkshopIds.AddRange(WorkshopUrlsToIds(workshopUrls));
}
if (rules.ContainsKey("usingwhitelist")) serverInfo.UsingWhiteList = rules["usingwhitelist"] == "True";
if (rules.ContainsKey("modeselectionmode"))
@@ -479,37 +485,16 @@ namespace Barotrauma.Steam
}
if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count ||
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count)
serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopIds.Count)
{
//invalid contentpackage info
serverInfo.ContentPackageNames.Clear();
serverInfo.ContentPackageHashes.Clear();
serverInfo.ContentPackageWorkshopIds.Clear();
}
}
public static ulong GetWorkshopItemIDFromUrl(string url)
{
try
{
Uri uri = new Uri(url);
string idStr = HttpUtility.ParseQueryString(uri.Query).Get("id");
if (ulong.TryParse(idStr, out ulong id))
{
return id;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to get Workshop item ID from the url \"" + url + "\"!", e);
}
return 0;
}
#region Connecting to servers
//TODO: reimplement server list queries
#region Connecting to servers
private static Steamworks.AuthTicket currentTicket = null;
public static Steamworks.AuthTicket GetAuthSessionTicket()
{
@@ -545,9 +530,9 @@ namespace Barotrauma.Steam
Steamworks.SteamUser.EndAuthSession(clientSteamID);
}
#endregion
#endregion
#region Workshop
#region Workshop
public const string WorkshopItemPreviewImageFolder = "Workshop";
public const string PreviewImageName = "PreviewImage.png";
@@ -620,7 +605,8 @@ namespace Barotrauma.Steam
.WithLongDescription();
if (requireTags != null) query.WithTags(requireTags);
TaskPool.Add("GetPopularWorkshopItems", GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) => {
TaskPool.Add("GetPopularWorkshopItems", GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) =>
{
var entries = ((Task<List<Steamworks.Ugc.Item>>)task).Result;
//count the number of each unique tag
@@ -669,47 +655,82 @@ namespace Barotrauma.Steam
TaskPool.Add("GetPublishedWorkshopItems", GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(((Task<List<Steamworks.Ugc.Item>>)task).Result); });
}
private static Dictionary<ulong, Task> ugcSubscriptionTasks;
private static readonly HashSet<ulong> pendingWorkshopSubscriptions = new HashSet<ulong>();
public static void SubscribeToWorkshopItem(string itemUrl)
{
if (!isInitialized) return;
ulong id = GetWorkshopItemIDFromUrl(itemUrl);
SubscribeToWorkshopItem(id);
}
public static void SubscribeToWorkshopItem(ulong id)
public static void SubscribeToWorkshopItem(ulong id, Action onInstalled = null)
{
if (!isInitialized) return;
if (id == 0) { return; }
if (ugcSubscriptionTasks?.ContainsKey(id) ?? false) { return; }
if (pendingWorkshopSubscriptions.Contains(id)) { return; }
ugcSubscriptionTasks ??= new Dictionary<ulong, Task>();
ugcSubscriptionTasks.Add(id, Task.Run(async () =>
{
Steamworks.Ugc.Item? item = await Steamworks.SteamUGC.QueryFileAsync(id);
pendingWorkshopSubscriptions.Add(id);
TaskPool.Add(
$"SubscribeToWorkshopItem({id})",
Task.Run(async () =>
{
Steamworks.Ugc.Item? item = await Steamworks.SteamUGC.QueryFileAsync(id);
if (!item.HasValue)
{
DebugConsole.ThrowError("Failed to find a Steam Workshop item with the ID " + id.ToString() + ".");
return;
}
if (!item.HasValue)
{
DebugConsole.ThrowError($"Failed to find a Steam Workshop item with the ID {id}.");
return null;
}
bool subscribed = await item?.Subscribe();
if (!subscribed)
if (!(item?.IsSubscribed ?? false))
{
bool subscribed = await item?.Subscribe();
if (!subscribed)
{
DebugConsole.ThrowError($"Failed to subscribe to Steam Workshop item with the ID {id}.");
return null;
}
}
return item;
}),
(t) =>
{
DebugConsole.ThrowError("Failed to subscribe to Steam Workshop item with the ID " + id.ToString() + ".");
}
bool downloading = item?.Download() ?? false;
if (!downloading)
{
DebugConsole.ThrowError("Failed to start downloading Steam Workshop item with the ID " + id.ToString() + ".");
}
}));
bool shouldCleanup = true;
if (t.IsFaulted)
{
TaskPool.PrintTaskExceptions(t, $"Workshop subscription task {id} faulted");
}
else
{
var item = ((Task<Steamworks.Ugc.Item?>)t).Result;
if (item != null)
{
if (item?.IsInstalled ?? false)
{
onInstalled?.Invoke();
}
else
{
void _onInstalled()
{
onInstalled?.Invoke();
pendingWorkshopSubscriptions.Remove(id);
}
bool downloading = item?.Download(_onInstalled) ?? false;
if (!downloading)
{
DebugConsole.ThrowError($"Failed to start downloading Steam Workshop item with the ID {id}.");
}
else
{
shouldCleanup = false;
}
}
}
if (shouldCleanup)
{
pendingWorkshopSubscriptions.Remove(id);
}
}
});
}
public static void CreateWorkshopItemStaging(ContentPackage contentPackage, out Steamworks.Ugc.Editor? itemEditor)
@@ -790,9 +811,9 @@ namespace Barotrauma.Steam
itemEditor = itemEditor?.WithPrivateVisibility();
}
if (!CheckWorkshopItemEnabled(existingItem))
if (!CheckWorkshopItemInstalled(existingItem))
{
if (!EnableWorkShopItem(existingItem, out string errorMsg))
if (!InstallWorkshopItem(existingItem, out string errorMsg))
{
DebugConsole.NewMessage(errorMsg, Color.Red);
new GUIMessageBox(
@@ -804,9 +825,9 @@ namespace Barotrauma.Steam
}
}
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem?.Directory, MetadataFileName)) { SteamWorkshopUrl = existingItem.Value.Url };
ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem?.Directory, MetadataFileName)) { SteamWorkshopId = existingItem.Value.Id };
string installedContentPackagePath = Path.GetFullPath(GetWorkshopItemContentPackagePath(tempContentPackage));
contentPackage = ContentPackage.List.Find(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath);
contentPackage = ContentPackage.AllPackages.FirstOrDefault(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath);
itemEditor = itemEditor?.WithContent(Path.GetDirectoryName(installedContentPackagePath));
@@ -923,7 +944,7 @@ namespace Barotrauma.Steam
workshopPublishStatus.Result = task.Result;
DebugConsole.NewMessage("Published workshop item " + item?.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen);
contentPackage.SteamWorkshopUrl = $"http://steamcommunity.com/sharedfiles/filedetails/?source=Facepunch.Steamworks&id={task.Result.FileId.Value}";
contentPackage.SteamWorkshopId = task.Result.FileId.Value;
//NOTE: This sets InstallTime one hour into the future to guarantee
//that the published content package won't be autoupdated incorrectly.
//Change if it causes issues.
@@ -937,9 +958,9 @@ namespace Barotrauma.Steam
}
/// <summary>
/// Enables a workshop item by moving it to the game folder.
/// Installs a workshop item by moving it to the game folder.
/// </summary>
public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool selectContentPackage = false, bool suppressInstallNotif = false)
public static bool InstallWorkshopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool enableContentPackage = false, bool suppressInstallNotif = false)
{
if (!(item?.IsInstalled ?? false))
{
@@ -959,11 +980,11 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(metaDataFilePath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
List<ContentPackage> existingPackages = ContentPackage.List.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
List<ContentPackage> existingPackages = ContentPackage.AllPackages.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
if (existingPackages.Any())
{
if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
@@ -975,7 +996,7 @@ namespace Barotrauma.Steam
}
else
{
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl,
RemoveMods(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == contentPackage.SteamWorkshopId,
false);
}
}
@@ -1024,7 +1045,7 @@ namespace Barotrauma.Steam
var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{
SteamWorkshopUrl = item?.Url,
SteamWorkshopId = item?.Id ?? 0,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
};
@@ -1045,17 +1066,17 @@ namespace Barotrauma.Steam
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
ContentPackage.AddPackage(newPackage);
if (selectContentPackage)
if (enableContentPackage)
{
if (newPackage.CorePackage)
if (newPackage.IsCorePackage)
{
GameMain.Config.SelectCorePackage(newPackage);
}
else
{
GameMain.Config.SelectContentPackage(newPackage);
GameMain.Config.EnableRegularPackage(newPackage);
}
GameMain.Config.SaveNewPlayerConfig();
@@ -1213,40 +1234,21 @@ namespace Barotrauma.Steam
return "";
}
private static bool CheckFileEquality(string filePath1, string filePath2)
{
if (filePath1 == filePath2)
{
return true;
}
using (FileStream fs1 = File.OpenRead(filePath1))
using (FileStream fs2 = File.OpenRead(filePath2))
{
Md5Hash hash1 = new Md5Hash(fs1);
Md5Hash hash2 = new Md5Hash(fs2);
return hash1.Hash == hash2.Hash;
}
}
private static void RemoveMods(Func<ContentPackage, bool> predicate, bool delete = true)
{
var toRemove = ContentPackage.List.Where(predicate).ToList();
var packagesToDeselect = GameMain.Config.SelectedContentPackages.Where(p => toRemove.Contains(p)).ToList();
var toRemoveCore = ContentPackage.CorePackages.Where(predicate).ToList();
if (toRemoveCore.Contains(GameMain.Config.CurrentCorePackage)) { GameMain.Config.AutoSelectCorePackage(toRemoveCore); }
var toRemoveRegular = ContentPackage.RegularPackages.Where(predicate).ToList();
var packagesToDeselect = GameMain.Config.EnabledRegularPackages.Where(p => toRemoveRegular.Contains(p)).ToList();
foreach (var cp in packagesToDeselect)
{
if (cp.CorePackage)
{
GameMain.Config.AutoSelectCorePackage(toRemove);
}
else
{
GameMain.Config.DeselectContentPackage(cp);
}
GameMain.Config.DisableRegularPackage(cp);
}
if (delete)
{
var toRemove = toRemoveCore.Concat(toRemoveRegular);
foreach (var cp in toRemove)
{
try
@@ -1258,22 +1260,19 @@ namespace Barotrauma.Steam
{
DebugConsole.ThrowError($"An error occurred while attempting to delete {Path.GetDirectoryName(cp.Path)}", e);
}
ContentPackage.RemovePackage(cp);
}
}
ContentPackage.List.RemoveAll(cp => toRemove.Contains(cp));
GameMain.Config.SelectedContentPackages.RemoveAll(cp => !ContentPackage.List.Contains(cp));
ContentPackage.SortContentPackages();
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
}
/// <summary>
/// Disables a workshop item by removing the files from the game folder.
/// Uninstalls a workshop item by removing the files from the game folder.
/// </summary>
public static bool DisableWorkShopItem(Steamworks.Ugc.Item? item, bool noLog, out string errorMsg)
public static bool UninstallWorkshopItem(Steamworks.Ugc.Item? item, bool noLog, out string errorMsg)
{
errorMsg = null;
if (!(item?.IsInstalled ?? false))
@@ -1288,13 +1287,13 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(Path.Combine(item?.Directory, MetadataFileName))
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
GameMain.Config.SuppressModFolderWatcher = true;
try
{
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl);
RemoveMods(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == contentPackage.SteamWorkshopId);
}
catch (Exception e)
{
@@ -1330,7 +1329,7 @@ namespace Barotrauma.Steam
return contentPackage.IsCompatible();
}
public static bool CheckWorkshopItemEnabled(Steamworks.Ugc.Item? item)
public static bool CheckWorkshopItemInstalled(Steamworks.Ugc.Item? item)
{
if (!(item?.IsInstalled ?? false)) { return false; }
@@ -1359,7 +1358,7 @@ namespace Barotrauma.Steam
string errorMessage = "Metadata file for the Workshop item \"" + item?.Title +
"\" not found. Could not combine path (" + (item?.Directory ?? "directory name empty") + ").";
DebugConsole.ThrowError(errorMessage);
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemEnabled:PathCombineException" + item?.Title,
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item?.Title,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMessage);
return false;
@@ -1373,12 +1372,12 @@ namespace Barotrauma.Steam
ContentPackage contentPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
//make sure the contentpackage file is present
if (!File.Exists(GetWorkshopItemContentPackagePath(contentPackage)) ||
!ContentPackage.List.Any(cp => cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl ||
(string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.Name == contentPackage.Name)))
!ContentPackage.AllPackages.Any(cp => cp.SteamWorkshopId == contentPackage.SteamWorkshopId ||
(cp.SteamWorkshopId == 0 && cp.Name == contentPackage.Name)))
{
return false;
}
@@ -1399,9 +1398,9 @@ namespace Barotrauma.Steam
ContentPackage steamPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopUrl = item?.Url
SteamWorkshopId = item?.Id ?? 0
};
ContentPackage myPackage = ContentPackage.List.Find(cp => cp.SteamWorkshopUrl == steamPackage.SteamWorkshopUrl);
ContentPackage myPackage = ContentPackage.AllPackages.FirstOrDefault(cp => cp.SteamWorkshopId == steamPackage.SteamWorkshopId);
if (myPackage?.InstallTime == null)
{
@@ -1427,7 +1426,7 @@ namespace Barotrauma.Steam
GameMain.Config.SuppressModFolderWatcher = true;
//remove mods that the player is no longer subscribed to
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && !items.Any(it => it.Id == GetWorkshopItemIDFromUrl(cp.SteamWorkshopUrl)));
RemoveMods(cp => cp.SteamWorkshopId != 0 && !items.Any(it => it.Id == cp.SteamWorkshopId));
GameMain.Config.SuppressModFolderWatcher = false;
@@ -1441,9 +1440,9 @@ namespace Barotrauma.Steam
bool installedSuccessfully = false;
string errorMsg;
if (!CheckWorkshopItemEnabled(item))
if (!CheckWorkshopItemInstalled(item))
{
installedSuccessfully = EnableWorkShopItem(item, out errorMsg);
installedSuccessfully = InstallWorkshopItem(item, out errorMsg);
}
else if (!CheckWorkshopItemUpToDate(item))
{
@@ -1529,12 +1528,12 @@ namespace Barotrauma.Steam
{
errorMsg = "";
if (!(item?.IsInstalled ?? false)) { return false; }
bool reenable = GameMain.Config.SelectedContentPackages.Any(p => !string.IsNullOrEmpty(p.SteamWorkshopUrl) && GetWorkshopItemIDFromUrl(p.SteamWorkshopUrl) == item?.Id);
bool reenable = GameMain.Config.AllEnabledPackages.Any(p => p.SteamWorkshopId != 0 && p.SteamWorkshopId == item?.Id);
if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
{
if (!DisableWorkShopItem(item, false, out errorMsg)) { return false; }
if (!UninstallWorkshopItem(item, false, out errorMsg)) { return false; }
}
if (!EnableWorkShopItem(item, errorMsg: out errorMsg, selectContentPackage: reenable)) { return false; }
if (!InstallWorkshopItem(item, errorMsg: out errorMsg, enableContentPackage: reenable)) { return false; }
return true;
}
@@ -1543,7 +1542,7 @@ namespace Barotrauma.Steam
string packageName = contentPackage.Name.Trim();
packageName = ToolBox.RemoveInvalidFileNameChars(packageName);
while (packageName.Last() == '.') { packageName = packageName.Substring(0, packageName.Length-1); }
//packageName = packageName + "_" + GetWorkshopItemIDFromUrl(contentPackage.SteamWorkshopUrl);
//packageName = packageName + "_" + contentPackage.SteamWorkshopId.ToString();
return Path.Combine("Mods", packageName, MetadataFileName);
}
@@ -1559,8 +1558,7 @@ namespace Barotrauma.Steam
attr.Name.ToString() == "characterfile") &&
attr.Value.CleanUpPath().Contains("/"))
{
ContentType type = ContentType.None;
Enum.TryParse(attr.Name.LocalName, true, out type);
Enum.TryParse(attr.Name.LocalName, true, out ContentType type);
attr.Value = CorrectContentFilePath(attr.Value, type, package, true);
}
}
@@ -1615,8 +1613,7 @@ namespace Barotrauma.Steam
if (checkIfFileExists)
{
bool exists = File.Exists(contentFilePath);
if (type == ContentType.Executable ||
type == ContentType.ServerExecutable)
if (type == ContentType.ServerExecutable)
{
exists |= File.Exists(Path.GetFileNameWithoutExtension(contentFilePath) + ".dll");
}
@@ -1634,7 +1631,7 @@ namespace Barotrauma.Steam
{
if (checkIfFileExists)
{
ContentPackage otherContentPackage = ContentPackage.List.Find(cp => cp.Name.Equals(splitPath[1], StringComparison.OrdinalIgnoreCase));
ContentPackage otherContentPackage = ContentPackage.AllPackages.FirstOrDefault(cp => cp.Name.Equals(splitPath[1], StringComparison.OrdinalIgnoreCase));
if (otherContentPackage != null)
{
string otherPackageName = Path.GetDirectoryName(otherContentPackage.Path);
@@ -167,7 +167,7 @@ namespace Barotrauma.Networking
bool prevCaptured = true;
int captureTimer;
void UpdateCapture()
private void UpdateCapture()
{
Array.Copy(uncompressedBuffer, 0, prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
Array.Clear(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
@@ -273,6 +273,11 @@ namespace Barotrauma.Networking
if (allowEnqueue || captureTimer > 0)
{
LastEnqueueAudio = DateTime.Now;
if (GameMain.Client?.Character != null)
{
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
}
//encode audio and enqueue it
lock (buffers)
{
@@ -91,13 +91,13 @@ namespace Barotrauma
if (userData == null) return;
foreach (GUIComponent comp in listBox.Content.Children)
{
if (comp.UserData != userData) continue;
GUITextBlock voteText = comp.FindChild("votes") as GUITextBlock;
if (voteText == null)
if (comp.UserData != userData) { continue; }
if (!(comp.FindChild("votes") is GUITextBlock voteText))
{
voteText = new GUITextBlock(new RectTransform(new Point(30, comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
"", textAlignment: Alignment.CenterRight)
{
Padding = Vector4.Zero,
UserData = "votes"
};
}