Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -11,6 +11,9 @@ using System.Text;
namespace Barotrauma.Networking
{
[AttributeUsage(AttributeTargets.Property)]
public class DoNotSyncOverNetwork : Attribute { }
public enum SelectionMode
{
Manual = 0, Random = 1, Vote = 2
@@ -32,6 +35,7 @@ namespace Barotrauma.Networking
public enum RespawnMode
{
None,
MidRound,
BetweenRounds,
Permadeath,
@@ -64,6 +68,7 @@ namespace Barotrauma.Networking
}
public static readonly string PermissionPresetFile = "Data" + Path.DirectorySeparatorChar + "permissionpresets.xml";
public static readonly string PermissionPresetFileCustom = "Data" + Path.DirectorySeparatorChar + "permissionpresets_player.xml";
public string Name
{
@@ -280,7 +285,9 @@ namespace Barotrauma.Networking
HiddenSubs = new HashSet<string>();
PermissionPreset.List.Clear();
PermissionPreset.LoadAll(PermissionPresetFile);
PermissionPreset.LoadAll(PermissionPresetFileCustom);
InitProjSpecific();
ServerName = serverName;
@@ -300,6 +307,7 @@ namespace Barotrauma.Networking
string typeName = SerializableProperty.GetSupportedTypeName(property.PropertyType);
if (typeName != null || property.PropertyType.IsEnum)
{
if (property.GetAttribute<DoNotSyncOverNetwork>() is not null) { continue; }
NetPropertyData netPropertyData = new NetPropertyData(this, property, typeName);
UInt32 key = ToolBoxCore.IdentifierToUint32Hash(netPropertyData.Name, md5);
if (key == 0) { key++; } //0 is reserved to indicate the end of the netproperties section of a message
@@ -412,6 +420,19 @@ namespace Barotrauma.Networking
set { tickRate = MathHelper.Clamp(value, 1, 60); }
}
private int maxLagCompensation = 150;
[Serialize(150, IsPropertySaveable.Yes, description:
"Maximum amount of lag compensation for firing weapons, in milliseconds. " +
"E.g. when a client fires a gun, the server will be notified about it with some latency, and checks if it hit anything in the past (at the time the shot was taken), up to this limit. " +
"The largest allowed lag compensation is 500 milliseconds.")]
public int MaxLagCompensation
{
get { return maxLagCompensation; }
set { maxLagCompensation = MathHelper.Clamp(value, 0, 500); }
}
public float MaxLagCompensationSeconds => maxLagCompensation / 1000.0f;
[Serialize(true, IsPropertySaveable.Yes, description: "Do clients need to be authenticated (e.g. based on Steam ID or an EGS ownership token). Can be disabled if you for example want to play the game in a local network without a connection to external services.")]
public bool RequireAuthentication
{
@@ -500,13 +521,16 @@ namespace Barotrauma.Networking
/// <summary>
/// This is an optional setting for permadeath mode. When it's enabled, a player client whose character dies cannot
/// respawn or get a new character in any way in that game (unlike in normal permadeath mode), and can only spectate.
/// </summary>
/// NOTE: this setting will do nothing if respawn mode is not set to PermaDeath.
/// </summary>
public bool IronmanMode
{
get;
private set;
}
public bool IronmanModeActive => IronmanMode && respawnMode == RespawnMode.Permadeath;
[Serialize(60.0f, IsPropertySaveable.Yes)]
public float AutoRestartInterval
{
@@ -520,6 +544,20 @@ namespace Barotrauma.Networking
get;
set;
}
[Serialize(PvpTeamSelectionMode.PlayerPreference, IsPropertySaveable.Yes)]
public PvpTeamSelectionMode PvpTeamSelectionMode
{
get;
private set;
}
[Serialize(1, IsPropertySaveable.Yes)]
public int PvpAutoBalanceThreshold
{
get;
private set;
}
[Serialize(0.8f, IsPropertySaveable.Yes)]
public float StartWhenClientsReadyRatio
@@ -527,6 +565,41 @@ namespace Barotrauma.Networking
get;
private set;
}
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float PvPStunResist
{
get;
private set;
}
[Serialize(false, IsPropertySaveable.Yes)]
public bool PvPSpawnMonsters
{
get;
private set;
}
[Serialize(true, IsPropertySaveable.Yes)]
public bool PvPSpawnWrecks
{
get;
private set;
}
[Serialize("Random", IsPropertySaveable.Yes)]
public Identifier Biome
{
get;
private set;
}
[Serialize("Random", IsPropertySaveable.Yes)]
public Identifier SelectedOutpostName
{
get;
private set;
}
private bool allowSpectating;
[Serialize(true, IsPropertySaveable.Yes)]
@@ -541,6 +614,19 @@ namespace Barotrauma.Networking
}
}
private bool allowAFK;
[Serialize(true, IsPropertySaveable.Yes)]
public bool AllowAFK
{
get { return allowAFK; }
private set
{
if (allowAFK == value) { return; }
allowAFK = value;
ServerDetailsChanged = true;
}
}
[Serialize(true, IsPropertySaveable.Yes)]
public bool SaveServerLogs
{
@@ -922,7 +1008,7 @@ namespace Barotrauma.Networking
public float KillDisconnectedTime
{
get;
private set;
set;
}
/// <summary>
@@ -993,10 +1079,14 @@ namespace Barotrauma.Networking
}
[Serialize("All", IsPropertySaveable.Yes)]
public string MissionType
public string MissionTypes
{
get;
set;
get => string.Join(",", AllowedRandomMissionTypes.Select(t => t.ToIdentifier()));
set
{
AllowedRandomMissionTypes = value.Split(",").Select(t => t.ToIdentifier()).Distinct().ToList();
ValidateMissionTypes();
}
}
[Serialize(8, IsPropertySaveable.Yes)]
@@ -1006,10 +1096,13 @@ namespace Barotrauma.Networking
set { maxPlayers = MathHelper.Clamp(value, 1, NetConfig.MaxPlayers); }
}
public List<MissionType> AllowedRandomMissionTypes
/// <summary>
/// Wrapper for <see cref="MissionTypes"/>.
/// </summary>
public List<Identifier> AllowedRandomMissionTypes
{
get;
set;
private set;
}
[Serialize(60f * 60.0f, IsPropertySaveable.Yes)]
@@ -1035,6 +1128,24 @@ namespace Barotrauma.Networking
[Serialize(0f, IsPropertySaveable.Yes)]
public float NewCampaignDefaultSalary { get; set; }
[Serialize(true, IsPropertySaveable.Yes)]
public bool TrackOpponentInPvP { get; set; }
[Serialize(7, IsPropertySaveable.Yes)]
public int DisembarkPointAllowance { get; set; }
[Serialize("", IsPropertySaveable.Yes), DoNotSyncOverNetwork]
public Identifier[] SelectedCoalitionPerks { get; set; } = Array.Empty<Identifier>();
/// <summary>
/// The score required to win a PvP mission (if it is a mission with some scoring system, such as a deathmatch mission)
/// </summary>
[Serialize(200, IsPropertySaveable.Yes)]
public int WinScorePvP { get; set; }
[Serialize("", IsPropertySaveable.Yes), DoNotSyncOverNetwork]
public Identifier[] SelectedSeparatistsPerks { get; set; } = Array.Empty<Identifier>();
public CampaignSettings CampaignSettings { get; set; } = CampaignSettings.Empty;
private bool allowSubVoting;
@@ -1093,6 +1204,9 @@ namespace Barotrauma.Networking
public void SetPassword(string password)
{
this.password = string.IsNullOrEmpty(password) ? null : password;
#if SERVER
GameMain.Server?.ClearRecentlyDisconnectedClients();
#endif
}
public static byte[] SaltPassword(byte[] password, int salt)
@@ -1136,7 +1250,7 @@ namespace Barotrauma.Networking
=> monsterEnabled.Keys
.OrderBy(k => CharacterPrefab.Prefabs[k].UintIdentifier)
.ToImmutableArray();
public bool ReadMonsterEnabled(IReadMessage inc)
{
bool changed = false;
@@ -1214,6 +1328,75 @@ namespace Barotrauma.Networking
}
}
public void WritePerks(IWriteMessage msg)
{
List<DisembarkPerkPrefab> coalitionPerks = GetPerks(SelectedCoalitionPerks);
msg.WriteVariableUInt32((uint)coalitionPerks.Count);
foreach (DisembarkPerkPrefab perk in coalitionPerks)
{
msg.WriteUInt32(perk.UintIdentifier);
}
List<DisembarkPerkPrefab> separatistsPerks = GetPerks(SelectedSeparatistsPerks);
msg.WriteVariableUInt32((uint)separatistsPerks.Count);
foreach (DisembarkPerkPrefab perk in separatistsPerks)
{
msg.WriteUInt32(perk.UintIdentifier);
}
static List<DisembarkPerkPrefab> GetPerks(Identifier[] perkIdentifiers)
{
List<DisembarkPerkPrefab> perks = new();
foreach (Identifier perk in perkIdentifiers)
{
if (!DisembarkPerkPrefab.Prefabs.TryGet(perk, out var prefab)) { continue; }
perks.Add(prefab);
}
return perks;
}
}
public bool ReadPerks(IReadMessage msg)
{
uint coalitionCount = msg.ReadVariableUInt32();
Identifier[] newCoalitionPerks = new Identifier[coalitionCount];
for (int i = 0; i < coalitionCount; i++)
{
uint id = msg.ReadUInt32();
DisembarkPerkPrefab prefab = DisembarkPerkPrefab.Prefabs.Find(p => p.UintIdentifier == id);
if (prefab == null)
{
DebugConsole.ThrowError($"Perk not found: {id}");
continue;
}
newCoalitionPerks[i] = prefab.Identifier;
}
uint separatistsCount = msg.ReadVariableUInt32();
Identifier[] newSeparatistsPerks = new Identifier[separatistsCount];
for (int i = 0; i < separatistsCount; i++)
{
uint id = msg.ReadUInt32();
DisembarkPerkPrefab prefab = DisembarkPerkPrefab.Prefabs.Find(p => p.UintIdentifier == id);
if (prefab == null)
{
DebugConsole.ThrowError($"Perk not found: {id}");
continue;
}
newSeparatistsPerks[i] = prefab.Identifier;
}
bool changed = !SelectedCoalitionPerks.SequenceEqual(newCoalitionPerks) ||
!SelectedSeparatistsPerks.SequenceEqual(newSeparatistsPerks);
SelectedCoalitionPerks = newCoalitionPerks;
SelectedSeparatistsPerks = newSeparatistsPerks;
return changed;
}
public void ReadHiddenSubs(IReadMessage msg)
{
var subList = GameMain.NetLobbyScreen.GetSubList();
@@ -1285,5 +1468,34 @@ namespace Barotrauma.Networking
}
}
}
/// <summary>
/// Ensures that there's at least one mission type selected for both co-op and PvP game modes.
/// </summary>
private void ValidateMissionTypes()
{
ValidateMissionTypes(MissionPrefab.CoOpMissionClasses.Values);
ValidateMissionTypes(MissionPrefab.PvPMissionClasses.Values);
}
private void ValidateMissionTypes(IEnumerable<Type> availableMissionClasses)
{
if (AllowedRandomMissionTypes.Contains(Tags.MissionTypeAll)) { return; }
//no selectable mission types that match any of the available mission classes
//(e.g. no mission types that use pvp-specific mission classes)
if (MissionPrefab.GetAllMultiplayerSelectableMissionTypes().None(missionType =>
MissionPrefab.Prefabs.Any(p => p.Type == missionType && AllowedRandomMissionTypes.Contains(p.Type) && availableMissionClasses.Contains(p.MissionClass))))
{
var matchingMission = MissionPrefab.Prefabs.First(p => availableMissionClasses.Contains(p.MissionClass));
if (matchingMission == null)
{
DebugConsole.ThrowError($"No missions found for any of the available mission classes ({string.Join(",", availableMissionClasses)})");
}
else
{
AllowedRandomMissionTypes.Add(matchingMission.Type);
}
}
}
}
}