(a00338777) v0.9.2.1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-12
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user