(4b33eda28) Merge branch 'dev' into enemy-ai

This commit is contained in:
Joonas Rikkonen
2019-03-25 19:49:34 +02:00
parent 7d74b5bd52
commit 26594e2c6c
31 changed files with 207 additions and 148 deletions
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")] [assembly: AssemblyVersion("0.8.9.7")]
[assembly: AssemblyFileVersion("0.8.9.6")] [assembly: AssemblyFileVersion("0.8.9.7")]
@@ -403,6 +403,11 @@ namespace Barotrauma
AssignRelayToServer("help", false); AssignRelayToServer("help", false);
AssignRelayToServer("verboselogging", false); AssignRelayToServer("verboselogging", false);
AssignRelayToServer("freecam", false); AssignRelayToServer("freecam", false);
#if DEBUG
AssignRelayToServer("simulatedlatency", false);
AssignRelayToServer("simulatedloss", false);
AssignRelayToServer("simulatedduplicateschance", false);
#endif
commands.Add(new Command("clientlist", "", (string[] args) => { })); commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true); AssignRelayToServer("clientlist", true);
@@ -957,6 +962,8 @@ namespace Barotrauma
} }
} }
} }
element.Value = lines[i];
i++;
} }
}, isCheat: false)); }, isCheat: false));
#endif #endif
@@ -785,8 +785,9 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in c.AnimController.Limbs) foreach (Limb limb in c.AnimController.Limbs)
{ {
float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared(); if (!limb.body.Enabled) { continue; }
float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared();
if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue; if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue;
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
@@ -1,4 +1,6 @@
using Barotrauma.Particles; using Barotrauma.Networking;
using Barotrauma.Particles;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic; using System.Collections.Generic;
@@ -112,8 +114,7 @@ namespace Barotrauma.Items.Components
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts"); System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children) foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{ {
Skill skill = c.UserData as Skill; if (!(c.UserData is Skill skill)) continue;
if (skill == null) continue;
GUITextBlock textBlock = (GUITextBlock)c; GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < skill.Level) if (character.GetSkillLevel(skill.Identifier) < skill.Level)
@@ -126,5 +127,15 @@ namespace Barotrauma.Items.Components
} }
} }
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
} }
} }
@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>(); Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions) foreach (Affliction affliction in allAfflictions)
{ {
if (affliction.Strength < affliction.Prefab.ActivationThreshold || affliction.Strength <= 0.0f) continue; if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue;
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab)) if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{ {
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength; combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
@@ -622,7 +622,7 @@ namespace Barotrauma.Networking
if (gameStarted) SetRadioButtonColor(); if (gameStarted) SetRadioButtonColor();
if (ShowNetStats) if (ShowNetStats && client?.ServerConnection != null)
{ {
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes); netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes);
netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes); netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes);
@@ -1109,7 +1109,7 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ")."; string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true); DebugConsole.ThrowError(errorMsg, createMessageBox: true);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch"+levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame("")); CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -1687,6 +1687,8 @@ namespace Barotrauma.Networking
SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession); SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession);
campaign.LastSaveID = campaign.PendingSaveID; campaign.LastSaveID = campaign.PendingSaveID;
DebugConsole.Log("Campaign save received, save ID " + campaign.LastSaveID);
//decrement campaign update ID so the server will send us the latest data //decrement campaign update ID so the server will send us the latest data
//(as there may have been campaign updates after the save file was created) //(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--; campaign.LastUpdateID--;
@@ -1704,7 +1706,7 @@ namespace Barotrauma.Networking
public override void CreateEntityEvent(INetSerializable entity, object[] extraData) public override void CreateEntityEvent(INetSerializable entity, object[] extraData)
{ {
if (!(entity is IClientSerializable)) throw new InvalidCastException("entity is not IClientSerializable"); if (!(entity is IClientSerializable)) throw new InvalidCastException("Entity is not IClientSerializable");
entityEventManager.CreateEvent(entity as IClientSerializable, extraData); entityEventManager.CreateEvent(entity as IClientSerializable, extraData);
} }
@@ -1973,7 +1975,8 @@ namespace Barotrauma.Networking
msg.Write(true); msg.WritePadBits(); msg.Write(true); msg.WritePadBits();
msg.Write(savePath); msg.Write(savePath);
msg.Write(mapSeed); msg.Write(mapSeed);
msg.Write(sub.FilePath); msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")] [assembly: AssemblyVersion("0.8.9.7")]
[assembly: AssemblyFileVersion("0.8.9.6")] [assembly: AssemblyFileVersion("0.8.9.7")]
@@ -11,11 +11,11 @@ namespace Barotrauma
{ {
private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>(); private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>();
public static void StartNewCampaign(string savePath, string subName, string seed) public static void StartNewCampaign(string savePath, string subPath, string seed)
{ {
if (string.IsNullOrWhiteSpace(savePath)) return; if (string.IsNullOrWhiteSpace(savePath)) return;
GameMain.GameSession = new GameSession(new Submarine(subName, ""), savePath, GameMain.GameSession = new GameSession(new Submarine(subPath, ""), savePath,
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign")); GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode); var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(seed); campaign.GenerateMap(seed);
@@ -251,6 +251,7 @@ namespace Barotrauma
} }
lastSaveID++; lastSaveID++;
DebugConsole.Log("Campaign saved, save ID " + lastSaveID);
} }
} }
} }
@@ -1,9 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -14,5 +10,16 @@ namespace Barotrauma.Items.Components
//let the clients know the initial deterioration delay //let the clients know the initial deterioration delay
item.CreateServerEvent(this); item.CreateServerEvent(this);
} }
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
} }
} }
@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
//when was a specific entity event last sent to the client //when was a specific entity event last sent to the client
// key = event id, value = NetTime.Now when sending // key = event id, value = NetTime.Now when sending
public readonly Dictionary<UInt16, float> EntityEventLastSent = new Dictionary<UInt16, float>(); public readonly Dictionary<UInt16, double> EntityEventLastSent = new Dictionary<UInt16, double>();
//when was a position update for a given entity last sent to the client //when was a position update for a given entity last sent to the client
// key = entity id, value = NetTime.Now when sending // key = entity id, value = NetTime.Now when sending
@@ -172,7 +172,7 @@ namespace Barotrauma.Networking
transfer.WaitTimer -= deltaTime; transfer.WaitTimer -= deltaTime;
if (transfer.WaitTimer > 0.0f) continue; if (transfer.WaitTimer > 0.0f) continue;
if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) continue; if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel)) continue;
transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime; transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime;
@@ -187,13 +187,6 @@ namespace Barotrauma.Networking
{ {
message = peer.CreateMessage(); message = peer.CreateMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER); message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Initiate);
message.Write((byte)transfer.FileType);
message.Write((ushort)chunkLen);
message.Write((ulong)transfer.Data.Length);
message.Write(transfer.FileName);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
//if the recipient is the owner of the server (= a client running the server from the main exe) //if the recipient is the owner of the server (= a client running the server from the main exe)
//we don't need to send anything, the client can just read the file directly //we don't need to send anything, the client can just read the file directly
@@ -715,23 +715,25 @@ namespace Barotrauma.Networking
{ {
string savePath = inc.ReadString(); string savePath = inc.ReadString();
string seed = inc.ReadString(); string seed = inc.ReadString();
string subPath = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString();
if (!File.Exists(subPath)) var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
if (matchingSub == null)
{ {
SendDirectChatMessage( SendDirectChatMessage(
TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subpath]", subPath), TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subname]", subName),
connectedClient, ChatMessageType.MessageBox); connectedClient, ChatMessageType.MessageBox);
} }
else else
{ {
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, subPath, seed); if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed);
} }
} }
else else
{ {
string saveName = inc.ReadString(); string saveName = inc.ReadString();
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName); if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName);
} }
break; break;
@@ -1335,10 +1337,6 @@ namespace Barotrauma.Networking
WriteClientList(c, outmsg); WriteClientList(c, outmsg);
clientListBytes = outmsg.LengthBytes - clientListBytes; clientListBytes = outmsg.LengthBytes - clientListBytes;
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
int chatMessageBytes = outmsg.LengthBytes; int chatMessageBytes = outmsg.LengthBytes;
WriteChatMessages(outmsg, c); WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes; chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
@@ -1387,25 +1385,55 @@ namespace Barotrauma.Networking
errorMsg += errorMsg +=
" Client list size: " + clientListBytes + " bytes\n" + " Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" + " Chat message size: " + chatMessageBytes + " bytes\n" +
" Event size: " + eventManagerBytes + " bytes\n" +
" Position update size: " + positionUpdateBytes + " bytes\n\n"; " Position update size: " + positionUpdateBytes + " bytes\n\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
} }
CompressOutgoingMessage(outmsg); CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
//---------------------------------------------------------------------------
for (int i = 0; i < NetConfig.MaxEventPacketsPerUpdate; i++)
{
outmsg = server.CreateMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)NetTime.Now);
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
if (sentEvents.Count == 0)
{
break;
}
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
if (outmsg.LengthBytes > NetPeerConfiguration.MaximumTransmissionUnit)
{
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + NetPeerConfiguration.MaximumTransmissionUnit + ")\n";
errorMsg +=
" Event size: " + eventManagerBytes + " bytes\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
}
} }
private void WriteClientList(Client c, NetOutgoingMessage outmsg) private void WriteClientList(Client c, NetOutgoingMessage outmsg)
@@ -164,7 +164,7 @@ namespace Barotrauma.Networking
if (connectedClient != null) if (connectedClient != null)
{ {
Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage); Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage);
KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid_[status]={status.ToString()}"); KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid~[status]={status.ToString()}");
} }
}*/ }*/
} }
@@ -342,7 +342,7 @@ namespace Barotrauma.Networking
if (clVersion != GameMain.Version.ToString()) if (clVersion != GameMain.Version.ToString())
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion_[version]={GameMain.Version.ToString()}_[clientversion]={clVersion}"); $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={clVersion}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red); DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red);
@@ -368,7 +368,7 @@ namespace Barotrauma.Networking
if (missingPackages.Count == 1) if (missingPackages.Count == 1)
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage_[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -376,7 +376,7 @@ namespace Barotrauma.Networking
{ {
List<string> packageStrs = new List<string>(); List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages_[missingcontentpackages]={string.Join(", ", packageStrs)}"); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -399,7 +399,7 @@ namespace Barotrauma.Networking
if (incompatiblePackages.Count == 1) if (incompatiblePackages.Count == 1)
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackage_[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}"); $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -408,7 +408,7 @@ namespace Barotrauma.Networking
List<string> packageStrs = new List<string>(); List<string> packageStrs = new List<string>();
incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp))); incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackages_[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -500,7 +500,7 @@ namespace Barotrauma.Networking
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message) private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message)
{ {
inc.SenderConnection.Disconnect(reason.ToString() + "/ " + message); inc.SenderConnection.Disconnect(reason.ToString() + "/ " + TextManager.GetServerMessage(message));
if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); } if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); }
if (unauthClient != null) if (unauthClient != null)
{ {
@@ -228,7 +228,7 @@ namespace Barotrauma.Networking
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event " GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
+ (c.LastRecvEntityEventID + 1).ToString() + + (c.LastRecvEntityEventID + 1).ToString() +
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" + " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage); " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent"); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
} }
); );
@@ -242,7 +242,7 @@ namespace Barotrauma.Networking
toKick.ForEach(c => toKick.ForEach(c =>
{ {
DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red); DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.ServerMessage); GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent"); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
}); });
} }
@@ -251,7 +251,7 @@ namespace Barotrauma.Networking
var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut); var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);
foreach (Client timedOutClient in timedOutClients) foreach (Client timedOutClient in timedOutClients)
{ {
GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.ServerMessage); GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout"); GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout");
} }
@@ -305,19 +305,7 @@ namespace Barotrauma.Networking
} }
//too many events for one packet //too many events for one packet
if (eventsToSync.Count > MaxEventsPerWrite) if (eventsToSync.Count > 200)
{
if (eventsToSync.Count > MaxEventsPerWrite * 3 && !client.NeedsMidRoundSync)
{
Color color = eventsToSync.Count > MaxEventsPerWrite * 20 ? Color.Red : Color.Orange;
if (eventsToSync.Count < MaxEventsPerWrite * 5) { color = Color.Yellow; }
DebugConsole.NewMessage("WARNING: event count very high: " + eventsToSync.Count + "/" + MaxEventsPerWrite, color);
}
eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite);
}
foreach (NetEntityEvent entityEvent in eventsToSync)
{ {
if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync) if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync)
{ {
@@ -350,7 +338,7 @@ namespace Barotrauma.Networking
msg.Write(client.UnreceivedEntityEventCount); msg.Write(client.UnreceivedEntityEventCount);
msg.Write(client.FirstNewEventID); msg.Write(client.FirstNewEventID);
Write(msg, eventsToSync, client); Write(msg, eventsToSync, out sentEvents, client);
} }
else else
{ {
@@ -387,10 +375,10 @@ namespace Barotrauma.Networking
for (int i = startIndex; i < eventList.Count; i++) for (int i = startIndex; i < eventList.Count; i++)
{ {
//find the first event that hasn't been sent in 1.5 * roundtriptime or at all //find the first event that hasn't been sent in roundtriptime or at all
client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent); client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent);
float minInterval = Math.Max(client.Connection.AverageRoundtripTime * 1.5f, (float)server.UpdateInterval.TotalSeconds * 2); float minInterval = Math.Max(client.Connection.AverageRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2);
if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f)) if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f))
{ {
@@ -416,7 +404,7 @@ namespace Barotrauma.Networking
} }
else else
{ {
double midRoundSyncTimeOut = uniqueEvents.Count / MaxEventsPerWrite * server.UpdateInterval.TotalSeconds; double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds;
midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f); midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f);
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count; client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;
@@ -1110,13 +1110,15 @@ namespace Barotrauma
ViewTarget = null; ViewTarget = null;
if (!AllowInput) return; if (!AllowInput) return;
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition; if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer))
if (Controlled == this)
{ {
SmoothedCursorPosition = cursorPosition; SmoothedCursorPosition = cursorPosition;
} }
else else
{ {
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff); smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff; SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
} }
@@ -1664,10 +1666,12 @@ namespace Barotrauma
focusedItem = null; focusedItem = null;
} }
findFocusedTimer -= deltaTime; findFocusedTimer -= deltaTime;
} }
#endif #endif
//climb ladders automatically when pressing up/down inside their trigger area //climb ladders automatically when pressing up/down inside their trigger area
if (SelectedConstruction == null && !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen) Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
if ((SelectedConstruction == null || currentLadder != null) &&
!AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
{ {
bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down); bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down);
bool isControlled = Controlled == this; bool isControlled = Controlled == this;
@@ -1678,6 +1682,19 @@ namespace Barotrauma
float minDist = float.PositiveInfinity; float minDist = float.PositiveInfinity;
foreach (Ladder ladder in Ladder.List) foreach (Ladder ladder in Ladder.List)
{ {
if (ladder == currentLadder)
{
continue;
}
else if (currentLadder != null)
{
//only switch from ladder to another if the ladders are above the current ladders and pressing up, or vice versa
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y != IsKeyDown(InputType.Up))
{
continue;
}
}
if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist) if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist)
{ {
minDist = dist; minDist = dist;
@@ -153,7 +153,10 @@ namespace Barotrauma
//how high the strength has to be for the affliction icon to be shown in the UI //how high the strength has to be for the affliction icon to be shown in the UI
public readonly float ShowIconThreshold = 0.05f; public readonly float ShowIconThreshold = 0.05f;
public readonly float MaxStrength = 100.0f; public readonly float MaxStrength = 100.0f;
//how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f;
public float BurnOverlayAlpha; public float BurnOverlayAlpha;
public float DamageOverlayAlpha; public float DamageOverlayAlpha;
@@ -257,6 +260,8 @@ namespace Barotrauma
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f)); ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
@@ -460,6 +460,7 @@ namespace Barotrauma
{ {
affliction.Strength = 0.0f; affliction.Strength = 0.0f;
} }
CalculateVitality();
} }
private void AddLimbAffliction(Limb limb, Affliction newAffliction) private void AddLimbAffliction(Limb limb, Affliction newAffliction)
@@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f; public static float SkillIncreaseMultiplier = 0.4f;
private string header; private string header;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer; private float deteriorationTimer;
@@ -52,17 +50,20 @@ namespace Barotrauma.Items.Components
set; set;
} }
/*private float repairProgress; [Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")]
public float RepairProgress public float FixDurationLowSkill
{ {
get { return repairProgress; } get;
set set;
{ }
repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f);
if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None; [Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")]
} public float FixDurationHighSkill
}*/ {
get;
set;
}
private Character currentFixer; private Character currentFixer;
public Character CurrentFixer public Character CurrentFixer
{ {
@@ -83,8 +84,6 @@ namespace Barotrauma.Items.Components
this.item = item; this.item = item;
header = element.GetAttributeString("name", ""); header = element.GetAttributeString("name", "");
fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f);
fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f);
InitProjSpecific(element); InitProjSpecific(element);
} }
@@ -160,7 +159,7 @@ namespace Barotrauma.Items.Components
} }
bool wasBroken = !item.IsFullCondition; bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor); float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f) if (fixDuration <= 0.0f)
{ {
item.Condition = item.MaxCondition; item.Condition = item.MaxCondition;
@@ -186,26 +185,5 @@ namespace Barotrauma.Items.Components
{ {
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f)); character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
} }
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
} }
} }
@@ -59,7 +59,7 @@ namespace Barotrauma
public PhysicsBody body; public PhysicsBody body;
public readonly XElement StaticBodyConfig; public readonly XElement StaticBodyConfig;
private float lastSentCondition; private float lastSentCondition;
private float sendConditionUpdateTimer; private float sendConditionUpdateTimer;
private bool conditionUpdatePending; private bool conditionUpdatePending;
@@ -274,12 +274,11 @@ namespace Barotrauma
SetActiveSprite(); SetActiveSprite();
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition) if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition))
{ {
if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health) if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health)
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); conditionUpdatePending = true;
lastSentCondition = condition;
} }
} }
} }
@@ -996,6 +995,21 @@ namespace Barotrauma
aiTarget.SightRange -= deltaTime * 1000.0f; aiTarget.SightRange -= deltaTime * 1000.0f;
aiTarget.SoundRange -= deltaTime * 1000.0f; aiTarget.SoundRange -= deltaTime * 1000.0f;
} }
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending)
{
if (sendConditionUpdateTimer <= 0.0f)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval;
conditionUpdatePending = false;
}
}
}
ApplyStatusEffects(ActionType.Always, deltaTime, null); ApplyStatusEffects(ActionType.Always, deltaTime, null);
@@ -1272,7 +1286,11 @@ namespace Barotrauma
LastSentSignalRecipients.Clear(); LastSentSignalRecipients.Clear();
if (connections == null) return; if (connections == null) return;
stepsTaken++; public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
{
List<T> connectedComponents = new List<T>();
List<Item> alreadySearched = new List<Item>() { this };
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
if (!connections.TryGetValue(connectionName, out Connection c)) return; if (!connections.TryGetValue(connectionName, out Connection c)) return;
@@ -231,7 +231,7 @@ namespace Barotrauma
public static Level CreateRandom(LocationConnection locationConnection) public static Level CreateRandom(LocationConnection locationConnection)
{ {
string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name; string seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName;
float sizeFactor = MathUtils.InverseLerp( float sizeFactor = MathUtils.InverseLerp(
MapGenerationParams.Instance.SmallLevelConnectionLength, MapGenerationParams.Instance.SmallLevelConnectionLength,
@@ -15,7 +15,6 @@ namespace Barotrauma
public Vector3 Position; public Vector3 Position;
public float NetworkUpdateTimer; public float NetworkUpdateTimer;
public const float NetworkUpdateInterval = 0.2f;
public float Scale; public float Scale;
@@ -343,7 +343,7 @@ namespace Barotrauma
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj }); GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj });
obj.NeedsNetworkSyncing = false; obj.NeedsNetworkSyncing = false;
obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval; obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval;
} }
} }
@@ -15,6 +15,8 @@ namespace Barotrauma
public int TypeChangeTimer; public int TypeChangeTimer;
public string BaseName { get => baseName; }
public string Name { get; private set; } public string Name { get; private set; }
public Vector2 MapPosition { get; private set; } public Vector2 MapPosition { get; private set; }
@@ -34,7 +36,7 @@ namespace Barotrauma
for (int i = availableMissions.Count; i < Connections.Count * 2; i++) for (int i = availableMissions.Count; i < Connections.Count * 2; i++)
{ {
int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue; int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue;
MTRandom rand = new MTRandom(seed); MTRandom rand = new MTRandom(seed);
LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count]; LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count];
@@ -578,10 +578,12 @@ namespace Barotrauma
location.MissionsCompleted = missionsCompleted; location.MissionsCompleted = missionsCompleted;
if (showNotifications && prevLocationType != location.Type) if (showNotifications && prevLocationType != location.Type)
{ {
ChangeLocationType( var change = prevLocationType.CanChangeTo.Find(c =>
location, c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant());
prevLocationName, if (change != null)
prevLocationType.CanChangeTo.Find(c => c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant())); {
ChangeLocationType(location, prevLocationName, change);
}
} }
break; break;
case "connection": case "connection":
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Xml.Linq; using System.Xml.Linq;
using Voronoi2; using Voronoi2;
@@ -7,7 +7,7 @@
enum FileTransferMessageType enum FileTransferMessageType
{ {
Unknown, Initiate, Data, Cancel Unknown, Initiate, Data, TransferOnSameMachine, Cancel
} }
enum FileTransferType enum FileTransferType
@@ -32,11 +32,13 @@ namespace Barotrauma.Networking
public const float HighPrioCharacterPositionUpdateInterval = 0.0f; public const float HighPrioCharacterPositionUpdateInterval = 0.0f;
public const float LowPrioCharacterPositionUpdateInterval = 1.0f; public const float LowPrioCharacterPositionUpdateInterval = 1.0f;
//how much the physics body of an item has to move until the server public const float DeleteDisconnectedTime = 20.0f;
//send a position update to clients (in sim units)
public const float ItemPosUpdateDistance = 2.0f; public const float ItemConditionUpdateInterval = 0.15f;
public const float DeleteDisconnectedTime = 10.0f; public const float LevelObjectUpdateInterval = 0.5f;
public const int MaxEventPacketsPerUpdate = 4;
/// <summary> /// <summary>
/// Interpolates the positional error of a physics body towards zero. /// Interpolates the positional error of a physics body towards zero.
@@ -58,15 +58,8 @@ namespace Barotrauma.Networking
eventCount++; eventCount++;
continue; continue;
} }
//the ID has been taken by another entity (the original entity has been removed) -> write an empty event
/*else if (Entity.FindEntityByID(e.Entity.ID) != e.Entity || e.Entity.IdFreed) if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength)
{
//technically the clients don't have any use for these, but removing events and shifting the IDs of all
//consecutive ones is so error-prone that I think this is a safer option
tempBuffer.Write(Entity.NullEntityID);
tempBuffer.WritePadBits();
}*/
else
{ {
//no more room in this packet //no more room in this packet
break; break;
Binary file not shown.
Binary file not shown.
@@ -9,6 +9,8 @@ Additions and changes:
- Clients communicate syncing errors to the server, and the server logs a more descriptive error about - Clients communicate syncing errors to the server, and the server logs a more descriptive error about
what went wrong. Should make it easier to diagnose disconnection issues from now on. what went wrong. Should make it easier to diagnose disconnection issues from now on.
- Ending a multiplayer campaign round by talking to watchman doesn't require any special permissions. - Ending a multiplayer campaign round by talking to watchman doesn't require any special permissions.
- Server automatically ends rounds if there have been no players alive in 60 seconds and respawning
is not allowed during the round.
- Added a button for resetting an entity's properties to the default values to the sub editor. - Added a button for resetting an entity's properties to the default values to the sub editor.
- Updated handheld sonar UI graphics. - Updated handheld sonar UI graphics.