Merge remote-tracking branch 'upstream/dev' into develop

This commit is contained in:
EvilFactory
2022-12-09 17:33:44 -03:00
416 changed files with 12674 additions and 5862 deletions
@@ -311,6 +311,12 @@ namespace Barotrauma.Networking
CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo");
}
public void SetLobbyPublic(bool isPublic)
{
GameMain.NetLobbyScreen.SetPublic(isPublic);
SteamManager.SetLobbyPublic(isPublic);
}
private ClientPeer CreateNetPeer()
{
Networking.ClientPeer.Callbacks callbacks = new ClientPeer.Callbacks(
@@ -326,10 +332,27 @@ namespace Barotrauma.Networking
};
}
public void CreateServerCrashMessage()
{
// Close any message boxes that say "The server has crashed."
var basicServerCrashMsg = TextManager.Get($"{nameof(DisconnectReason)}.{nameof(DisconnectReason.ServerCrashed)}");
GUIMessageBox.MessageBoxes
.OfType<GUIMessageBox>()
.Where(mb => mb.Text?.Text == basicServerCrashMsg)
.ToArray()
.ForEach(mb => mb.Close());
// Open a new message box with the crash report path
if (GUIMessageBox.MessageBoxes.All(
mb => (mb as GUIMessageBox)?.Text?.Text != ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
}
}
private bool ReturnToPreviousMenu(GUIButton button, object obj)
{
Quit();
Submarine.Unload();
GameMain.Client = null;
GameMain.GameSession = null;
@@ -443,7 +466,7 @@ namespace Barotrauma.Networking
foreach (Client c in ConnectedClients)
{
if (c.Character != null && c.Character.Removed) { c.Character = null; }
c.UpdateSoundPosition();
c.UpdateVoipSound();
}
if (VoipCapture.Instance != null)
@@ -479,15 +502,11 @@ namespace Barotrauma.Networking
}
catch (Exception e)
{
string errorMsg = "Error while reading a message from server. {" + e + "}. ";
string errorMsg = "Error while reading a message from server. ";
if (GameMain.Client == null) { errorMsg += "Client disposed."; }
errorMsg += "\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null)
{
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
}
AppendExceptionInfo(ref errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
DebugConsole.ThrowError("Error while reading a message from server.", e);
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", e.TargetSite.ToString())))
{
DisplayInLoadingScreens = true
@@ -529,14 +548,10 @@ namespace Barotrauma.Networking
{
if (GameMain.WindowActive)
{
if (ChildServerRelay.Process?.HasExited ?? true)
if (!ChildServerRelay.IsProcessAlive)
{
Quit();
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
}
CreateServerCrashMessage();
}
}
}
@@ -632,14 +647,8 @@ namespace Barotrauma.Networking
}
catch (Exception e)
{
string errorMsg = "Error while reading an ingame update message from server. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
if (e.InnerException != null)
{
errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace.CleanupStackTrace();
}
#if DEBUG
DebugConsole.ThrowError("Error while reading an ingame update message from server.", e);
#endif
string errorMsg = "Error while reading an ingame update message from server.";
AppendExceptionInfo(ref errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw;
}
@@ -868,22 +877,31 @@ namespace Barotrauma.Networking
}
byte missionCount = inc.ReadByte();
if (missionCount != GameMain.GameSession.Missions.Count())
{
string errorMsg = $"Mission equality check failed. Mission count doesn't match the server (server: {missionCount}, client: {GameMain.GameSession.Missions.Count()})";
throw new Exception(errorMsg);
}
List<Identifier> serverMissionIdentifiers = new List<Identifier>();
for (int i = 0; i < missionCount; i++)
{
serverMissionIdentifiers.Add(inc.ReadIdentifier());
}
if (missionCount != GameMain.GameSession.GameMode.Missions.Count())
{
string errorMsg =
$"Mission equality check failed. Mission count doesn't match the server. " +
$"Server: {string.Join(", ", serverMissionIdentifiers)}, " +
$"client: {string.Join(", ", GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier))}, " +
$"game session: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsCountMismatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
if (missionCount > 0)
{
if (!GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
if (!GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
{
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {string.Join(", ", serverMissionIdentifiers)}, client: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
string errorMsg =
$"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server " +
$"Server: {string.Join(", ", serverMissionIdentifiers)}, " +
$"client: {string.Join(", ", GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier))}, " +
$"game session: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
@@ -939,13 +957,6 @@ namespace Barotrauma.Networking
GUI.ClearCursorWait();
ChildServerRelay.ShutDown();
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
}
if (disconnectPacket.ShouldCreateAnalyticsEvent)
{
GameAnalyticsManager.AddErrorEventOnce(
@@ -971,11 +982,43 @@ namespace Barotrauma.Networking
}
else
{
ReturnToPreviousMenu(null, null);
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
if (ClientPeer is SteamP2PClientPeer or SteamP2POwnerPeer)
{
DisplayInLoadingScreens = true
};
SteamManager.LeaveLobby();
}
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
CampaignMode.StartRoundCancellationToken?.Cancel();
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
}
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
{
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
}
ChildServerRelay.AttemptGracefulShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove();
VoipClient?.Dispose();
VoipClient = null;
GameMain.Client = null;
GameMain.GameSession = null;
ReturnToPreviousMenu(null, null);
if (disconnectPacket.DisconnectReason != DisconnectReason.Disconnected)
{
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
{
DisplayInLoadingScreens = true
};
}
}
}
@@ -1321,6 +1364,7 @@ namespace Barotrauma.Networking
ServerSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
ServerSettings.ShowEnemyHealthBars = (EnemyHealthBarMode)inc.ReadByte();
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
GameMain.LightManager.LightingEnabled = true;
@@ -1878,12 +1922,11 @@ namespace Barotrauma.Networking
private void ReadLobbyUpdate(IReadMessage inc)
{
ServerNetObject objHeader;
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
SegmentTableReader<ServerNetSegment>.Read(inc, (segment, inc) =>
{
switch (objHeader)
switch (segment)
{
case ServerNetObject.SYNC_IDS:
case ServerNetSegment.SyncIds:
bool lobbyUpdated = inc.ReadBoolean();
inc.ReadPadBits();
@@ -2015,17 +2058,19 @@ namespace Barotrauma.Networking
lastSentChatMsgID = inc.ReadUInt16();
break;
case ServerNetObject.CLIENT_LIST:
case ServerNetSegment.ClientList:
ReadClientList(inc);
break;
case ServerNetObject.CHAT_MESSAGE:
case ServerNetSegment.ChatMessage:
ChatMessage.ClientRead(inc);
break;
case ServerNetObject.VOTE:
case ServerNetSegment.Vote:
Voting.ClientRead(inc);
break;
}
}
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.No;
});
}
readonly List<IServerSerializable> debugEntityList = new List<IServerSerializable>();
@@ -2035,117 +2080,106 @@ namespace Barotrauma.Networking
float sendingTime = inc.ReadSingle() - 0.0f;//TODO: reimplement inc.SenderConnection.RemoteTimeOffset;
ServerNetObject? prevObjHeader = null;
long prevBitPos = 0;
long prevBytePos = 0;
long prevBitLength = 0;
long prevByteLength = 0;
ServerNetObject? objHeader = null;
try
SegmentTableReader<ServerNetSegment>.Read(inc,
segmentDataReader: (segment, inc) =>
{
while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE)
switch (segment)
{
switch (objHeader)
{
case ServerNetObject.SYNC_IDS:
lastSentChatMsgID = inc.ReadUInt16();
LastSentEntityEventID = inc.ReadUInt16();
case ServerNetSegment.SyncIds:
lastSentChatMsgID = inc.ReadUInt16();
LastSentEntityEventID = inc.ReadUInt16();
bool campaignUpdated = inc.ReadBoolean();
inc.ReadPadBits();
if (campaignUpdated)
{
MultiPlayerCampaign.ClientRead(inc);
}
else if (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign)
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
}
break;
case ServerNetObject.ENTITY_POSITION:
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
UInt32 incomingUintIdentifier = inc.ReadUInt32();
UInt16 id = inc.ReadUInt16();
uint msgLength = inc.ReadVariableUInt32();
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
bool campaignUpdated = inc.ReadBoolean();
inc.ReadPadBits();
if (campaignUpdated)
{
MultiPlayerCampaign.ClientRead(inc);
}
else if (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign)
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
}
break;
case ServerNetSegment.EntityPosition:
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
UInt32 incomingUintIdentifier = inc.ReadUInt32();
UInt16 id = inc.ReadUInt16();
uint msgLength = inc.ReadVariableUInt32();
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
if (msgEndPos > inc.LengthBits)
{
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
return;
}
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
if (msgEndPos > inc.LengthBits)
{
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
}
debugEntityList.Add(entity);
if (entity != null)
debugEntityList.Add(entity);
if (entity != null)
{
if (entity is Item != isItem)
{
if (entity is Item != isItem)
{
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
}
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
uintIdentifier != incomingUintIdentifier)
{
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
}
else
{
entity.ClientReadPosition(inc, sendingTime);
}
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
}
//force to the correct position in case the entity doesn't exist
//or the message wasn't read correctly for whatever reason
inc.BitPosition = msgEndPos;
inc.ReadPadBits();
break;
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.ENTITY_EVENT:
case ServerNetObject.ENTITY_EVENT_INITIAL:
if (!EntityEventManager.Read(objHeader.Value, inc, sendingTime, debugEntityList))
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
uintIdentifier != incomingUintIdentifier)
{
return;
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
}
break;
case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc);
break;
default:
throw new Exception($"Unknown object header \"{objHeader}\"!)");
}
prevBitLength = inc.BitPosition - prevBitPos;
prevByteLength = inc.BytePosition - prevBytePos;
else
{
entity.ClientReadPosition(inc, sendingTime);
}
}
prevObjHeader = objHeader;
prevBitPos = inc.BitPosition;
prevBytePos = inc.BytePosition;
//force to the correct position in case the entity doesn't exist
//or the message wasn't read correctly for whatever reason
inc.BitPosition = msgEndPos;
inc.ReadPadBits();
break;
case ServerNetSegment.ClientList:
ReadClientList(inc);
break;
case ServerNetSegment.EntityEvent:
case ServerNetSegment.EntityEventInitial:
if (!EntityEventManager.Read(segment, inc, sendingTime, debugEntityList))
{
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
}
break;
case ServerNetSegment.ChatMessage:
ChatMessage.ClientRead(inc);
break;
default:
throw new Exception($"Unknown segment \"{segment}\"!)");
}
}
catch (Exception ex)
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.No;
},
exceptionHandler: (segment, prevSegments, ex) =>
{
List<string> errorLines = new List<string>
{
ex.Message,
"Message length: " + inc.LengthBits + " (" + inc.LengthBytes + " bytes)",
"Read position: " + inc.BitPosition,
"Header: " + (objHeader != null ? objHeader.Value.ToString() : "Error occurred on the very first header!"),
prevObjHeader != null ? "Previous header: " + prevObjHeader : "Error occurred on the very first header!",
"Previous object was " + (prevBitLength) + " bits long (" + (prevByteLength) + " bytes)",
" "
$"Segment with error: {segment}"
};
if (prevSegments.Any())
{
errorLines.Add("Prev segments: " + string.Join(", ", prevSegments));
errorLines.Add(" ");
}
errorLines.Add(ex.StackTrace.CleanupStackTrace());
errorLines.Add(" ");
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
objHeader == ServerNetObject.ENTITY_POSITION || prevObjHeader == ServerNetObject.ENTITY_POSITION)
if (prevSegments.Concat(segment.ToEnumerable()).Any(s => s.Identifier
is ServerNetSegment.EntityPosition
or ServerNetSegment.EntityEvent
or ServerNetSegment.EntityEventInitial))
{
foreach (IServerSerializable ent in debugEntityList)
{
@@ -2159,34 +2193,18 @@ namespace Barotrauma.Networking
}
}
foreach (string line in errorLines)
{
DebugConsole.ThrowError(line);
}
errorLines.Add("Last console messages:");
for (int i = DebugConsole.Messages.Count - 1; i > Math.Max(0, DebugConsole.Messages.Count - 20); i--)
{
errorLines.Add("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
}
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsManager.ErrorSeverity.Critical, string.Join("\n", errorLines));
DebugConsole.ThrowError("Writing object data to \"networkerror_data.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
using (FileStream fl = File.Open("networkerror_data.log", System.IO.FileMode.Create))
{
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fl))
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fl))
{
bw.Write(inc.Buffer, (int)(prevBytePos - prevByteLength), (int)(prevByteLength));
sw.WriteLine("");
foreach (string line in errorLines)
{
sw.WriteLine(line);
}
}
}
throw new Exception("Read error: please send us \"networkerror_data.log\"!");
}
throw new Exception(
$"Exception thrown while reading segment {segment.Identifier} at position {segment.Pointer}." +
(prevSegments.Any() ? $" Previous segments: {string.Join(", ", prevSegments)}" : ""),
ex);
});
}
private void SendLobbyUpdate()
@@ -2194,50 +2212,51 @@ namespace Barotrauma.Networking
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
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)
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(outmsg))
{
outmsg.WriteIdentifier(jobPreferences[0].Prefab.Identifier);
}
else
{
outmsg.WriteIdentifier(Identifier.Empty);
}
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
{
outmsg.WriteUInt16((UInt16)0);
}
else
{
outmsg.WriteUInt16(campaign.LastSaveID);
outmsg.WriteByte(campaign.CampaignID);
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
segmentTable.StartNewSegment(ClientNetSegment.SyncIds);
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.WriteUInt16(campaign.GetLastUpdateIdForFlag(netFlag));
outmsg.WriteIdentifier(jobPreferences[0].Prefab.Identifier);
}
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
{
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
else
{
//no more room in this packet
break;
outmsg.WriteIdentifier(Identifier.Empty);
}
chatMsgQueue[i].ClientWrite(outmsg);
}
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
{
outmsg.WriteUInt16((UInt16)0);
}
else
{
outmsg.WriteUInt16(campaign.LastSaveID);
outmsg.WriteByte(campaign.CampaignID);
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(netFlag));
}
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
{
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
{
//no more room in this packet
break;
}
chatMsgQueue[i].ClientWrite(segmentTable, outmsg);
}
}
if (outmsg.LengthBytes > MsgConstants.MTU)
{
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
@@ -2253,44 +2272,47 @@ namespace Barotrauma.Networking
outmsg.WriteBoolean(EntityEventManager.MidRoundSyncingDone);
outmsg.WritePadBits();
outmsg.WriteByte((byte)ClientNetObject.SYNC_IDS);
//outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
outmsg.WriteUInt16(ChatMessage.LastID);
outmsg.WriteUInt16(EntityEventManager.LastReceivedID);
outmsg.WriteUInt16(LastClientListUpdateID);
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(outmsg))
{
segmentTable.StartNewSegment(ClientNetSegment.SyncIds);
//outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
outmsg.WriteUInt16(ChatMessage.LastID);
outmsg.WriteUInt16(EntityEventManager.LastReceivedID);
outmsg.WriteUInt16(LastClientListUpdateID);
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
{
outmsg.WriteUInt16((UInt16)0);
}
else
{
outmsg.WriteUInt16(campaign.LastSaveID);
outmsg.WriteByte(campaign.CampaignID);
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
{
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(flag));
outmsg.WriteUInt16((UInt16)0);
}
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
Character.Controlled?.ClientWriteInput(outmsg);
GameMain.GameScreen.Cam?.ClientWrite(outmsg);
EntityEventManager.Write(outmsg, ClientPeer?.ServerConnection);
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
{
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
else
{
//not enough room in this packet
break;
}
chatMsgQueue[i].ClientWrite(outmsg);
}
outmsg.WriteUInt16(campaign.LastSaveID);
outmsg.WriteByte(campaign.CampaignID);
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.WriteUInt16(campaign.GetLastUpdateIdForFlag(flag));
}
outmsg.WriteByte((byte)ClientNetObject.END_OF_MESSAGE);
outmsg.WriteBoolean(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
Character.Controlled?.ClientWriteInput(segmentTable, outmsg);
GameMain.GameScreen.Cam?.ClientWrite(segmentTable, outmsg);
EntityEventManager.Write(segmentTable, outmsg, ClientPeer?.ServerConnection);
chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID));
for (int i = 0; i < chatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
{
if (outmsg.LengthBytes + chatMsgQueue[i].EstimateLengthBytesClient() > MsgConstants.MTU - 5)
{
//not enough room in this packet
break;
}
chatMsgQueue[i].ClientWrite(segmentTable, outmsg);
}
}
if (outmsg.LengthBytes > MsgConstants.MTU)
{
@@ -2523,7 +2545,7 @@ namespace Barotrauma.Networking
public override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null)
{
if (!(entity is IClientSerializable clientSerializable))
if (entity is not IClientSerializable clientSerializable)
{
throw new InvalidCastException($"Entity is not {nameof(IClientSerializable)}");
}
@@ -2557,46 +2579,10 @@ namespace Barotrauma.Networking
public void Quit()
{
GameMain.LuaCs.Stop();
if (ClientPeer is SteamP2PClientPeer || ClientPeer is SteamP2POwnerPeer)
{
SteamManager.LeaveLobby();
}
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
CampaignMode.StartRoundCancellationToken?.Cancel();
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
ClientPeer = null;
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
{
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
}
if (ChildServerRelay.Process != null)
{
int checks = 0;
while (ChildServerRelay.Process is { HasExited: false })
{
if (checks > 10)
{
ChildServerRelay.ShutDown();
}
Thread.Sleep(100);
checks++;
}
}
ChildServerRelay.ShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove();
VoipClient?.Dispose();
VoipClient = null;
GameMain.Client = null;
GameMain.GameSession = null;
}
public void SendCharacterInfo(string newName = null)
@@ -2604,7 +2590,6 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
WriteCharacterInfo(msg, newName);
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
@@ -2644,9 +2629,11 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ClientPacketHeader.UPDATE_LOBBY);
msg.WriteByte((byte)ClientNetObject.VOTE);
Voting.ClientWrite(msg, voteType, data);
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(msg))
{
segmentTable.StartNewSegment(ClientNetSegment.Vote);
Voting.ClientWrite(msg, voteType, data);
}
ClientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -2762,7 +2749,6 @@ namespace Barotrauma.Networking
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
msg.WriteUInt16((UInt16)ClientPermissions.ManageCampaign);
campaign.ClientWrite(msg);
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
ClientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -2811,7 +2797,6 @@ namespace Barotrauma.Networking
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);
}
@@ -2831,7 +2816,6 @@ namespace Barotrauma.Networking
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);
}
@@ -3537,6 +3521,23 @@ namespace Barotrauma.Networking
eventErrorWritten = true;
}
private static void AppendExceptionInfo(ref string errorMsg, Exception e)
{
if (!errorMsg.EndsWith("\n")) { errorMsg += "\n"; }
errorMsg += e.Message + "\n";
var innermostException = e.GetInnermost();
if (innermostException != e)
{
// If available, only append the stacktrace of the innermost exception,
// because that's the most important one to fix
errorMsg += "Inner exception: " + innermostException.Message + "\n" + innermostException.StackTrace.CleanupStackTrace();
}
else
{
errorMsg += e.StackTrace.CleanupStackTrace();
}
}
#if DEBUG
public void ForceTimeOut()
{