Unstable v0.19.3.0
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
#nullable enable
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -24,36 +25,31 @@ namespace Barotrauma.Networking
|
||||
|
||||
partial class BanList
|
||||
{
|
||||
private GUIComponent banFrame;
|
||||
|
||||
public GUIComponent BanFrame
|
||||
{
|
||||
get { return banFrame; }
|
||||
}
|
||||
public GUIComponent? BanFrame { get; private set; }
|
||||
|
||||
public List<UInt32> localRemovedBans = new List<UInt32>();
|
||||
|
||||
private void RecreateBanFrame()
|
||||
{
|
||||
if (banFrame != null)
|
||||
if (BanFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
var parent = BanFrame.Parent;
|
||||
parent.RemoveChild(BanFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public GUIComponent CreateBanFrame(GUIComponent parent)
|
||||
{
|
||||
banFrame = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center));
|
||||
BanFrame = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center));
|
||||
|
||||
foreach (BannedPlayer bannedPlayer in bannedPlayers)
|
||||
{
|
||||
if (localRemovedBans.Contains(bannedPlayer.UniqueIdentifier)) { continue; }
|
||||
|
||||
var playerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), ((GUIListBox)banFrame).Content.RectTransform) { MinSize = new Point(0, 70) })
|
||||
var playerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), ((GUIListBox)BanFrame).Content.RectTransform) { MinSize = new Point(0, 70) })
|
||||
{
|
||||
UserData = banFrame
|
||||
UserData = BanFrame
|
||||
};
|
||||
|
||||
var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), playerFrame.RectTransform, Anchor.Center))
|
||||
@@ -102,16 +98,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
paddedPlayerFrame.Recalculate();
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), ((GUIListBox)banFrame).Content.RectTransform), style: "HorizontalLine");
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), ((GUIListBox)BanFrame).Content.RectTransform), style: "HorizontalLine");
|
||||
}
|
||||
|
||||
return banFrame;
|
||||
return BanFrame;
|
||||
}
|
||||
|
||||
private bool RemoveBan(GUIButton button, object obj)
|
||||
{
|
||||
BannedPlayer banned = obj as BannedPlayer;
|
||||
if (banned == null) { return false; }
|
||||
if (!(obj is BannedPlayer banned)) { return false; }
|
||||
|
||||
localRemovedBans.Add(banned.UniqueIdentifier);
|
||||
RecreateBanFrame();
|
||||
@@ -178,10 +173,10 @@ namespace Barotrauma.Networking
|
||||
bannedPlayers.Add(new BannedPlayer(uniqueIdentifier, name, addressOrAccountId, reason, expiration));
|
||||
}
|
||||
|
||||
if (banFrame != null)
|
||||
if (BanFrame != null)
|
||||
{
|
||||
var parent = banFrame.Parent;
|
||||
parent.RemoveChild(banFrame);
|
||||
var parent = BanFrame.Parent;
|
||||
parent.RemoveChild(BanFrame);
|
||||
CreateBanFrame(parent);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +186,7 @@ namespace Barotrauma.Networking
|
||||
outMsg.WriteVariableUInt32((UInt32)localRemovedBans.Count);
|
||||
foreach (UInt32 uniqueId in localRemovedBans)
|
||||
{
|
||||
outMsg.Write(uniqueId);
|
||||
outMsg.WriteUInt32(uniqueId);
|
||||
}
|
||||
|
||||
localRemovedBans.Clear();
|
||||
|
||||
@@ -8,11 +8,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public virtual void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
msg.Write(NetStateID);
|
||||
msg.WriteByte((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)Type, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.WriteRangedInteger((int)ChatMode, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
|
||||
msg.Write(Text);
|
||||
msg.WriteString(Text);
|
||||
}
|
||||
|
||||
public static void ClientRead(IReadMessage msg)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma.Networking
|
||||
class FileReceiver
|
||||
{
|
||||
public class FileTransferIn : IDisposable
|
||||
{
|
||||
{
|
||||
public string FileName
|
||||
{
|
||||
get;
|
||||
@@ -36,7 +36,7 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public int LastSeen { get; set; }
|
||||
|
||||
public FileTransferType FileType
|
||||
@@ -93,6 +93,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
public int ID;
|
||||
|
||||
public const int DataBufferSize = 50;
|
||||
/// <summary>
|
||||
/// Data that we've ignored because we're waiting for some earlier data. Key = byte offset, value = the actual data
|
||||
/// </summary>
|
||||
public readonly Dictionary<int, byte[]> DataBuffer = new Dictionary<int, byte[]>();
|
||||
|
||||
public FileTransferIn(NetworkConnection connection, string filePath, FileTransferType fileType)
|
||||
{
|
||||
FilePath = filePath;
|
||||
@@ -128,20 +134,25 @@ namespace Barotrauma.Networking
|
||||
bytesToRead -= Received + bytesToRead - FileSize;
|
||||
}
|
||||
|
||||
byte[] all = inc.ReadBytes(bytesToRead);
|
||||
Received += all.Length;
|
||||
WriteStream.Write(all, 0, all.Length);
|
||||
ReadBytes(inc.ReadBytes(bytesToRead));
|
||||
}
|
||||
|
||||
public void ReadBytes(byte[] data)
|
||||
{
|
||||
Received += data.Length;
|
||||
WriteStream.Write(data, 0, data.Length);
|
||||
|
||||
int passed = Environment.TickCount - TimeStarted;
|
||||
float psec = passed / 1000.0f;
|
||||
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.Log($"Received {all.Length} bytes of the file {FileName} ({Received / 1000}/{FileSize / 1000} kB received)");
|
||||
}
|
||||
|
||||
BytesPerSecond = Received / psec;
|
||||
|
||||
var outdatedKeys = DataBuffer.Keys.Where(k => k < Received).ToList();
|
||||
foreach (int key in outdatedKeys)
|
||||
{
|
||||
DataBuffer.Remove(key);
|
||||
}
|
||||
|
||||
Status = Received >= FileSize ? FileTransferStatus.Finished : FileTransferStatus.Receiving;
|
||||
}
|
||||
|
||||
@@ -349,6 +360,10 @@ namespace Barotrauma.Networking
|
||||
if (offset != activeTransfer.Received)
|
||||
{
|
||||
activeTransfer.LastSeen = Math.Max(offset, activeTransfer.LastSeen);
|
||||
if (!activeTransfer.DataBuffer.ContainsKey(offset) && activeTransfer.DataBuffer.Count < FileTransferIn.DataBufferSize)
|
||||
{
|
||||
activeTransfer.DataBuffer.Add(offset, inc.ReadBytes(bytesToRead));
|
||||
}
|
||||
DebugConsole.Log($"Received {bytesToRead} bytes of the file {activeTransfer.FileName} (ignoring: offset {offset}, waiting for {activeTransfer.Received})");
|
||||
GameMain.Client.UpdateFileTransfer(activeTransfer, activeTransfer.Received, activeTransfer.LastSeen);
|
||||
return;
|
||||
@@ -370,7 +385,16 @@ namespace Barotrauma.Networking
|
||||
|
||||
try
|
||||
{
|
||||
activeTransfer.ReadBytes(inc, bytesToRead);
|
||||
activeTransfer.ReadBytes(inc, bytesToRead);
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.Log($"Received {bytesToRead} bytes of the file {activeTransfer.FileName} ({activeTransfer.Received / 1000}/{activeTransfer.FileSize / 1000} kB received)");
|
||||
}
|
||||
while (activeTransfer.DataBuffer.TryGetValue(activeTransfer.Received, out byte[] data))
|
||||
{
|
||||
activeTransfer.ReadBytes(data);
|
||||
DebugConsole.Log($"Read {data.Length} bytes of buffer data of the file {activeTransfer.FileName} ({activeTransfer.Received / 1000}/{activeTransfer.FileSize / 1000} kB received)");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -434,7 +458,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(fileName) ||
|
||||
fileName.IndexOfAny(Path.GetInvalidFileNameChars().ToArray()) > -1)
|
||||
fileName.IndexOfAny(Path.GetInvalidFileNameCharsCrossPlatform().ToArray()) > -1)
|
||||
{
|
||||
errorMessage = "Illegal characters in file name ''" + fileName + "''";
|
||||
return false;
|
||||
@@ -470,7 +494,7 @@ namespace Barotrauma.Networking
|
||||
System.IO.Stream stream;
|
||||
try
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(fileTransfer.FilePath);
|
||||
stream = SaveUtil.DecompressFileToStream(fileTransfer.FilePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -674,13 +674,13 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case ServerPacketHeader.PING_REQUEST:
|
||||
IWriteMessage response = new WriteOnlyMessage();
|
||||
response.Write((byte)ClientPacketHeader.PING_RESPONSE);
|
||||
response.WriteByte((byte)ClientPacketHeader.PING_RESPONSE);
|
||||
byte requestLen = inc.ReadByte();
|
||||
response.Write(requestLen);
|
||||
response.WriteByte(requestLen);
|
||||
for (int i = 0; i < requestLen; i++)
|
||||
{
|
||||
byte b = inc.ReadByte();
|
||||
response.Write(b);
|
||||
response.WriteByte(b);
|
||||
}
|
||||
clientPeer.Send(response, DeliveryMethod.Unreliable);
|
||||
break;
|
||||
@@ -752,7 +752,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
IWriteMessage readyToStartMsg = new WriteOnlyMessage();
|
||||
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
|
||||
readyToStartMsg.WriteByte((byte)ClientPacketHeader.RESPONSE_STARTGAME);
|
||||
|
||||
if (campaign != null) { campaign.PendingSubmarineSwitch = null; }
|
||||
GameMain.NetLobbyScreen.UsingShuttle = usingShuttle;
|
||||
@@ -770,7 +770,7 @@ namespace Barotrauma.Networking
|
||||
campaign.LastSaveID == campaignSaveID &&
|
||||
campaignUpdateIDs.All(kvp => campaign.GetLastUpdateIdForFlag(kvp.Key) == kvp.Value);
|
||||
}
|
||||
readyToStartMsg.Write(readyToStart);
|
||||
readyToStartMsg.WriteBoolean(readyToStart);
|
||||
|
||||
DebugConsole.Log(readyToStart ? "Ready to start." : "Not ready to start.");
|
||||
|
||||
@@ -1202,7 +1202,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
VoipClient = new VoipClient(this, clientPeer);
|
||||
|
||||
if (Screen.Selected != GameMain.GameScreen && !(Screen.Selected is RoundSummaryScreen))
|
||||
//if we're still in the game, roundsummary or lobby screen, we don't need to redownload the mods
|
||||
if (!(Screen.Selected is GameScreen) && !(Screen.Selected is RoundSummaryScreen) && !(Screen.Selected is NetLobbyScreen))
|
||||
{
|
||||
GameMain.ModDownloadScreen.Select();
|
||||
}
|
||||
@@ -1639,7 +1640,7 @@ namespace Barotrauma.Networking
|
||||
DateTime requestFinalizeTime = DateTime.Now;
|
||||
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
msg.WriteByte((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Unreliable);
|
||||
|
||||
GUIMessageBox interruptPrompt = null;
|
||||
@@ -1653,7 +1654,7 @@ namespace Barotrauma.Networking
|
||||
if (DateTime.Now > requestFinalizeTime)
|
||||
{
|
||||
msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
msg.WriteByte((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Unreliable);
|
||||
requestFinalizeTime = DateTime.Now + requestFinalizeInterval;
|
||||
}
|
||||
@@ -1816,16 +1817,24 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
Submarine refSub = Submarine.MainSub;
|
||||
if (Submarine.MainSubs[1] != null &&
|
||||
GameMain.GameSession.GameMode is PvPMode &&
|
||||
GameMain.GameSession.WinningTeam.HasValue && GameMain.GameSession.WinningTeam == CharacterTeamType.Team1)
|
||||
{
|
||||
refSub = Submarine.MainSubs[1];
|
||||
}
|
||||
|
||||
// Enable characters near the main sub for the endCinematic
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
|
||||
if (Vector2.DistanceSquared(refSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
|
||||
{
|
||||
c.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
EndCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight);
|
||||
EndCinematic = new CameraTransition(refSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight);
|
||||
while (EndCinematic.Running && Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
@@ -1874,7 +1883,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, ServerSubmarines);
|
||||
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.ShuttleList.ListBox, ServerSubmarines);
|
||||
GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.ShuttleList.ListBox, ServerSubmarines.Where(s => s.HasTag(SubmarineTag.Shuttle)));
|
||||
|
||||
gameStarted = inc.ReadBoolean();
|
||||
bool allowSpectating = inc.ReadBoolean();
|
||||
@@ -2073,7 +2082,7 @@ namespace Barotrauma.Networking
|
||||
(isInitialUpdate || initialUpdateReceived))
|
||||
{
|
||||
ReadWriteMessage settingsBuf = new ReadWriteMessage();
|
||||
settingsBuf.Write(settingsData, 0, settingsLen); settingsBuf.BitPosition = 0;
|
||||
settingsBuf.WriteBytes(settingsData, 0, settingsLen); settingsBuf.BitPosition = 0;
|
||||
serverSettings.ClientRead(settingsBuf);
|
||||
if (!IsServerOwner)
|
||||
{
|
||||
@@ -2325,38 +2334,38 @@ namespace Barotrauma.Networking
|
||||
private void SendLobbyUpdate()
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
outmsg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
|
||||
outmsg.Write((byte)ClientNetObject.SYNC_IDS);
|
||||
outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.Write(ChatMessage.LastID);
|
||||
outmsg.Write(LastClientListUpdateID);
|
||||
outmsg.Write(nameId);
|
||||
outmsg.Write(Name);
|
||||
outmsg.WriteByte((byte)ClientNetObject.SYNC_IDS);
|
||||
outmsg.WriteUInt16(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
outmsg.WriteUInt16(nameId);
|
||||
outmsg.WriteString(Name);
|
||||
var jobPreferences = GameMain.NetLobbyScreen.JobPreferences;
|
||||
if (jobPreferences.Count > 0)
|
||||
{
|
||||
outmsg.Write(jobPreferences[0].Prefab.Identifier);
|
||||
outmsg.WriteIdentifier(jobPreferences[0].Prefab.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.Write("");
|
||||
outmsg.WriteIdentifier(Identifier.Empty);
|
||||
}
|
||||
outmsg.Write((byte)MultiplayerPreferences.Instance.TeamPreference);
|
||||
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.Write((UInt16)0);
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.Write(campaign.LastSaveID);
|
||||
outmsg.Write(campaign.CampaignID);
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
{
|
||||
outmsg.Write(campaign.GetLastUpdateIdForFlag(netFlag));
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(netFlag));
|
||||
}
|
||||
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
|
||||
@@ -2369,7 +2378,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
chatMsgQueue[i].ClientWrite(outmsg);
|
||||
}
|
||||
outmsg.Write((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
@@ -2382,29 +2391,29 @@ namespace Barotrauma.Networking
|
||||
private void SendIngameUpdate()
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ClientPacketHeader.UPDATE_INGAME);
|
||||
outmsg.Write(entityEventManager.MidRoundSyncingDone);
|
||||
outmsg.WriteByte((byte)ClientPacketHeader.UPDATE_INGAME);
|
||||
outmsg.WriteBoolean(entityEventManager.MidRoundSyncingDone);
|
||||
outmsg.WritePadBits();
|
||||
|
||||
outmsg.Write((byte)ClientNetObject.SYNC_IDS);
|
||||
outmsg.WriteByte((byte)ClientNetObject.SYNC_IDS);
|
||||
//outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
outmsg.Write(ChatMessage.LastID);
|
||||
outmsg.Write(entityEventManager.LastReceivedID);
|
||||
outmsg.Write(LastClientListUpdateID);
|
||||
outmsg.WriteUInt16(ChatMessage.LastID);
|
||||
outmsg.WriteUInt16(entityEventManager.LastReceivedID);
|
||||
outmsg.WriteUInt16(LastClientListUpdateID);
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
|
||||
{
|
||||
outmsg.Write((UInt16)0);
|
||||
outmsg.WriteUInt16((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outmsg.Write(campaign.LastSaveID);
|
||||
outmsg.Write(campaign.CampaignID);
|
||||
outmsg.WriteUInt16(campaign.LastSaveID);
|
||||
outmsg.WriteByte(campaign.CampaignID);
|
||||
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
|
||||
{
|
||||
outmsg.Write(campaign.GetLastUpdateIdForFlag(flag));
|
||||
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(flag));
|
||||
}
|
||||
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
Character.Controlled?.ClientWriteInput(outmsg);
|
||||
@@ -2423,7 +2432,7 @@ namespace Barotrauma.Networking
|
||||
chatMsgQueue[i].ClientWrite(outmsg);
|
||||
}
|
||||
|
||||
outmsg.Write((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
|
||||
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
@@ -2462,8 +2471,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
WaitForNextRoundRespawn = waitForNextRoundRespawn;
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.READY_TO_SPAWN);
|
||||
msg.Write((bool)waitForNextRoundRespawn);
|
||||
msg.WriteByte((byte)ClientPacketHeader.READY_TO_SPAWN);
|
||||
msg.WriteBoolean((bool)waitForNextRoundRespawn);
|
||||
clientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2475,13 +2484,13 @@ namespace Barotrauma.Networking
|
||||
$"Sending a file request to the server (type: {fileType}, path: {file ?? "null"}");
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.Write((byte)FileTransferMessageType.Initiate);
|
||||
msg.Write((byte)fileType);
|
||||
msg.WriteByte((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.WriteByte((byte)FileTransferMessageType.Initiate);
|
||||
msg.WriteByte((byte)fileType);
|
||||
if (fileType != FileTransferType.CampaignSave)
|
||||
{
|
||||
msg.Write(file ?? throw new ArgumentNullException(nameof(file)));
|
||||
msg.Write(fileHash ?? throw new ArgumentNullException(nameof(fileHash)));
|
||||
msg.WriteString(file ?? throw new ArgumentNullException(nameof(file)));
|
||||
msg.WriteString(fileHash ?? throw new ArgumentNullException(nameof(fileHash)));
|
||||
}
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2500,20 +2509,20 @@ namespace Barotrauma.Networking
|
||||
transfer.RecordOffsetAckTime();
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.Write((byte)FileTransferMessageType.Data);
|
||||
msg.Write((byte)transfer.ID);
|
||||
msg.Write(expecting);
|
||||
msg.Write(lastSeen);
|
||||
msg.WriteByte((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.WriteByte((byte)FileTransferMessageType.Data);
|
||||
msg.WriteByte((byte)transfer.ID);
|
||||
msg.WriteInt32(expecting);
|
||||
msg.WriteInt32(lastSeen);
|
||||
clientPeer.Send(msg, reliable ? DeliveryMethod.Reliable : DeliveryMethod.Unreliable);
|
||||
}
|
||||
|
||||
public void CancelFileTransfer(int id)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.Write((byte)FileTransferMessageType.Cancel);
|
||||
msg.Write((byte)id);
|
||||
msg.WriteByte((byte)ClientPacketHeader.FILE_REQUEST);
|
||||
msg.WriteByte((byte)FileTransferMessageType.Cancel);
|
||||
msg.WriteByte((byte)id);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2547,8 +2556,7 @@ namespace Barotrauma.Networking
|
||||
Color newSubTextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f);
|
||||
subElement.GetChild<GUITextBlock>().TextColor = newSubTextColor;
|
||||
|
||||
GUITextBlock classTextBlock = subElement.GetChildByUserData("classtext") as GUITextBlock;
|
||||
if (classTextBlock != null)
|
||||
if (subElement.GetChildByUserData("classtext") is GUITextBlock classTextBlock)
|
||||
{
|
||||
Color newSubClassTextColor = new Color(classTextBlock.TextColor, 0.8f);
|
||||
classTextBlock.Text = TextManager.Get($"submarineclass.{newSub.SubmarineClass}");
|
||||
@@ -2742,39 +2750,39 @@ namespace Barotrauma.Networking
|
||||
public void SendCharacterInfo(string newName = null)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
|
||||
msg.WriteByte((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
|
||||
WriteCharacterInfo(msg, newName);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
clientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void WriteCharacterInfo(IWriteMessage msg, string newName = null)
|
||||
{
|
||||
msg.Write(characterInfo == null);
|
||||
msg.WriteBoolean(characterInfo == null);
|
||||
if (characterInfo == null) { return; }
|
||||
|
||||
msg.Write(newName ?? string.Empty);
|
||||
msg.WriteString(newName ?? string.Empty);
|
||||
|
||||
msg.Write((byte)characterInfo.Head.Preset.TagSet.Count);
|
||||
msg.WriteByte((byte)characterInfo.Head.Preset.TagSet.Count);
|
||||
foreach (Identifier tag in characterInfo.Head.Preset.TagSet)
|
||||
{
|
||||
msg.Write(tag);
|
||||
msg.WriteIdentifier(tag);
|
||||
}
|
||||
msg.Write((byte)characterInfo.Head.HairIndex);
|
||||
msg.Write((byte)characterInfo.Head.BeardIndex);
|
||||
msg.Write((byte)characterInfo.Head.MoustacheIndex);
|
||||
msg.Write((byte)characterInfo.Head.FaceAttachmentIndex);
|
||||
msg.WriteByte((byte)characterInfo.Head.HairIndex);
|
||||
msg.WriteByte((byte)characterInfo.Head.BeardIndex);
|
||||
msg.WriteByte((byte)characterInfo.Head.MoustacheIndex);
|
||||
msg.WriteByte((byte)characterInfo.Head.FaceAttachmentIndex);
|
||||
msg.WriteColorR8G8B8(characterInfo.Head.SkinColor);
|
||||
msg.WriteColorR8G8B8(characterInfo.Head.HairColor);
|
||||
msg.WriteColorR8G8B8(characterInfo.Head.FacialHairColor);
|
||||
|
||||
var jobPreferences = GameMain.NetLobbyScreen.JobPreferences;
|
||||
int count = Math.Min(jobPreferences.Count, 3);
|
||||
msg.Write((byte)count);
|
||||
msg.WriteByte((byte)count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
msg.Write(jobPreferences[i].Prefab.Identifier);
|
||||
msg.Write((byte)jobPreferences[i].Variant);
|
||||
msg.WriteIdentifier(jobPreferences[i].Prefab.Identifier);
|
||||
msg.WriteByte((byte)jobPreferences[i].Variant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2783,10 +2791,10 @@ namespace Barotrauma.Networking
|
||||
if (clientPeer == null) { return; }
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
msg.Write((byte)ClientNetObject.VOTE);
|
||||
msg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
msg.WriteByte((byte)ClientNetObject.VOTE);
|
||||
Voting.ClientWrite(msg, voteType, data);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2794,7 +2802,6 @@ namespace Barotrauma.Networking
|
||||
public void VoteForKick(Client votedClient)
|
||||
{
|
||||
if (votedClient == null) { return; }
|
||||
votedClient.AddKickVote(ConnectedClients.FirstOrDefault(c => c.SessionId == SessionId));
|
||||
Vote(VoteType.Kick, votedClient);
|
||||
}
|
||||
|
||||
@@ -2840,10 +2847,10 @@ namespace Barotrauma.Networking
|
||||
public override void KickPlayer(string kickedName, string reason)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.Kick);
|
||||
msg.Write(kickedName);
|
||||
msg.Write(reason);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.Kick);
|
||||
msg.WriteString(kickedName);
|
||||
msg.WriteString(reason);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2851,11 +2858,11 @@ namespace Barotrauma.Networking
|
||||
public override void BanPlayer(string kickedName, string reason, TimeSpan? duration = null)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.Ban);
|
||||
msg.Write(kickedName);
|
||||
msg.Write(reason);
|
||||
msg.Write(duration.HasValue ? duration.Value.TotalSeconds : 0.0); //0 = permaban
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.Ban);
|
||||
msg.WriteString(kickedName);
|
||||
msg.WriteString(reason);
|
||||
msg.WriteDouble(duration.HasValue ? duration.Value.TotalSeconds : 0.0); //0 = permaban
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2863,28 +2870,28 @@ namespace Barotrauma.Networking
|
||||
public override void UnbanPlayer(string playerName)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.Unban);
|
||||
msg.Write(true); msg.WritePadBits();
|
||||
msg.Write(playerName);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.Unban);
|
||||
msg.WriteBoolean(true); msg.WritePadBits();
|
||||
msg.WriteString(playerName);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public override void UnbanPlayer(Endpoint endpoint)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.Unban);
|
||||
msg.Write(false); msg.WritePadBits();
|
||||
msg.Write(endpoint.StringRepresentation);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.Unban);
|
||||
msg.WriteBoolean(false); msg.WritePadBits();
|
||||
msg.WriteString(endpoint.StringRepresentation);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void UpdateClientPermissions(Client targetClient)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.ManagePermissions);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ManagePermissions);
|
||||
targetClient.WritePermissions(msg);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2897,10 +2904,10 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.ManageCampaign);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ManageCampaign);
|
||||
campaign.ClientWrite(msg);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2913,12 +2920,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.ConsoleCommands);
|
||||
msg.Write(command);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ConsoleCommands);
|
||||
msg.WriteString(command);
|
||||
Vector2 cursorWorldPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
msg.Write(cursorWorldPos.X);
|
||||
msg.Write(cursorWorldPos.Y);
|
||||
msg.WriteSingle(cursorWorldPos.X);
|
||||
msg.WriteSingle(cursorWorldPos.Y);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2929,10 +2936,10 @@ namespace Barotrauma.Networking
|
||||
public void RequestStartRound(bool continueCampaign = false)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.ManageRound);
|
||||
msg.Write(false); //indicates round start
|
||||
msg.Write(continueCampaign);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ManageRound);
|
||||
msg.WriteBoolean(false); //indicates round start
|
||||
msg.WriteBoolean(continueCampaign);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2940,25 +2947,16 @@ namespace Barotrauma.Networking
|
||||
/// <summary>
|
||||
/// Tell the server to select a submarine (permission required)
|
||||
/// </summary>
|
||||
public void RequestSelectSub(int subIndex, bool isShuttle)
|
||||
public void RequestSelectSub(SubmarineInfo sub, bool isShuttle)
|
||||
{
|
||||
if (!HasPermission(ClientPermissions.SelectSub)) return;
|
||||
|
||||
var subList = isShuttle ? GameMain.NetLobbyScreen.ShuttleList.ListBox : GameMain.NetLobbyScreen.SubList;
|
||||
|
||||
if (subIndex < 0 || subIndex >= subList.Content.CountChildren)
|
||||
{
|
||||
DebugConsole.ThrowError("Submarine index out of bounds (" + subIndex + ")\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return;
|
||||
}
|
||||
if (!HasPermission(ClientPermissions.SelectSub) || sub == null) { return; }
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.SelectSub);
|
||||
msg.Write(isShuttle); msg.WritePadBits();
|
||||
msg.Write((UInt16)subIndex);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.SelectSub);
|
||||
msg.WriteBoolean(isShuttle); msg.WritePadBits();
|
||||
msg.WriteString(sub.MD5Hash.StringRepresentation);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2975,10 +2973,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.SelectMode);
|
||||
msg.Write((UInt16)modeIndex);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.SelectMode);
|
||||
msg.WriteUInt16((UInt16)modeIndex);
|
||||
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -2991,14 +2989,14 @@ namespace Barotrauma.Networking
|
||||
saveName = Path.GetFileNameWithoutExtension(saveName);
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||
msg.WriteByte((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||
|
||||
msg.Write(true); msg.WritePadBits();
|
||||
msg.Write(saveName);
|
||||
msg.Write(mapSeed);
|
||||
msg.Write(sub.Name);
|
||||
msg.Write(sub.MD5Hash.StringRepresentation);
|
||||
msg.Write(settings);
|
||||
msg.WriteBoolean(true); msg.WritePadBits();
|
||||
msg.WriteString(saveName);
|
||||
msg.WriteString(mapSeed);
|
||||
msg.WriteString(sub.Name);
|
||||
msg.WriteString(sub.MD5Hash.StringRepresentation);
|
||||
msg.WriteNetSerializableStruct(settings);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -3011,10 +3009,10 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.CampaignFrame.Visible = false;
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||
msg.WriteByte((byte)ClientPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||
|
||||
msg.Write(false); msg.WritePadBits();
|
||||
msg.Write(saveName);
|
||||
msg.WriteBoolean(false); msg.WritePadBits();
|
||||
msg.WriteString(saveName);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -3025,10 +3023,10 @@ namespace Barotrauma.Networking
|
||||
public void RequestRoundEnd(bool save)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.Write((UInt16)ClientPermissions.ManageRound);
|
||||
msg.Write(true); //indicates round end
|
||||
msg.Write(save);
|
||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||
msg.WriteUInt16((UInt16)ClientPermissions.ManageRound);
|
||||
msg.WriteBoolean(true); //indicates round end
|
||||
msg.WriteBoolean(save);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -3049,11 +3047,11 @@ namespace Barotrauma.Networking
|
||||
if (clientPeer == null) { return false; }
|
||||
|
||||
IWriteMessage readyToStartMsg = new WriteOnlyMessage();
|
||||
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
|
||||
readyToStartMsg.WriteByte((byte)ClientPacketHeader.RESPONSE_STARTGAME);
|
||||
|
||||
//assume we have the required sub files to start the round
|
||||
//(if not, we'll find out when the server sends the STARTGAME message and can initiate a file transfer)
|
||||
readyToStartMsg.Write(true);
|
||||
readyToStartMsg.WriteBoolean(true);
|
||||
|
||||
WriteCharacterInfo(readyToStartMsg);
|
||||
|
||||
@@ -3480,10 +3478,6 @@ namespace Barotrauma.Networking
|
||||
UserData = client,
|
||||
OnClicked = (btn, userdata) => { VoteForKick(client); btn.Enabled = false; return true; }
|
||||
};
|
||||
if (GameMain.NetworkMember.ConnectedClients != null)
|
||||
{
|
||||
kickVoteButton.Enabled = !client.HasKickVoteFromSessionId(SessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3568,21 +3562,21 @@ namespace Barotrauma.Networking
|
||||
public void ReportError(ClientNetError error, UInt16 expectedId = 0, UInt16 eventId = 0, UInt16 entityId = 0)
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)ClientPacketHeader.ERROR);
|
||||
outMsg.Write((byte)error);
|
||||
outMsg.WriteByte((byte)ClientPacketHeader.ERROR);
|
||||
outMsg.WriteByte((byte)error);
|
||||
switch (error)
|
||||
{
|
||||
case ClientNetError.MISSING_EVENT:
|
||||
outMsg.Write(expectedId);
|
||||
outMsg.Write(eventId);
|
||||
outMsg.WriteUInt16(expectedId);
|
||||
outMsg.WriteUInt16(eventId);
|
||||
break;
|
||||
case ClientNetError.MISSING_ENTITY:
|
||||
outMsg.Write(eventId);
|
||||
outMsg.Write(entityId);
|
||||
outMsg.Write((byte)Submarine.Loaded.Count);
|
||||
outMsg.WriteUInt16(eventId);
|
||||
outMsg.WriteUInt16(entityId);
|
||||
outMsg.WriteByte((byte)Submarine.Loaded.Count);
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
outMsg.Write(sub.Info.Name);
|
||||
outMsg.WriteString(sub.Info.Name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ namespace Barotrauma.Networking
|
||||
eventLastSent[entityEvent.ID] = (float)Lidgren.Network.NetTime.Now;
|
||||
}
|
||||
|
||||
msg.Write((byte)ClientNetObject.ENTITY_STATE);
|
||||
msg.WriteByte((byte)ClientNetObject.ENTITY_STATE);
|
||||
Write(msg, eventsToSync, out _);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Write(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(CharacterStateID);
|
||||
msg.WriteUInt16(CharacterStateID);
|
||||
serializable.ClientEventWrite(msg, Data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public override void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
msg.Write(NetStateID);
|
||||
msg.WriteByte((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.WriteRangedInteger((int)ChatMode.None, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
|
||||
WriteOrder(msg);
|
||||
|
||||
+77
-102
@@ -1,60 +1,23 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Steam;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
abstract class ClientPeer
|
||||
internal abstract class ClientPeer
|
||||
{
|
||||
public class ServerContentPackage
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly Md5Hash Hash;
|
||||
public readonly UInt64 WorkshopId;
|
||||
public readonly DateTime InstallTime;
|
||||
|
||||
public RegularPackage? RegularPackage
|
||||
{
|
||||
get
|
||||
{
|
||||
return ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Hash.Equals(Hash));
|
||||
}
|
||||
}
|
||||
|
||||
public CorePackage? CorePackage
|
||||
{
|
||||
get
|
||||
{
|
||||
return ContentPackageManager.CorePackages.FirstOrDefault(p => p.Hash.Equals(Hash));
|
||||
}
|
||||
}
|
||||
|
||||
public ContentPackage? ContentPackage
|
||||
=> (ContentPackage?)RegularPackage ?? CorePackage;
|
||||
|
||||
|
||||
public string GetPackageStr()
|
||||
=> $"\"{Name}\" (hash {Hash.ShortRepresentation})";
|
||||
|
||||
public ServerContentPackage(string name, Md5Hash hash, UInt64 workshopId, DateTime installTime)
|
||||
{
|
||||
Name = name;
|
||||
Hash = hash;
|
||||
WorkshopId = workshopId;
|
||||
InstallTime = installTime;
|
||||
}
|
||||
}
|
||||
|
||||
public ImmutableArray<ServerContentPackage> ServerContentPackages { get; set; } =
|
||||
ImmutableArray<ServerContentPackage>.Empty;
|
||||
|
||||
public delegate void MessageCallback(IReadMessage message);
|
||||
|
||||
public delegate void DisconnectCallback(bool disableReconnect);
|
||||
|
||||
public delegate void DisconnectMessageCallback(string message);
|
||||
|
||||
public delegate void PasswordCallback(int salt, int retries);
|
||||
|
||||
public delegate void InitializationCompleteCallback();
|
||||
|
||||
[Obsolete("TODO: delete in nr3-layer-1-2-cleanup")]
|
||||
@@ -65,8 +28,12 @@ namespace Barotrauma.Networking
|
||||
public readonly DisconnectMessageCallback OnDisconnectMessageReceived;
|
||||
public readonly PasswordCallback OnRequestPassword;
|
||||
public readonly InitializationCompleteCallback OnInitializationComplete;
|
||||
|
||||
public Callbacks(MessageCallback onMessageReceived, DisconnectCallback onDisconnect, DisconnectMessageCallback onDisconnectMessageReceived, PasswordCallback onRequestPassword, InitializationCompleteCallback onInitializationComplete)
|
||||
|
||||
public Callbacks(MessageCallback onMessageReceived,
|
||||
DisconnectCallback onDisconnect,
|
||||
DisconnectMessageCallback onDisconnectMessageReceived,
|
||||
PasswordCallback onRequestPassword,
|
||||
InitializationCompleteCallback onInitializationComplete)
|
||||
{
|
||||
OnMessageReceived = onMessageReceived;
|
||||
OnDisconnect = onDisconnect;
|
||||
@@ -91,98 +58,106 @@ namespace Barotrauma.Networking
|
||||
this.ownerKey = ownerKey;
|
||||
isOwner = ownerKey.IsSome();
|
||||
}
|
||||
|
||||
|
||||
public abstract void Start();
|
||||
public abstract void Close(string? msg = null, bool disableReconnect = false);
|
||||
public abstract void Update(float deltaTime);
|
||||
public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
|
||||
public abstract void SendPassword(string password);
|
||||
|
||||
protected abstract void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg);
|
||||
protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body);
|
||||
|
||||
protected ConnectionInitialization initializationStep;
|
||||
public bool ContentPackageOrderReceived { get; protected set; }
|
||||
protected int passwordSalt;
|
||||
protected Steamworks.AuthTicket? steamAuthTicket;
|
||||
protected void ReadConnectionInitializationStep(IReadMessage inc)
|
||||
|
||||
public struct IncomingInitializationMessage
|
||||
{
|
||||
ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();
|
||||
public ConnectionInitialization InitializationStep;
|
||||
public IReadMessage Message;
|
||||
}
|
||||
|
||||
IWriteMessage outMsg;
|
||||
|
||||
switch (step)
|
||||
protected void ReadConnectionInitializationStep(IncomingInitializationMessage inc)
|
||||
{
|
||||
switch (inc.InitializationStep)
|
||||
{
|
||||
case ConnectionInitialization.SteamTicketAndVersion:
|
||||
{
|
||||
if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; }
|
||||
outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
|
||||
outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
|
||||
outMsg.Write(GameMain.Client.Name);
|
||||
outMsg.Write(ownerKey.Fallback(0));
|
||||
outMsg.Write(SteamManager.GetSteamId().Select(steamId => steamId.Value).Fallback(0));
|
||||
if (steamAuthTicket == null)
|
||||
{
|
||||
outMsg.Write((UInt16)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.Write((UInt16)steamAuthTicket.Data.Length);
|
||||
outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
|
||||
}
|
||||
outMsg.Write(GameMain.Version.ToString());
|
||||
outMsg.Write(GameSettings.CurrentConfig.Language.Value);
|
||||
|
||||
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
|
||||
PeerPacketHeaders headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.SteamTicketAndVersion
|
||||
};
|
||||
|
||||
ClientSteamTicketAndVersionPacket body = new ClientSteamTicketAndVersionPacket
|
||||
{
|
||||
Name = GameMain.Client.Name,
|
||||
OwnerKey = ownerKey,
|
||||
SteamId = SteamManager.GetSteamId().Select(id => (AccountId)id),
|
||||
SteamAuthTicket = steamAuthTicket switch
|
||||
{
|
||||
null => Option<byte[]>.None(),
|
||||
var ticket => Option<byte[]>.Some(ticket.Data)
|
||||
},
|
||||
GameVersion = GameMain.Version.ToString(),
|
||||
Language = GameSettings.CurrentConfig.Language.Value
|
||||
};
|
||||
|
||||
SendMsgInternal(headers, body);
|
||||
break;
|
||||
}
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
{
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
|
||||
initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; }
|
||||
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
|
||||
outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
|
||||
outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);
|
||||
|
||||
UInt32 packageCount = inc.ReadVariableUInt32();
|
||||
List<ServerContentPackage> serverPackages = new List<ServerContentPackage>();
|
||||
for (int i = 0; i < packageCount; i++)
|
||||
initializationStep == ConnectionInitialization.Password)
|
||||
{
|
||||
string name = inc.ReadString();
|
||||
UInt32 hashByteCount = inc.ReadVariableUInt32();
|
||||
byte[] hashBytes = inc.ReadBytes((int)hashByteCount);
|
||||
UInt64 workshopId = inc.ReadUInt64();
|
||||
UInt32 installTimeDiffSeconds = inc.ReadUInt32();
|
||||
DateTime installTime = DateTime.UtcNow + TimeSpan.FromSeconds(installTimeDiffSeconds);
|
||||
|
||||
var pkg = new ServerContentPackage(name, Md5Hash.BytesAsHash(hashBytes), workshopId, installTime);
|
||||
serverPackages.Add(pkg);
|
||||
initializationStep = ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
|
||||
if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
|
||||
|
||||
PeerPacketHeaders headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.ContentPackageOrder
|
||||
};
|
||||
|
||||
var orderPacket = INetSerializableStruct.Read<ServerPeerContentPackageOrderPacket>(inc.Message);
|
||||
|
||||
if (!ContentPackageOrderReceived)
|
||||
{
|
||||
ServerContentPackages = serverPackages.ToImmutableArray();
|
||||
if (serverPackages.Count == 0)
|
||||
ServerContentPackages = orderPacket.ContentPackages;
|
||||
if (ServerContentPackages.Length == 0)
|
||||
{
|
||||
string errorMsg = "Error in ContentPackageOrder message: list of content packages enabled on the server was empty.";
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientPeer.ReadConnectionInitializationStep:NoContentPackages", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
}
|
||||
ContentPackageOrderReceived = true;
|
||||
SendMsgInternal(DeliveryMethod.Reliable, outMsg);
|
||||
|
||||
SendMsgInternal(headers, null);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ConnectionInitialization.Password:
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = 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();
|
||||
}
|
||||
|
||||
var passwordPacket = INetSerializableStruct.Read<ServerPeerPasswordPacket>(inc.Message);
|
||||
|
||||
passwordPacket.Salt.TryUnwrap(out passwordSalt);
|
||||
passwordPacket.RetriesLeft.TryUnwrap(out var retries);
|
||||
|
||||
callbacks.OnRequestPassword.Invoke(passwordSalt, retries);
|
||||
break;
|
||||
}
|
||||
@@ -192,4 +167,4 @@ namespace Barotrauma.Networking
|
||||
public abstract void ForceTimeOut();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+105
-72
@@ -1,21 +1,22 @@
|
||||
using Barotrauma.Steam;
|
||||
using Lidgren.Network;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Lidgren.Network;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class LidgrenClientPeer : ClientPeer
|
||||
internal sealed class LidgrenClientPeer : ClientPeer
|
||||
{
|
||||
private bool isActive;
|
||||
private NetClient netClient;
|
||||
private NetPeerConfiguration netPeerConfiguration;
|
||||
private NetClient? netClient;
|
||||
private readonly NetPeerConfiguration netPeerConfiguration;
|
||||
|
||||
List<NetIncomingMessage> incomingLidgrenMessages;
|
||||
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
|
||||
|
||||
private LidgrenEndpoint lidgrenEndpoint
|
||||
=> ServerConnection is LidgrenConnection { Endpoint: LidgrenEndpoint result }
|
||||
private LidgrenEndpoint lidgrenEndpoint =>
|
||||
ServerConnection is LidgrenConnection { Endpoint: LidgrenEndpoint result }
|
||||
? result
|
||||
: throw new InvalidOperationException();
|
||||
|
||||
@@ -25,13 +26,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
netClient = null;
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
if (isActive) { return; }
|
||||
|
||||
ContentPackageOrderReceived = false;
|
||||
|
||||
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
|
||||
{
|
||||
@@ -45,6 +39,17 @@ namespace Barotrauma.Networking
|
||||
| NetIncomingMessageType.ErrorMessage
|
||||
| NetIncomingMessageType.Error);
|
||||
|
||||
incomingLidgrenMessages = new List<NetIncomingMessage>();
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
if (isActive) { return; }
|
||||
|
||||
incomingLidgrenMessages.Clear();
|
||||
|
||||
ContentPackageOrderReceived = false;
|
||||
|
||||
netClient = new NetClient(netPeerConfiguration);
|
||||
|
||||
if (SteamManager.IsInitialized)
|
||||
@@ -56,14 +61,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
incomingLidgrenMessages = new List<NetIncomingMessage>();
|
||||
|
||||
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
|
||||
if (!(ServerEndpoint is LidgrenEndpoint lidgrenEndpoint))
|
||||
if (!(ServerEndpoint is LidgrenEndpoint lidgrenEndpointValue))
|
||||
{
|
||||
throw new InvalidCastException("endPoint is not IPEndPoint");
|
||||
throw new InvalidCastException($"Endpoint is not {nameof(LidgrenEndpoint)}");
|
||||
}
|
||||
|
||||
if (ServerConnection != null)
|
||||
{
|
||||
throw new InvalidOperationException("ServerConnection is not null");
|
||||
@@ -71,8 +75,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
netClient.Start();
|
||||
|
||||
var netConnection = netClient.Connect(lidgrenEndpoint.NetEndpoint);
|
||||
|
||||
var netConnection = netClient.Connect(lidgrenEndpointValue.NetEndpoint);
|
||||
|
||||
ServerConnection = new LidgrenConnection(netConnection)
|
||||
{
|
||||
Status = NetworkConnectionStatus.Connected
|
||||
@@ -85,11 +89,18 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
||||
|
||||
if (isOwner && !(ChildServerRelay.Process is { HasExited: false }))
|
||||
{
|
||||
Close();
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) => { GameMain.MainMenuScreen.Select(); return false; };
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +112,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
|
||||
{
|
||||
if (!inc.SenderConnection.RemoteEndPoint.Equals(lidgrenEndpoint.NetEndpoint)) { continue; }
|
||||
if (!inc.SenderConnection.RemoteEndPoint.Equals(lidgrenEndpoint.NetEndpoint))
|
||||
{
|
||||
DebugConsole.AddWarning($"Mismatched endpoint: expected {lidgrenEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (inc.MessageType)
|
||||
{
|
||||
@@ -115,15 +130,23 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDataMessage(NetIncomingMessage inc)
|
||||
private void HandleDataMessage(NetIncomingMessage lidgrenMsg)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
ToolBox.ThrowIfNull(ServerConnection);
|
||||
|
||||
IReadMessage inc = lidgrenMsg.ToReadMessage();
|
||||
|
||||
var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep() && initializationStep != ConnectionInitialization.Success)
|
||||
{
|
||||
ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
|
||||
ReadConnectionInitializationStep(new IncomingInitializationMessage
|
||||
{
|
||||
InitializationStep = initialization ?? throw new Exception("Initialization step missing"),
|
||||
Message = inc
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,9 +155,9 @@ namespace Barotrauma.Networking
|
||||
callbacks.OnInitializationComplete.Invoke();
|
||||
initializationStep = ConnectionInitialization.Success;
|
||||
}
|
||||
UInt16 length = inc.ReadUInt16();
|
||||
IReadMessage msg = new ReadOnlyMessage(inc.Data, packetHeader.IsCompressed(), inc.PositionInBytes, length, ServerConnection);
|
||||
callbacks.OnMessageReceived.Invoke(msg);
|
||||
|
||||
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
callbacks.OnMessageReceived.Invoke(packet.GetReadMessage(packetHeader.IsCompressed(), ServerConnection));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +165,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
NetConnectionStatus status = (NetConnectionStatus)inc.ReadByte();
|
||||
NetConnectionStatus status = inc.ReadHeader<NetConnectionStatus>();
|
||||
switch (status)
|
||||
{
|
||||
case NetConnectionStatus.Disconnected:
|
||||
@@ -157,29 +180,38 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
|
||||
if (initializationStep != ConnectionInitialization.Password) { return; }
|
||||
NetOutgoingMessage outMsg = netClient.CreateMessage();
|
||||
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
|
||||
outMsg.Write((byte)ConnectionInitialization.Password);
|
||||
byte[] saltedPw = ServerSettings.SaltPassword(Encoding.UTF8.GetBytes(password), passwordSalt);
|
||||
outMsg.Write((byte)saltedPw.Length);
|
||||
outMsg.Write(saltedPw, 0, saltedPw.Length);
|
||||
NetSendResult result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered);
|
||||
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
|
||||
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send " + initializationStep.ToString() + " message to host: " + result);
|
||||
}
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.Password
|
||||
};
|
||||
var body = new ClientPeerPasswordPacket
|
||||
{
|
||||
Password = ServerSettings.SaltPassword(Encoding.UTF8.GetBytes(password), passwordSalt)
|
||||
};
|
||||
|
||||
SendMsgInternal(headers, body);
|
||||
}
|
||||
|
||||
public override void Close(string msg = null, bool disableReconnect = false)
|
||||
public override void Close(string? msg = null, bool disableReconnect = false)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
|
||||
isActive = false;
|
||||
|
||||
netClient.Shutdown(msg ?? TextManager.Get("Disconnecting").Value);
|
||||
netClient = null;
|
||||
steamAuthTicket?.Cancel(); steamAuthTicket = null;
|
||||
|
||||
steamAuthTicket?.Cancel();
|
||||
steamAuthTicket = null;
|
||||
|
||||
callbacks.OnDisconnect.Invoke(disableReconnect);
|
||||
}
|
||||
|
||||
@@ -187,19 +219,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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;
|
||||
}
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
ToolBox.ThrowIfNull(netPeerConfiguration);
|
||||
|
||||
#if DEBUG
|
||||
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
|
||||
@@ -208,30 +229,42 @@ namespace Barotrauma.Networking
|
||||
netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
|
||||
#endif
|
||||
|
||||
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
|
||||
byte[] msgData = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
|
||||
lidgrenMsg.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
|
||||
lidgrenMsg.Write((UInt16)length);
|
||||
lidgrenMsg.Write(msgData, 0, length);
|
||||
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
|
||||
|
||||
NetSendResult result = netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod);
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
|
||||
Initialization = null
|
||||
};
|
||||
var body = new PeerPacketMessage
|
||||
{
|
||||
Buffer = bufAux
|
||||
};
|
||||
|
||||
SendMsgInternal(headers, body);
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteNetSerializableStruct(headers);
|
||||
body?.Write(msg);
|
||||
|
||||
NetSendResult result = ForwardToLidgren(msg, DeliveryMethod.Reliable);
|
||||
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send message to host: " + result);
|
||||
DebugConsole.NewMessage($"Failed to send message to host: {result}\n{Environment.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
private NetSendResult ForwardToLidgren(IWriteMessage msg, DeliveryMethod deliveryMethod)
|
||||
{
|
||||
NetOutgoingMessage lidgrenMsg = netClient.CreateMessage();
|
||||
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
|
||||
NetSendResult result = netClient.SendMessage(lidgrenMsg, NetDeliveryMethod.ReliableUnordered);
|
||||
if (result != NetSendResult.Queued && result != NetSendResult.Sent)
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send message to host: " + result + "\n" + Environment.StackTrace);
|
||||
}
|
||||
return netClient.SendMessage(msg.ToLidgren(netClient), deliveryMethod.ToLidgren());
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -241,4 +274,4 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+141
-167
@@ -1,13 +1,14 @@
|
||||
using Barotrauma.Steam;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.Steam;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class SteamP2PClientPeer : ClientPeer
|
||||
internal sealed class SteamP2PClientPeer : ClientPeer
|
||||
{
|
||||
private bool isActive;
|
||||
private readonly SteamId hostSteamId;
|
||||
@@ -17,20 +18,20 @@ namespace Barotrauma.Networking
|
||||
|
||||
private long sentBytes, receivedBytes;
|
||||
|
||||
private List<IReadMessage> incomingInitializationMessages;
|
||||
private List<IReadMessage> incomingDataMessages;
|
||||
private readonly List<IncomingInitializationMessage> incomingInitializationMessages = new List<IncomingInitializationMessage>();
|
||||
private readonly List<IReadMessage> incomingDataMessages = new List<IReadMessage>();
|
||||
|
||||
public SteamP2PClientPeer(SteamP2PEndpoint endpoint, Callbacks callbacks) : base(endpoint, callbacks, Option<int>.None())
|
||||
{
|
||||
ServerConnection = null;
|
||||
|
||||
isActive = false;
|
||||
|
||||
|
||||
if (!(ServerEndpoint is SteamP2PEndpoint steamIdEndpoint))
|
||||
{
|
||||
throw new InvalidCastException("endPoint is not SteamId");
|
||||
}
|
||||
|
||||
|
||||
hostSteamId = steamIdEndpoint.SteamId;
|
||||
}
|
||||
|
||||
@@ -55,16 +56,13 @@ namespace Barotrauma.Networking
|
||||
ServerConnection = new SteamP2PConnection(hostSteamId);
|
||||
ServerConnection.SetAccountInfo(new AccountInfo(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);
|
||||
|
||||
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
|
||||
sentBytes += outMsg.LengthBytes;
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.ConnectionStarted
|
||||
};
|
||||
SendMsgInternal(headers, null);
|
||||
|
||||
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
|
||||
@@ -78,6 +76,7 @@ namespace Barotrauma.Networking
|
||||
private void OnIncomingConnection(Steamworks.SteamId steamId)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
if (steamId == hostSteamId.Value)
|
||||
{
|
||||
Steamworks.SteamNetworking.AcceptP2PSessionWithUser(steamId);
|
||||
@@ -86,16 +85,18 @@ namespace Barotrauma.Networking
|
||||
initializationStep != ConnectionInitialization.ContentPackageOrder &&
|
||||
initializationStep != ConnectionInitialization.Success)
|
||||
{
|
||||
DebugConsole.ThrowError($"Connection from incorrect SteamID was rejected: "+
|
||||
$"expected {hostSteamId}," +
|
||||
$"got {new SteamId(steamId)}");
|
||||
DebugConsole.ThrowError("Connection from incorrect SteamID was rejected: " +
|
||||
$"expected {hostSteamId}," +
|
||||
$"got {new SteamId(steamId)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConnectionFailed(Steamworks.SteamId steamId, Steamworks.P2PSessionError error)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
if (steamId != hostSteamId.Value) { return; }
|
||||
|
||||
Close($"SteamP2P connection failed: {error}");
|
||||
callbacks.OnDisconnectMessageReceived.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P connection failed: {error}");
|
||||
}
|
||||
@@ -103,27 +104,31 @@ namespace Barotrauma.Networking
|
||||
private void OnP2PData(ulong steamId, byte[] data, int dataLength)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
if (steamId != hostSteamId.Value) { return; }
|
||||
|
||||
timeout = Screen.Selected == GameMain.GameScreen ?
|
||||
NetworkConnection.TimeoutThresholdInGame :
|
||||
NetworkConnection.TimeoutThreshold;
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)data[0];
|
||||
timeout = Screen.Selected == GameMain.GameScreen
|
||||
? NetworkConnection.TimeoutThresholdInGame
|
||||
: NetworkConnection.TimeoutThreshold;
|
||||
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 0, dataLength, ServerConnection);
|
||||
|
||||
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (!packetHeader.IsServerMessage()) { return; }
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
if (packetHeader.IsConnectionInitializationStep() && initialization.HasValue)
|
||||
{
|
||||
ulong low = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8);
|
||||
ulong high = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8 + 32);
|
||||
ulong lobbyId = low + (high << 32);
|
||||
var relayPacket = INetSerializableStruct.Read<SteamP2PInitializationRelayPacket>(inc);
|
||||
|
||||
Steam.SteamManager.JoinLobby(lobbyId, false);
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 1 + 8, dataLength - (1 + 8), ServerConnection);
|
||||
SteamManager.JoinLobby(relayPacket.LobbyID, false);
|
||||
if (initializationStep != ConnectionInitialization.Success)
|
||||
{
|
||||
incomingInitializationMessages.Add(inc);
|
||||
incomingInitializationMessages.Add(new IncomingInitializationMessage
|
||||
{
|
||||
InitializationStep = initialization.Value,
|
||||
Message = relayPacket.Message.GetReadMessage()
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (packetHeader.IsHeartbeatMessage())
|
||||
@@ -132,17 +137,14 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
IReadMessage inc = new ReadOnlyMessage(data, false, 1, dataLength - 1, ServerConnection);
|
||||
string msg = inc.ReadString();
|
||||
Close(msg);
|
||||
callbacks.OnDisconnectMessageReceived.Invoke(msg);
|
||||
PeerDisconnectPacket packet = INetSerializableStruct.Read<PeerDisconnectPacket>(inc);
|
||||
Close(packet.Message);
|
||||
callbacks.OnDisconnectMessageReceived.Invoke(packet.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt16 length = Lidgren.Network.NetBitWriter.ReadUInt16(data, 16, 8);
|
||||
|
||||
IReadMessage inc = new ReadOnlyMessage(data, packetHeader.IsCompressed(), 3, length, ServerConnection);
|
||||
incomingDataMessages.Add(inc);
|
||||
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
incomingDataMessages.Add(packet.GetReadMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +156,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
timeout -= deltaTime;
|
||||
}
|
||||
|
||||
heartbeatTimer -= deltaTime;
|
||||
|
||||
if (initializationStep != ConnectionInitialization.Password &&
|
||||
@@ -163,20 +166,20 @@ namespace Barotrauma.Networking
|
||||
connectionStatusTimer -= deltaTime;
|
||||
if (connectionStatusTimer <= 0.0)
|
||||
{
|
||||
var state = Steamworks.SteamNetworking.GetP2PSessionState(hostSteamId.Value);
|
||||
if (state == null)
|
||||
if (Steamworks.SteamNetworking.GetP2PSessionState(hostSteamId.Value) is { } state)
|
||||
{
|
||||
if (state.P2PSessionError != Steamworks.P2PSessionError.None)
|
||||
{
|
||||
Close($"SteamP2P error code: {state.P2PSessionError}");
|
||||
callbacks.OnDisconnectMessageReceived.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P error code: {state.P2PSessionError}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Close("SteamP2P connection could not be established");
|
||||
callbacks.OnDisconnectMessageReceived.Invoke(DisconnectReason.SteamP2PError.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state?.P2PSessionError != Steamworks.P2PSessionError.None)
|
||||
{
|
||||
Close($"SteamP2P error code: {state?.P2PSessionError}");
|
||||
callbacks.OnDisconnectMessageReceived.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P error code: {state?.P2PSessionError}");
|
||||
}
|
||||
}
|
||||
|
||||
connectionStatusTimer = 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -184,11 +187,12 @@ namespace Barotrauma.Networking
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; }
|
||||
|
||||
var packet = Steamworks.SteamNetworking.ReadP2PPacket();
|
||||
if (packet.HasValue)
|
||||
if (packet is { SteamId: var steamId, Data: var data })
|
||||
{
|
||||
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0);
|
||||
receivedBytes += packet?.Data.Length ?? 0;
|
||||
OnP2PData(steamId, data, data.Length);
|
||||
receivedBytes += data.Length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +201,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (heartbeatTimer < 0.0)
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)DeliveryMethod.Unreliable);
|
||||
outMsg.Write((byte)PacketHeader.IsHeartbeatMessage);
|
||||
|
||||
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Unreliable);
|
||||
sentBytes += outMsg.LengthBytes;
|
||||
|
||||
heartbeatTimer = 5.0;
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Unreliable,
|
||||
PacketHeader = PacketHeader.IsHeartbeatMessage,
|
||||
Initialization = null
|
||||
};
|
||||
SendMsgInternal(headers, null);
|
||||
}
|
||||
|
||||
if (timeout < 0.0)
|
||||
@@ -238,7 +241,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (IReadMessage inc in incomingInitializationMessages)
|
||||
foreach (var inc in incomingInitializationMessages)
|
||||
{
|
||||
ReadConnectionInitializationStep(inc);
|
||||
}
|
||||
@@ -261,76 +264,40 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
byte[] buf = new byte[msg.LengthBytes + 4];
|
||||
buf[0] = (byte)deliveryMethod;
|
||||
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
|
||||
|
||||
byte[] bufAux = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref bufAux, compressPastThreshold, out bool isCompressed, out int 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);
|
||||
|
||||
Steamworks.P2PSend sendType;
|
||||
switch (deliveryMethod)
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
case DeliveryMethod.Reliable:
|
||||
case DeliveryMethod.ReliableOrdered:
|
||||
//the documentation seems to suggest that the Reliable send type
|
||||
//enforces packet order (TODO: verify)
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
break;
|
||||
default:
|
||||
sendType = Steamworks.P2PSend.Unreliable;
|
||||
break;
|
||||
}
|
||||
|
||||
if (length + 8 >= MsgConstants.MTU)
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
|
||||
Initialization = null
|
||||
};
|
||||
var body = new PeerPacketMessage
|
||||
{
|
||||
DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + length.ToString() + " bytes)");
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
}
|
||||
Buffer = bufAux
|
||||
};
|
||||
|
||||
heartbeatTimer = 5.0;
|
||||
|
||||
// Using an extra local method here to reduce chance of error whenever we need to change this
|
||||
void performSend() => SendMsgInternal(headers, body);
|
||||
#if DEBUG
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && sendType != Steamworks.P2PSend.Reliable) { return; }
|
||||
int count = Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedDuplicatesChance ? 2 : 1;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Send(buf, length + 4, sendType);
|
||||
}
|
||||
},
|
||||
GameMain.Client.SimulatedMinimumLatency + Rand.Range(0.0f, GameMain.Client.SimulatedRandomLatency));
|
||||
#else
|
||||
Send(buf, length + 4, sendType);
|
||||
#endif
|
||||
}
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
private void Send(byte[] buf, int length, Steamworks.P2PSend sendType)
|
||||
{
|
||||
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, buf, length + 4, 0, sendType);
|
||||
sentBytes += length + 4;
|
||||
if (!successSend)
|
||||
{
|
||||
if (sendType != Steamworks.P2PSend.Reliable)
|
||||
{
|
||||
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + length.ToString() + " bytes)");
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, buf, length + 4, 0, sendType);
|
||||
sentBytes += length + 4;
|
||||
}
|
||||
if (!successSend)
|
||||
{
|
||||
DebugConsole.AddWarning("Failed to send message to remote peer! (" + length.ToString() + " bytes)");
|
||||
}
|
||||
}
|
||||
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && deliveryMethod is DeliveryMethod.Unreliable) { return; }
|
||||
|
||||
int count = Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedDuplicatesChance ? 2 : 1;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
performSend();
|
||||
}
|
||||
},
|
||||
GameMain.Client.SimulatedMinimumLatency + Rand.Range(0.0f, GameMain.Client.SimulatedRandomLatency));
|
||||
#else
|
||||
performSend();
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void SendPassword(string password)
|
||||
@@ -338,20 +305,22 @@ namespace Barotrauma.Networking
|
||||
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(Encoding.UTF8.GetBytes(password), passwordSalt);
|
||||
outMsg.Write((byte)saltedPw.Length);
|
||||
outMsg.Write(saltedPw, 0, saltedPw.Length);
|
||||
|
||||
heartbeatTimer = 5.0;
|
||||
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
|
||||
sentBytes += outMsg.LengthBytes;
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.Password
|
||||
};
|
||||
var body = new ClientPeerPasswordPacket
|
||||
{
|
||||
Password = ServerSettings.SaltPassword(Encoding.UTF8.GetBytes(password), passwordSalt)
|
||||
};
|
||||
|
||||
SendMsgInternal(headers, body);
|
||||
}
|
||||
|
||||
public override void Close(string msg = null, bool disableReconnect = false)
|
||||
|
||||
public override void Close(string? msg = null, bool disableReconnect = false)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
@@ -359,54 +328,59 @@ namespace Barotrauma.Networking
|
||||
|
||||
isActive = false;
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)DeliveryMethod.Reliable);
|
||||
outMsg.Write((byte)PacketHeader.IsDisconnectMessage);
|
||||
outMsg.Write(msg ?? "Disconnected");
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsDisconnectMessage,
|
||||
Initialization = null
|
||||
};
|
||||
var body = new PeerDisconnectPacket
|
||||
{
|
||||
Message = msg ?? "Disconnected"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
|
||||
sentBytes += outMsg.LengthBytes;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to send a disconnect message to the server using SteamP2P.", e);
|
||||
}
|
||||
SendMsgInternal(headers, body);
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
||||
Steamworks.SteamNetworking.ResetActions();
|
||||
Steamworks.SteamNetworking.CloseP2PSessionWithUser(hostSteamId.Value);
|
||||
|
||||
steamAuthTicket?.Cancel(); steamAuthTicket = null;
|
||||
steamAuthTicket?.Cancel();
|
||||
steamAuthTicket = null;
|
||||
|
||||
callbacks.OnDisconnect.Invoke(disableReconnect);
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
Steamworks.P2PSend sendType;
|
||||
switch (deliveryMethod)
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
msgToSend.WriteNetSerializableStruct(headers);
|
||||
body?.Write(msgToSend);
|
||||
ForwardToSteamP2P(msgToSend, headers.DeliveryMethod);
|
||||
}
|
||||
|
||||
private void ForwardToSteamP2P(IWriteMessage msg, DeliveryMethod deliveryMethod)
|
||||
{
|
||||
heartbeatTimer = 5.0;
|
||||
int length = msg.LengthBytes;
|
||||
|
||||
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, msg.Buffer, length, 0, deliveryMethod.ToSteam());
|
||||
sentBytes += length;
|
||||
|
||||
if (successSend) { return; }
|
||||
|
||||
if (deliveryMethod is DeliveryMethod.Unreliable)
|
||||
{
|
||||
case DeliveryMethod.Reliable:
|
||||
case DeliveryMethod.ReliableOrdered:
|
||||
//the documentation seems to suggest that the Reliable send type
|
||||
//enforces packet order (TODO: verify)
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
break;
|
||||
default:
|
||||
sendType = Steamworks.P2PSend.Unreliable;
|
||||
break;
|
||||
DebugConsole.Log($"WARNING: message couldn't be sent unreliably, forcing reliable send ({length} bytes)");
|
||||
successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, msg.Buffer, length, 0, DeliveryMethod.Reliable.ToSteam());
|
||||
sentBytes += length;
|
||||
}
|
||||
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
msgToSend.Write((byte)deliveryMethod);
|
||||
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
|
||||
heartbeatTimer = 5.0;
|
||||
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId.Value, msgToSend.Buffer, msgToSend.LengthBytes, 0, sendType);
|
||||
sentBytes += msg.LengthBytes;
|
||||
if (!successSend)
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to send message to remote peer! ({length} bytes)");
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -416,4 +390,4 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+239
-208
@@ -1,38 +1,46 @@
|
||||
using Barotrauma.Extensions;
|
||||
#nullable enable
|
||||
using Barotrauma.Steam;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class SteamP2POwnerPeer : ClientPeer
|
||||
sealed class SteamP2POwnerPeer : ClientPeer
|
||||
{
|
||||
private bool isActive;
|
||||
|
||||
private readonly SteamId selfSteamID;
|
||||
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Fallback(0));
|
||||
|
||||
private SteamId ReadSteamId(IReadMessage inc)
|
||||
=> new SteamId(inc.ReadUInt64() ^ ownerKey64);
|
||||
private void WriteSteamId(IWriteMessage msg, SteamId val)
|
||||
=> msg.Write(val.Value ^ ownerKey64);
|
||||
|
||||
private SteamId ReadSteamId(IReadMessage inc) => new SteamId(inc.ReadUInt64() ^ ownerKey64);
|
||||
private void WriteSteamId(IWriteMessage msg, SteamId val) => msg.WriteUInt64(val.Value ^ ownerKey64);
|
||||
|
||||
private long sentBytes, receivedBytes;
|
||||
|
||||
class RemotePeer
|
||||
private sealed class RemotePeer
|
||||
{
|
||||
public SteamId SteamId;
|
||||
public readonly SteamId SteamId;
|
||||
public Option<SteamId> OwnerSteamId;
|
||||
public double? DisconnectTime;
|
||||
public bool Authenticating;
|
||||
public bool Authenticated;
|
||||
|
||||
public class UnauthedMessage
|
||||
public readonly struct UnauthedMessage
|
||||
{
|
||||
public DeliveryMethod DeliveryMethod;
|
||||
public IWriteMessage Message;
|
||||
public readonly SteamId Sender;
|
||||
public readonly byte[] Bytes;
|
||||
public readonly int Length;
|
||||
|
||||
public UnauthedMessage(SteamId sender, byte[] bytes)
|
||||
{
|
||||
Sender = sender;
|
||||
Bytes = bytes;
|
||||
Length = bytes.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly List<UnauthedMessage> UnauthedMessages;
|
||||
|
||||
public RemotePeer(SteamId steamId)
|
||||
@@ -45,9 +53,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
UnauthedMessages = new List<UnauthedMessage>();
|
||||
}
|
||||
|
||||
}
|
||||
List<RemotePeer> remotePeers;
|
||||
|
||||
private List<RemotePeer> remotePeers = null!;
|
||||
|
||||
public SteamP2POwnerPeer(Callbacks callbacks, int ownerKey) : base(new PipeEndpoint(), callbacks, Option<int>.Some(ownerKey))
|
||||
{
|
||||
@@ -84,8 +92,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void OnAuthChange(Steamworks.SteamId steamId, Steamworks.SteamId ownerId, Steamworks.AuthResponse status)
|
||||
{
|
||||
RemotePeer remotePeer = remotePeers.Find(p => p.SteamId.Value == steamId);
|
||||
DebugConsole.Log(steamId + " validation: " + status + ", " + (remotePeer != null));
|
||||
RemotePeer? remotePeer = remotePeers.Find(p => p.SteamId.Value == steamId);
|
||||
DebugConsole.Log($"{steamId} validation: {status}, {remotePeer != null}");
|
||||
|
||||
if (remotePeer == null) { return; }
|
||||
|
||||
@@ -93,36 +101,32 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (status != Steamworks.AuthResponse.OK)
|
||||
{
|
||||
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
|
||||
DisconnectPeer(remotePeer, $"{DisconnectReason.SteamAuthenticationFailed}/ Steam authentication status changed: {status}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == Steamworks.AuthResponse.OK)
|
||||
{
|
||||
remotePeer.OwnerSteamId = Option<SteamId>.Some(new SteamId(ownerId));
|
||||
SteamId ownerSteamId = new SteamId(ownerId);
|
||||
remotePeer.OwnerSteamId = Option<SteamId>.Some(ownerSteamId);
|
||||
remotePeer.Authenticated = true;
|
||||
remotePeer.Authenticating = false;
|
||||
foreach (var msg in remotePeer.UnauthedMessages)
|
||||
foreach (var unauthedMessage in remotePeer.UnauthedMessages)
|
||||
{
|
||||
//rewrite the owner id before
|
||||
//forwarding the messages to
|
||||
//the server, since it's only
|
||||
//known now
|
||||
int prevBitPosition = msg.Message.BitPosition;
|
||||
msg.Message.BitPosition = sizeof(ulong) * 8;
|
||||
WriteSteamId(msg.Message, new SteamId(ownerId));
|
||||
msg.Message.BitPosition = prevBitPosition;
|
||||
byte[] msgToSend = (byte[])msg.Message.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, msg.Message.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
WriteSteamId(msg, unauthedMessage.Sender);
|
||||
WriteSteamId(msg, ownerSteamId);
|
||||
msg.WriteBytes(unauthedMessage.Bytes, 0, unauthedMessage.Length);
|
||||
ForwardToServerProcess(msg);
|
||||
}
|
||||
|
||||
remotePeer.UnauthedMessages.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication failed: " + status.ToString());
|
||||
return;
|
||||
DisconnectPeer(remotePeer, $"{DisconnectReason.SteamAuthenticationFailed}/ Steam authentication failed: {status}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,58 +142,54 @@ namespace Barotrauma.Networking
|
||||
Steamworks.SteamNetworking.AcceptP2PSessionWithUser(steamId); //accept all connections, the server will figure things out later
|
||||
}
|
||||
|
||||
private void OnP2PData(ulong steamId, byte[] data, int dataLength, int _)
|
||||
private void OnP2PData(ulong steamId, IReadMessage inc)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
RemotePeer remotePeer = remotePeers.Find(p => p.SteamId.Value == steamId);
|
||||
RemotePeer? remotePeer = remotePeers.Find(p => p.SteamId.Value == steamId);
|
||||
if (remotePeer == null) { return; }
|
||||
|
||||
if (remotePeer.DisconnectTime != null) { return; }
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
var steamUserId = new SteamId(steamId);
|
||||
WriteSteamId(outMsg, steamUserId);
|
||||
WriteSteamId(outMsg, remotePeer.OwnerSteamId.Fallback(steamUserId));
|
||||
outMsg.Write(data, 1, dataLength - 1);
|
||||
|
||||
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)data[1];
|
||||
var peerPacketHeaders = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
PacketHeader packetHeader = peerPacketHeaders.PacketHeader;
|
||||
|
||||
if (!remotePeer.Authenticated && !remotePeer.Authenticating && packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
remotePeer.DisconnectTime = null;
|
||||
|
||||
IReadMessage authMsg = new ReadOnlyMessage(data, packetHeader.IsCompressed(), 2, dataLength - 2, null);
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
|
||||
ConnectionInitialization initialization = peerPacketHeaders.Initialization ?? throw new Exception("Initialization step missing");
|
||||
if (initialization == ConnectionInitialization.SteamTicketAndVersion)
|
||||
{
|
||||
remotePeer.Authenticating = true;
|
||||
|
||||
authMsg.ReadString(); //skip name
|
||||
authMsg.ReadInt32(); //skip owner key
|
||||
authMsg.ReadUInt64(); //skip steamid
|
||||
UInt16 ticketLength = authMsg.ReadUInt16();
|
||||
byte[] ticket = authMsg.ReadBytes(ticketLength);
|
||||
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
var packet = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc);
|
||||
|
||||
packet.SteamAuthTicket.TryUnwrap(out byte[] ticket);
|
||||
|
||||
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
DisconnectPeer(remotePeer, $"{DisconnectReason.SteamAuthenticationFailed}/ Steam auth session failed to start: {authSessionStartState}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var steamUserId = new SteamId(steamId);
|
||||
if (remotePeer.Authenticating)
|
||||
{
|
||||
remotePeer.UnauthedMessages.Add(new RemotePeer.UnauthedMessage() { DeliveryMethod = deliveryMethod, Message = outMsg });
|
||||
remotePeer.UnauthedMessages.Add(new RemotePeer.UnauthedMessage(steamUserId, inc.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, outMsg.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
WriteSteamId(outMsg, steamUserId);
|
||||
WriteSteamId(outMsg, remotePeer.OwnerSteamId.Fallback(steamUserId));
|
||||
outMsg.WriteBytes(inc.Buffer, 0, inc.LengthBytes);
|
||||
|
||||
ForwardToServerProcess(outMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Close();
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) => { GameMain.MainMenuScreen.Select(); return false; };
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,11 +220,12 @@ namespace Barotrauma.Networking
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; }
|
||||
|
||||
var packet = Steamworks.SteamNetworking.ReadP2PPacket();
|
||||
if (packet.HasValue)
|
||||
if (packet is { SteamId: var steamId, Data: var data })
|
||||
{
|
||||
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0);
|
||||
receivedBytes += packet?.Data.Length ?? 0;
|
||||
OnP2PData(steamId, new ReadWriteMessage(data, 0, data.Length * 8, false));
|
||||
receivedBytes += data.Length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,155 +245,144 @@ namespace Barotrauma.Networking
|
||||
if (!isActive) { return; }
|
||||
|
||||
SteamId recipientSteamId = ReadSteamId(inc);
|
||||
DeliveryMethod deliveryMethod = (DeliveryMethod)inc.ReadByte();
|
||||
|
||||
int p2pDataStart = inc.BytePosition;
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
var peerPacketHeaders = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (recipientSteamId != selfSteamID)
|
||||
{
|
||||
if (!packetHeader.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 (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DisconnectPeer(peer, inc.ReadString());
|
||||
return;
|
||||
}
|
||||
|
||||
Steamworks.P2PSend sendType;
|
||||
switch (deliveryMethod)
|
||||
{
|
||||
case DeliveryMethod.Reliable:
|
||||
case DeliveryMethod.ReliableOrdered:
|
||||
//the documentation seems to suggest that the
|
||||
//Reliable send type enforces packet order
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
break;
|
||||
default:
|
||||
sendType = Steamworks.P2PSend.Unreliable;
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] p2pData;
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
p2pData = new byte[inc.LengthBytes - p2pDataStart + 8];
|
||||
p2pData[0] = inc.Buffer[p2pDataStart];
|
||||
Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 8 * 8, p2pData, 1 * 8);
|
||||
Array.Copy(inc.Buffer, p2pDataStart+1, p2pData, 1 + 8, inc.LengthBytes - p2pDataStart - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2pData = new byte[inc.LengthBytes - p2pDataStart];
|
||||
Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length);
|
||||
|
||||
if (!packetHeader.IsHeartbeatMessage() && !packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
UInt16 length = Lidgren.Network.NetBitWriter.ReadUInt16(p2pData, 16, 8);
|
||||
if (length > p2pData.Length - 2)
|
||||
{
|
||||
string errorMsg = $"Length written in message to send to client is larger than buffer size ({length} > {p2pData.Length - 2})";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"SteamP2POwnerPeerLengthValidationFail",
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p2pData.Length + 4 >= MsgConstants.MTU)
|
||||
{
|
||||
DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
}
|
||||
|
||||
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId.Value, p2pData, p2pData.Length, 0, sendType);
|
||||
sentBytes += p2pData.Length;
|
||||
|
||||
if (!successSend)
|
||||
{
|
||||
if (sendType != Steamworks.P2PSend.Reliable)
|
||||
{
|
||||
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
|
||||
sendType = Steamworks.P2PSend.Reliable;
|
||||
successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId.Value, p2pData, p2pData.Length, 0, sendType);
|
||||
sentBytes += p2pData.Length;
|
||||
}
|
||||
if (!successSend)
|
||||
{
|
||||
DebugConsole.AddWarning("Failed to send message to remote peer! (" + p2pData.Length.ToString() + " bytes)");
|
||||
}
|
||||
}
|
||||
HandleMessageForRemotePeer(peerPacketHeaders, recipientSteamId, inc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received disconnect message from owned server");
|
||||
return;
|
||||
}
|
||||
if (!packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received non-server message from owned server");
|
||||
return;
|
||||
}
|
||||
if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
return; //no timeout since we're using pipes, ignore this message
|
||||
}
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
WriteSteamId(outMsg, selfSteamID);
|
||||
WriteSteamId(outMsg, selfSteamID);
|
||||
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
|
||||
outMsg.Write(GameMain.Client.Name);
|
||||
|
||||
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, outMsg.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initializationStep != ConnectionInitialization.Success)
|
||||
{
|
||||
callbacks.OnInitializationComplete.Invoke();
|
||||
initializationStep = ConnectionInitialization.Success;
|
||||
}
|
||||
UInt16 length = inc.ReadUInt16();
|
||||
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, ServerConnection);
|
||||
callbacks.OnMessageReceived.Invoke(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
HandleMessageForOwner(peerPacketHeaders, inc);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetRemainingBytes(IReadMessage msg)
|
||||
{
|
||||
return msg.Buffer[msg.BytePosition..msg.LengthBytes];
|
||||
}
|
||||
|
||||
private void HandleMessageForRemotePeer(PeerPacketHeaders peerPacketHeaders, SteamId recipientSteamId, IReadMessage inc)
|
||||
{
|
||||
var (deliveryMethod, packetHeader, initialization) = peerPacketHeaders;
|
||||
|
||||
if (!packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received non-server message meant for remote peer");
|
||||
return;
|
||||
}
|
||||
|
||||
RemotePeer? peer = remotePeers.Find(p => p.SteamId == recipientSteamId);
|
||||
if (peer is null) { return; }
|
||||
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
var packet = INetSerializableStruct.Read<PeerDisconnectPacket>(inc);
|
||||
DisconnectPeer(peer, packet.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
|
||||
outMsg.WriteNetSerializableStruct(new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = packetHeader,
|
||||
Initialization = initialization
|
||||
});
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
var initRelayPacket = new SteamP2PInitializationRelayPacket
|
||||
{
|
||||
LobbyID = SteamManager.CurrentLobbyID,
|
||||
Message = new PeerPacketMessage
|
||||
{
|
||||
Buffer = GetRemainingBytes(inc)
|
||||
}
|
||||
};
|
||||
|
||||
outMsg.WriteNetSerializableStruct(initRelayPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] userMessage = GetRemainingBytes(inc);
|
||||
outMsg.WriteBytes(userMessage, 0, userMessage.Length);
|
||||
}
|
||||
|
||||
ForwardToRemotePeer(deliveryMethod, recipientSteamId, outMsg);
|
||||
}
|
||||
|
||||
private void HandleMessageForOwner(PeerPacketHeaders peerPacketHeaders, IReadMessage inc)
|
||||
{
|
||||
var (_, packetHeader, _) = peerPacketHeaders;
|
||||
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received disconnect message from owned server");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received non-server message from owned server");
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
return; //no timeout since we're using pipes, ignore this message
|
||||
}
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
WriteSteamId(outMsg, selfSteamID);
|
||||
WriteSteamId(outMsg, selfSteamID);
|
||||
outMsg.WriteNetSerializableStruct(new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep,
|
||||
Initialization = ConnectionInitialization.SteamTicketAndVersion
|
||||
});
|
||||
outMsg.WriteNetSerializableStruct(new SteamP2PInitializationOwnerPacket
|
||||
{
|
||||
OwnerName = GameMain.Client.Name
|
||||
});
|
||||
ForwardToServerProcess(outMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initializationStep != ConnectionInitialization.Success)
|
||||
{
|
||||
callbacks.OnInitializationComplete.Invoke();
|
||||
initializationStep = ConnectionInitialization.Success;
|
||||
}
|
||||
|
||||
PeerPacketMessage packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, ServerConnection);
|
||||
callbacks.OnMessageReceived.Invoke(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void DisconnectPeer(RemotePeer peer, string msg)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
if (peer.DisconnectTime == null)
|
||||
{
|
||||
peer.DisconnectTime = Timing.TotalTime + 1.0;
|
||||
}
|
||||
peer.DisconnectTime ??= Timing.TotalTime + 1.0;
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write((byte)(PacketHeader.IsServerMessage | PacketHeader.IsDisconnectMessage));
|
||||
outMsg.Write(msg);
|
||||
|
||||
outMsg.WriteNetSerializableStruct(new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsServerMessage | PacketHeader.IsDisconnectMessage
|
||||
});
|
||||
outMsg.WriteNetSerializableStruct(new PeerDisconnectPacket
|
||||
{
|
||||
Message = msg
|
||||
});
|
||||
|
||||
Steamworks.SteamNetworking.SendP2PPacket(peer.SteamId.Value, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
|
||||
sentBytes += outMsg.LengthBytes;
|
||||
}
|
||||
@@ -406,10 +400,10 @@ namespace Barotrauma.Networking
|
||||
|
||||
public override void SendPassword(string password)
|
||||
{
|
||||
return; //owner doesn't send passwords
|
||||
//owner doesn't send passwords
|
||||
}
|
||||
|
||||
public override void Close(string msg = null, bool disableReconnect = false)
|
||||
public override void Close(string? msg = null, bool disableReconnect = false)
|
||||
{
|
||||
if (!isActive) { return; }
|
||||
|
||||
@@ -441,25 +435,62 @@ namespace Barotrauma.Networking
|
||||
if (!isActive) { return; }
|
||||
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
byte[] msgData = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
|
||||
byte[] msgData = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
|
||||
WriteSteamId(msgToSend, selfSteamID);
|
||||
WriteSteamId(msgToSend, selfSteamID);
|
||||
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
|
||||
msgToSend.Write((UInt16)length);
|
||||
msgToSend.Write(msgData, 0, length);
|
||||
|
||||
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
|
||||
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
msgToSend.WriteNetSerializableStruct(new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None
|
||||
});
|
||||
msgToSend.WriteNetSerializableStruct(new PeerPacketMessage
|
||||
{
|
||||
Buffer = msgData
|
||||
});
|
||||
ForwardToServerProcess(msgToSend);
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
//not currently used by SteamP2POwnerPeer
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void ForwardToServerProcess(IWriteMessage msg)
|
||||
{
|
||||
byte[] bufToSend = new byte[msg.LengthBytes];
|
||||
msg.Buffer[..msg.LengthBytes].CopyTo(bufToSend.AsSpan());
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
}
|
||||
|
||||
private void ForwardToRemotePeer(DeliveryMethod deliveryMethod, SteamId recipent, IWriteMessage outMsg)
|
||||
{
|
||||
byte[] buf = outMsg.PrepareForSending(compressPastThreshold: false, out _, out int length);
|
||||
|
||||
if (length + 4 >= MsgConstants.MTU)
|
||||
{
|
||||
DebugConsole.Log($"WARNING: message length comes close to exceeding MTU, forcing reliable send ({length} bytes)");
|
||||
deliveryMethod = DeliveryMethod.Reliable;
|
||||
}
|
||||
|
||||
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipent.Value, buf, length, 0, deliveryMethod.ToSteam());
|
||||
sentBytes += length;
|
||||
|
||||
if (successSend) { return; }
|
||||
|
||||
if (deliveryMethod is DeliveryMethod.Unreliable)
|
||||
{
|
||||
DebugConsole.Log($"WARNING: message couldn't be sent unreliably, forcing reliable send ({length} bytes)");
|
||||
successSend = Steamworks.SteamNetworking.SendP2PPacket(recipent.Value, buf, length, 0, DeliveryMethod.Reliable.ToSteam());
|
||||
sentBytes += length;
|
||||
}
|
||||
|
||||
if (!successSend)
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to send message to remote peer! ({length} bytes)");
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override void ForceTimeOut()
|
||||
{
|
||||
@@ -467,4 +498,4 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,14 +44,19 @@ namespace Barotrauma.Networking
|
||||
if (!UseRespawnPrompt) { return; }
|
||||
if (CoroutineManager.IsCoroutineRunning(respawnPromptCoroutine) || GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "respawnquestionprompt"))
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
respawnPromptCoroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (Character.Controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
|
||||
|
||||
LocalizedString text =
|
||||
TextManager.GetWithVariable("respawnskillpenalty", "[percentage]", ((int)(SkillReductionOnDeath * 100)).ToString())
|
||||
+ "\n\n" + TextManager.Get("respawnquestionprompt");
|
||||
|
||||
var respawnPrompt = new GUIMessageBox(
|
||||
TextManager.Get("tutorial.tryagainheader"), TextManager.Get("respawnquestionprompt"),
|
||||
TextManager.Get("tutorial.tryagainheader"), text,
|
||||
new LocalizedString[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") })
|
||||
{
|
||||
UserData = "respawnquestionprompt"
|
||||
|
||||
@@ -348,7 +348,7 @@ namespace Barotrauma.Networking
|
||||
ServerName = element.GetAttributeString("ServerName", ""),
|
||||
ServerMessage = element.GetAttributeString("ServerMessage", ""),
|
||||
Endpoint = endpoint,
|
||||
QueryPort = element.GetAttributeInt("QueryPort", 0),
|
||||
QueryPort = !string.IsNullOrEmpty(element.GetAttributeString("QueryPort", string.Empty)) ? element.GetAttributeInt("QueryPort", 0) : 0,
|
||||
GameMode = element.GetAttributeIdentifier("GameMode", Identifier.Empty),
|
||||
GameVersion = element.GetAttributeString("GameVersion", ""),
|
||||
MaxPlayers = Math.Min(element.GetAttributeInt("MaxPlayers", 0), NetConfig.MaxPlayers),
|
||||
|
||||
@@ -182,9 +182,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
|
||||
outMsg.Write((byte)ClientPacketHeader.SERVER_SETTINGS);
|
||||
outMsg.WriteByte((byte)ClientPacketHeader.SERVER_SETTINGS);
|
||||
|
||||
outMsg.Write((byte)dataToSend);
|
||||
outMsg.WriteByte((byte)dataToSend);
|
||||
|
||||
if (dataToSend.HasFlag(NetFlags.Name))
|
||||
{
|
||||
@@ -192,7 +192,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ServerName = GameMain.NetLobbyScreen.ServerName.Text;
|
||||
}
|
||||
outMsg.Write(ServerName);
|
||||
outMsg.WriteString(ServerName);
|
||||
}
|
||||
|
||||
if (dataToSend.HasFlag(NetFlags.Message))
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ServerMessageText = GameMain.NetLobbyScreen.ServerMessage.Text;
|
||||
}
|
||||
outMsg.Write(ServerMessageText);
|
||||
outMsg.WriteString(ServerMessageText);
|
||||
}
|
||||
|
||||
if (dataToSend.HasFlag(NetFlags.Properties))
|
||||
@@ -213,15 +213,15 @@ namespace Barotrauma.Networking
|
||||
UInt32 count = (UInt32)changedProperties.Count();
|
||||
bool changedMonsterSettings = tempMonsterEnabled != null && tempMonsterEnabled.Any(p => p.Value != MonsterEnabled[p.Key]);
|
||||
|
||||
outMsg.Write(count);
|
||||
outMsg.WriteUInt32(count);
|
||||
foreach (KeyValuePair<UInt32, NetPropertyData> prop in changedProperties)
|
||||
{
|
||||
DebugConsole.NewMessage(prop.Value.Name.Value, Color.Lime);
|
||||
outMsg.Write(prop.Key);
|
||||
outMsg.WriteUInt32(prop.Key);
|
||||
prop.Value.Write(outMsg, prop.Value.GUIComponentValue);
|
||||
}
|
||||
|
||||
outMsg.Write(changedMonsterSettings); outMsg.WritePadBits();
|
||||
outMsg.WriteBoolean(changedMonsterSettings); outMsg.WritePadBits();
|
||||
if (changedMonsterSettings) WriteMonsterEnabled(outMsg, tempMonsterEnabled);
|
||||
BanList.ClientAdminWrite(outMsg);
|
||||
}
|
||||
@@ -235,23 +235,23 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
outMsg.WriteRangedInteger(missionTypeOr ?? (int)Barotrauma.MissionType.None, 0, (int)Barotrauma.MissionType.All);
|
||||
outMsg.WriteRangedInteger(missionTypeAnd ?? (int)Barotrauma.MissionType.All, 0, (int)Barotrauma.MissionType.All);
|
||||
outMsg.Write((byte)(traitorSetting + 1));
|
||||
outMsg.Write((byte)(botCount + 1));
|
||||
outMsg.Write((byte)(botSpawnMode + 1));
|
||||
outMsg.WriteByte((byte)(traitorSetting + 1));
|
||||
outMsg.WriteByte((byte)(botCount + 1));
|
||||
outMsg.WriteByte((byte)(botSpawnMode + 1));
|
||||
|
||||
outMsg.Write(levelDifficulty ?? -1000.0f);
|
||||
outMsg.WriteSingle(levelDifficulty ?? -1000.0f);
|
||||
|
||||
outMsg.Write(useRespawnShuttle ?? UseRespawnShuttle);
|
||||
outMsg.WriteBoolean(useRespawnShuttle ?? UseRespawnShuttle);
|
||||
|
||||
outMsg.Write(autoRestart != null);
|
||||
outMsg.Write(autoRestart ?? false);
|
||||
outMsg.WriteBoolean(autoRestart != null);
|
||||
outMsg.WriteBoolean(autoRestart ?? false);
|
||||
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
|
||||
if (dataToSend.HasFlag(NetFlags.LevelSeed))
|
||||
{
|
||||
outMsg.Write(GameMain.NetLobbyScreen.SeedBox.Text);
|
||||
outMsg.WriteString(GameMain.NetLobbyScreen.SeedBox.Text);
|
||||
}
|
||||
|
||||
GameMain.Client.ClientPeer.Send(outMsg, DeliveryMethod.Reliable);
|
||||
|
||||
@@ -4,7 +4,6 @@ using Microsoft.Xna.Framework;
|
||||
using OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
@@ -22,13 +21,13 @@ namespace Barotrauma.Networking
|
||||
public static IReadOnlyList<string> CaptureDeviceNames =>
|
||||
Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||
|
||||
private IntPtr captureDevice;
|
||||
private readonly IntPtr captureDevice;
|
||||
|
||||
private Thread captureThread;
|
||||
|
||||
private bool capturing;
|
||||
|
||||
private OpusEncoder encoder;
|
||||
private readonly OpusEncoder encoder;
|
||||
|
||||
public double LastdB
|
||||
{
|
||||
@@ -171,8 +170,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
IntPtr nativeBuffer;
|
||||
short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
bool prevCaptured = true;
|
||||
int captureTimer;
|
||||
|
||||
@@ -227,16 +226,20 @@ namespace Barotrauma.Networking
|
||||
bool allowEnqueue = overrideSound != null;
|
||||
if (GameMain.WindowActive && SettingsMenu.Instance is null)
|
||||
{
|
||||
bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
|
||||
bool usingLocalMode = PlayerInput.KeyDown(InputType.LocalVoice);
|
||||
bool usingRadioMode = PlayerInput.KeyDown(InputType.RadioVoice);
|
||||
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
||||
if (pttDown || captureTimer <= 0)
|
||||
{
|
||||
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
||||
}
|
||||
if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Activity)
|
||||
{
|
||||
bool pttDown = (usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
||||
if (pttDown)
|
||||
{
|
||||
ForceLocal = usingLocalMode;
|
||||
}
|
||||
//in Activity mode, we default to the active mode UNLESS a specific ptt key is held
|
||||
else
|
||||
{
|
||||
ForceLocal = GameMain.ActiveChatMode == ChatMode.Local;
|
||||
}
|
||||
if (dB > GameSettings.CurrentConfig.Audio.NoiseGateThreshold)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
@@ -244,6 +247,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.PushToTalk)
|
||||
{
|
||||
//in push-to-talk mode, InputType.Voice uses the active chat mode
|
||||
bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
|
||||
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
|
||||
if (pttDown || captureTimer <= 0)
|
||||
{
|
||||
ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
|
||||
}
|
||||
if (pttDown)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
|
||||
@@ -72,8 +72,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
|
||||
msg.Write((byte)ClientPacketHeader.VOICE);
|
||||
msg.Write((byte)VoipCapture.Instance.QueueID);
|
||||
msg.WriteByte((byte)ClientPacketHeader.VOICE);
|
||||
msg.WriteByte((byte)VoipCapture.Instance.QueueID);
|
||||
VoipCapture.Instance.Write(msg);
|
||||
|
||||
netClient.Send(msg, DeliveryMethod.Unreliable);
|
||||
|
||||
@@ -113,35 +113,35 @@ namespace Barotrauma
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, VoteType voteType, object data)
|
||||
{
|
||||
msg.Write((byte)voteType);
|
||||
msg.WriteByte((byte)voteType);
|
||||
|
||||
switch (voteType)
|
||||
{
|
||||
case VoteType.Sub:
|
||||
if (!(data is SubmarineInfo sub)) { return; }
|
||||
msg.Write(sub.EqualityCheckVal);
|
||||
msg.WriteInt32(sub.EqualityCheckVal);
|
||||
if (sub.EqualityCheckVal == 0)
|
||||
{
|
||||
//sub doesn't exist client-side, use hash to let the server know which one we voted for
|
||||
msg.Write(sub.MD5Hash.StringRepresentation);
|
||||
msg.WriteString(sub.MD5Hash.StringRepresentation);
|
||||
}
|
||||
break;
|
||||
case VoteType.Mode:
|
||||
if (!(data is GameModePreset gameMode)) { return; }
|
||||
msg.Write(gameMode.Identifier);
|
||||
msg.WriteIdentifier(gameMode.Identifier);
|
||||
break;
|
||||
case VoteType.EndRound:
|
||||
if (!(data is bool)) { return; }
|
||||
msg.Write((bool)data);
|
||||
msg.WriteBoolean((bool)data);
|
||||
break;
|
||||
case VoteType.Kick:
|
||||
if (!(data is Client votedClient)) { return; }
|
||||
|
||||
msg.Write(votedClient.SessionId);
|
||||
msg.WriteByte(votedClient.SessionId);
|
||||
break;
|
||||
case VoteType.StartRound:
|
||||
if (!(data is bool)) { return; }
|
||||
msg.Write((bool)data);
|
||||
msg.WriteBoolean((bool)data);
|
||||
break;
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.PurchaseSub:
|
||||
@@ -149,22 +149,22 @@ namespace Barotrauma
|
||||
if (data is (SubmarineInfo voteSub, bool transferItems))
|
||||
{
|
||||
//initiate sub vote
|
||||
msg.Write(true);
|
||||
msg.Write(voteSub.Name);
|
||||
msg.Write(transferItems);
|
||||
msg.WriteBoolean(true);
|
||||
msg.WriteString(voteSub.Name);
|
||||
msg.WriteBoolean(transferItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
// vote
|
||||
if (!(data is int)) { return; }
|
||||
msg.Write(false);
|
||||
msg.Write((int)data);
|
||||
msg.WriteBoolean(false);
|
||||
msg.WriteInt32((int)data);
|
||||
}
|
||||
break;
|
||||
case VoteType.TransferMoney:
|
||||
if (!(data is int)) { return; }
|
||||
msg.Write(false); //not initiating a vote
|
||||
msg.Write((int)data);
|
||||
msg.WriteBoolean(false); //not initiating a vote
|
||||
msg.WriteInt32((int)data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class WhiteListedPlayer
|
||||
{
|
||||
public WhiteListedPlayer(string name, UInt16 identifier, string ip)
|
||||
{
|
||||
Name = name;
|
||||
IP = ip;
|
||||
|
||||
UniqueIdentifier = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
partial class WhiteList
|
||||
{
|
||||
private GUIComponent whitelistFrame;
|
||||
|
||||
private GUITextBox nameBox;
|
||||
private GUITextBox ipBox;
|
||||
private GUIButton addNewButton;
|
||||
|
||||
public class LocalAdded
|
||||
{
|
||||
public string Name;
|
||||
public string IP;
|
||||
};
|
||||
|
||||
public bool localEnabled;
|
||||
public List<UInt16> localRemoved = new List<UInt16>();
|
||||
public List<LocalAdded> localAdded = new List<LocalAdded>();
|
||||
|
||||
public GUIComponent CreateWhiteListFrame(GUIComponent parent)
|
||||
{
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
whitelistFrame.Parent.ClearChildren();
|
||||
whitelistFrame = null;
|
||||
}
|
||||
|
||||
whitelistFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), parent.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
var enabledTick = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.1f), whitelistFrame.RectTransform), TextManager.Get("WhiteListEnabled"))
|
||||
{
|
||||
Selected = localEnabled,
|
||||
UpdateOrder = 1,
|
||||
OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
nameBox.Enabled = box.Selected;
|
||||
ipBox.Enabled = box.Selected;
|
||||
addNewButton.Enabled = box.Selected && !string.IsNullOrEmpty(ipBox.Text) && !string.IsNullOrEmpty(nameBox.Text);
|
||||
localEnabled = box.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), whitelistFrame.RectTransform));
|
||||
foreach (WhiteListedPlayer wlp in whitelistedPlayers)
|
||||
{
|
||||
if (localRemoved.Contains(wlp.UniqueIdentifier)) continue;
|
||||
string blockText = wlp.Name;
|
||||
if (!string.IsNullOrWhiteSpace(wlp.IP)) blockText += " (" + wlp.IP + ")";
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform),
|
||||
blockText)
|
||||
{
|
||||
UserData = wlp
|
||||
};
|
||||
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), textBlock.RectTransform, Anchor.CenterRight),
|
||||
TextManager.Get("WhiteListRemove"), style: "GUIButtonSmall")
|
||||
{
|
||||
UserData = wlp,
|
||||
OnClicked = RemoveFromWhiteList
|
||||
};
|
||||
}
|
||||
|
||||
foreach (LocalAdded lad in localAdded)
|
||||
{
|
||||
string blockText = lad.Name;
|
||||
if (!string.IsNullOrWhiteSpace(lad.IP)) blockText += " (" + lad.IP + ")";
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform),
|
||||
blockText)
|
||||
{
|
||||
UserData = lad
|
||||
};
|
||||
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), textBlock.RectTransform, Anchor.CenterRight),
|
||||
TextManager.Get("WhiteListRemove"), style: "GUIButtonSmall")
|
||||
{
|
||||
UserData = lad,
|
||||
OnClicked = RemoveFromWhiteList
|
||||
};
|
||||
}
|
||||
|
||||
foreach (GUIComponent c in listBox.Content.Children)
|
||||
{
|
||||
c.RectTransform.MinSize = new Point(0, Math.Max((int)(20 * GUI.Scale), c.RectTransform.Children.Max(c2 => c2.MinSize.Y)));
|
||||
}
|
||||
|
||||
var nameArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), whitelistFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), nameArea.RectTransform), TextManager.Get("WhiteListName"));
|
||||
nameBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), nameArea.RectTransform), "");
|
||||
nameBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
addNewButton.Enabled = !string.IsNullOrEmpty(ipBox.Text) && !string.IsNullOrEmpty(nameBox.Text);
|
||||
return true;
|
||||
};
|
||||
nameArea.RectTransform.MinSize = new Point(0, nameBox.RectTransform.MinSize.Y);
|
||||
|
||||
var ipArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), whitelistFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), ipArea.RectTransform), TextManager.Get("WhiteListIP"));
|
||||
ipBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), ipArea.RectTransform), "");
|
||||
ipBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
addNewButton.Enabled = !string.IsNullOrEmpty(ipBox.Text) && !string.IsNullOrEmpty(nameBox.Text);
|
||||
return true;
|
||||
};
|
||||
ipBox.RectTransform.MinSize = new Point(0, ipBox.RectTransform.MinSize.Y);
|
||||
|
||||
addNewButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), whitelistFrame.RectTransform), TextManager.Get("WhiteListAdd"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = AddToWhiteList
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(addNewButton.TextBlock);
|
||||
|
||||
nameBox.Enabled = localEnabled;
|
||||
ipBox.Enabled = localEnabled;
|
||||
addNewButton.Enabled = false;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
private bool RemoveFromWhiteList(GUIButton button, object obj)
|
||||
{
|
||||
if (obj is WhiteListedPlayer)
|
||||
{
|
||||
if (!(obj is WhiteListedPlayer wlp)) return false;
|
||||
if (!localRemoved.Contains(wlp.UniqueIdentifier)) localRemoved.Add(wlp.UniqueIdentifier);
|
||||
}
|
||||
else if (obj is LocalAdded)
|
||||
{
|
||||
if (!(obj is LocalAdded lad)) return false;
|
||||
if (localAdded.Contains(lad)) localAdded.Remove(lad);
|
||||
}
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
CreateWhiteListFrame(whitelistFrame.Parent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool AddToWhiteList(GUIButton button, object obj)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nameBox.Text)) return false;
|
||||
if (whitelistedPlayers.Any(x => x.Name.ToLower() == nameBox.Text.ToLower() && x.IP == ipBox.Text)) return false;
|
||||
|
||||
if (!localAdded.Any(p => p.IP == ipBox.Text)) localAdded.Add(new LocalAdded() { Name = nameBox.Text, IP = ipBox.Text });
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
CreateWhiteListFrame(whitelistFrame.Parent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientAdminRead(IReadMessage incMsg)
|
||||
{
|
||||
bool hasPermission = incMsg.ReadBoolean();
|
||||
if (!hasPermission)
|
||||
{
|
||||
incMsg.ReadPadBits();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwner = incMsg.ReadBoolean();
|
||||
localEnabled = incMsg.ReadBoolean();
|
||||
Enabled = localEnabled;
|
||||
incMsg.ReadPadBits();
|
||||
|
||||
whitelistedPlayers.Clear();
|
||||
UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();
|
||||
for (int i = 0; i < (int)bannedPlayerCount; i++)
|
||||
{
|
||||
string name = incMsg.ReadString();
|
||||
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
|
||||
|
||||
string ip = "";
|
||||
if (isOwner)
|
||||
{
|
||||
ip = incMsg.ReadString();
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = "IP concealed by host";
|
||||
}
|
||||
whitelistedPlayers.Add(new WhiteListedPlayer(name, uniqueIdentifier, ip));
|
||||
}
|
||||
|
||||
if (whitelistFrame != null)
|
||||
{
|
||||
CreateWhiteListFrame(whitelistFrame.Parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientAdminWrite(IWriteMessage outMsg)
|
||||
{
|
||||
outMsg.Write(localEnabled);
|
||||
outMsg.WritePadBits();
|
||||
|
||||
outMsg.Write((UInt16)localRemoved.Count);
|
||||
foreach (UInt16 uniqueId in localRemoved)
|
||||
{
|
||||
outMsg.Write(uniqueId);
|
||||
}
|
||||
|
||||
outMsg.Write((UInt16)localAdded.Count);
|
||||
foreach (LocalAdded lad in localAdded)
|
||||
{
|
||||
outMsg.Write(lad.Name);
|
||||
outMsg.Write(lad.IP); //TODO: ENCRYPT
|
||||
}
|
||||
|
||||
localRemoved.Clear();
|
||||
localAdded.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user