(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
@@ -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)
@@ -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]);
@@ -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;
}
@@ -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();
@@ -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
@@ -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);
}
}
}
@@ -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);
}
@@ -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);
@@ -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);
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
@@ -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;
@@ -5,7 +5,7 @@ using System.Linq;
namespace Barotrauma.Networking
{
partial class ServerLog
public partial class ServerLog
{
public GUIButton LogFrame;
private GUIListBox listBox;
@@ -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;
}
}
}
}
@@ -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
@@ -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)
{
@@ -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;
}
}
}
}
@@ -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)
@@ -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();