Faction Test 100.4.0.0

This commit is contained in:
Markus Isberg
2022-11-14 18:28:28 +02:00
parent 87426b68b2
commit c772b61fc1
412 changed files with 16984 additions and 5530 deletions
@@ -64,13 +64,18 @@ namespace Barotrauma
msg.WriteString(ragdollFileName);
msg.WriteIdentifier(HumanPrefabIds.NpcIdentifier);
msg.WriteIdentifier(MinReputationToHire.factionId);
if (MinReputationToHire.factionId != default)
{
msg.WriteSingle(MinReputationToHire.reputation);
}
if (Job != null)
{
msg.WriteUInt32(Job.Prefab.UintIdentifier);
msg.WriteByte((byte)Job.Variant);
foreach (SkillPrefab skillPrefab in Job.Prefab.Skills.OrderBy(s => s.Identifier))
{
msg.WriteSingle(Job.GetSkill(skillPrefab.Identifier).Level);
msg.WriteSingle(Job.GetSkill(skillPrefab.Identifier)?.Level ?? 0.0f);
}
}
else
@@ -219,9 +219,9 @@ namespace Barotrauma
else if (NetIdUtils.Difference(networkUpdateID, LastNetworkUpdateID) > 500)
{
#if DEBUG || UNSTABLE
DebugConsole.AddWarning($"Large disrepancy between a client character's network update ID server-side and client-side (client: {networkUpdateID}, server: {LastNetworkUpdateID}). Resetting the ID.");
DebugConsole.AddWarning($"Large discrepancy between a client character's network update ID server-side and client-side (client: {networkUpdateID}, server: {LastNetworkUpdateID}). Resetting the ID.");
#endif
LastNetworkUpdateID = networkUpdateID;
LastNetworkUpdateID = LastProcessedID = networkUpdateID;
}
if (memInput.Count > 60)
{
@@ -269,10 +269,13 @@ namespace Barotrauma
case EventType.UpdateTalents:
if (c.Character != this)
{
if (!IsBot || !c.HasPermission(ClientPermissions.ManageBotTalents))
{
#if DEBUG
DebugConsole.Log("Received a character update message from a client who's not controlling the character");
DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
return;
return;
}
}
// get the full list of talents from the player, only give the ones
@@ -549,7 +552,7 @@ namespace Barotrauma
msg.WriteByte((byte)statType);
foreach (var savedStatValue in Info.SavedStatValues[statType])
{
msg.WriteString(savedStatValue.StatIdentifier);
msg.WriteIdentifier(savedStatValue.StatIdentifier);
msg.WriteSingle(savedStatValue.StatValue);
msg.WriteBoolean(savedStatValue.RemoveOnDeath);
}
@@ -1408,6 +1408,21 @@ namespace Barotrauma
GameMain.Server.PrintSenderTransters();
}));
AssignOnExecute("resetcharacternetstate", (string[] args) =>
{
if (GameMain.Server == null) { return; }
if (args.Length < 1)
{
ThrowError("Invalid parameters. The command should be formatted as \"resetcharacternetstate [character]\". If the names consist of multiple words, you should surround them with quotation marks.");
return;
}
var character = FindMatchingCharacter(args.Skip(1).ToArray(), false);
character?.ResetNetState();
});
commands.Add(new Command("eventdata", "", (string[] args) =>
{
if (args.Length == 0) { return; }
@@ -1665,13 +1680,7 @@ namespace Barotrauma
"teleportsub",
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
if (Submarine.MainSub == null || Level.Loaded == null) return;
if (Level.Loaded.Type == LevelData.LevelType.Outpost)
{
GameMain.Server.SendConsoleMessage("The teleportsub command is unavailable in outpost levels!", client, Color.Red);
return;
}
if (Submarine.MainSub == null || Level.Loaded == null) { return; }
if (args.Length == 0 || args[0].Equals("cursor", StringComparison.OrdinalIgnoreCase))
{
Submarine.MainSub.SetPosition(cursorWorldPos);
@@ -31,7 +31,7 @@ namespace Barotrauma
if (convAction.SelectedOption > -1)
{
//someone else already chose an option for this conversation: interrupt for this client
convAction.ServerWrite(convAction.speaker, sender, interrupt: true);
convAction.ServerWrite(convAction.Speaker, sender, interrupt: true);
}
else
{
@@ -0,0 +1,19 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class EndMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
boss.WriteSpawnData(msg, boss.ID, restrictMessageSize: false);
msg.WriteByte((byte)minions.Length);
foreach (Character minion in minions)
{
minion.WriteSpawnData(msg, minion.ID, restrictMessageSize: false);
}
}
}
}
@@ -6,33 +6,66 @@ namespace Barotrauma
{
partial class SalvageMission : Mission
{
private bool usedExistingItem;
struct SpawnInfo
{
public readonly bool UsedExistingItem;
public readonly UInt16 OriginalInventoryID;
public readonly byte OriginalItemContainerIndex;
public readonly int OriginalSlotIndex;
public readonly List<(int listIndex, int effectIndex)> ExecutedEffectIndices;
private UInt16 originalInventoryID;
private byte originalItemContainerIndex;
private int originalSlotIndex;
public SpawnInfo(bool usedExistingItem, UInt16 originalInventoryID, byte originalItemContainerIndex, int originalSlotIndex, List<(int listIndex, int effectIndex)> executedEffectIndices)
{
UsedExistingItem = usedExistingItem;
OriginalInventoryID = originalInventoryID;
OriginalItemContainerIndex = originalItemContainerIndex;
OriginalSlotIndex = originalSlotIndex;
ExecutedEffectIndices = executedEffectIndices;
}
}
private readonly List<Pair<int, int>> executedEffectIndices = new List<Pair<int, int>>();
private readonly Dictionary<Target, SpawnInfo> spawnInfo = new Dictionary<Target, SpawnInfo>();
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.WriteBoolean(usedExistingItem);
if (usedExistingItem)
foreach (var target in targets)
{
msg.WriteUInt16(item.ID);
}
else
{
item.WriteSpawnData(msg, item.ID, originalInventoryID, originalItemContainerIndex, originalSlotIndex);
}
bool targetFound = spawnInfo.ContainsKey(target) && target.Item != null;
msg.WriteBoolean(targetFound);
if (!targetFound) { continue; }
msg.WriteByte((byte)executedEffectIndices.Count);
foreach (Pair<int, int> effectIndex in executedEffectIndices)
msg.WriteBoolean(spawnInfo[target].UsedExistingItem);
if (spawnInfo[target].UsedExistingItem)
{
msg.WriteUInt16(target.Item.ID);
}
else
{
target.Item.WriteSpawnData(msg,
target.Item.ID,
spawnInfo[target].OriginalInventoryID,
spawnInfo[target].OriginalItemContainerIndex,
spawnInfo[target].OriginalSlotIndex);
}
msg.WriteByte((byte)spawnInfo[target].ExecutedEffectIndices.Count);
foreach ((int listIndex, int effectIndex) in spawnInfo[target].ExecutedEffectIndices)
{
msg.WriteByte((byte)listIndex);
msg.WriteByte((byte)effectIndex);
}
}
}
public override void ServerWrite(IWriteMessage msg)
{
base.ServerWrite(msg);
msg.WriteByte((byte)targets.Count);
for (int i = 0; i < targets.Count; i++)
{
msg.WriteByte((byte)effectIndex.First);
msg.WriteByte((byte)effectIndex.Second);
msg.WriteByte((byte)targets[i].State);
}
}
}
@@ -158,7 +158,7 @@ namespace Barotrauma
XDocument doc = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile);
if (doc?.Root == null)
{
DebugConsole.ThrowError("File \"" + ServerSettings.SettingsFile + "\" not found. Starting the server with default settings.");
DebugConsole.AddWarning("File \"" + ServerSettings.SettingsFile + "\" not found. Starting the server with default settings.");
}
else
{
@@ -23,10 +23,8 @@ namespace Barotrauma
return
client.HasPermission(permissions) ||
client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Server.ConnectedClients.Count == 1 ||
IsOwner(client) ||
//allow managing if no-one with permissions is alive
GameMain.Server.ConnectedClients.None(c => c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } && (IsOwner(c) || c.HasPermission(permissions)));
AnyOneAllowedToManageCampaign(permissions);
}
public bool AllowedToManageWallets(Client client)
@@ -335,7 +335,7 @@ namespace Barotrauma
break;
}
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
Map.ProgressWorld(this, transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
if (success)
@@ -347,6 +347,8 @@ namespace Barotrauma
(GameMain.GameSession?.GameMode as MultiPlayerCampaign)?.SaveExperiencePoints(c);
}
}
// Event history must be registered before ending the round or it will be cleared
GameMain.GameSession.EventManager.RegisterEventHistory();
}
GameMain.GameSession.EndRound("", traitorResults, transitionType);
@@ -360,7 +362,6 @@ namespace Barotrauma
LeaveUnconnectedSubs(leavingSub);
NextLevel = newLevel;
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
GameMain.GameSession.EventManager.RegisterEventHistory();
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
else
@@ -384,17 +385,14 @@ namespace Barotrauma
NextLevel = newLevel;
MirrorLevel = mirror;
//give clients time to play the end cinematic before starting the next round
if (transitionType == TransitionType.End)
{
yield return new WaitForSeconds(EndCinematicDuration);
}
else
{
yield return new WaitForSeconds(EndTransitionDuration * 0.5f);
}
GameMain.Server.StartGame();
yield return new WaitForSeconds(EndTransitionDuration * 0.5f);
//don't start the next round automatically if we just finished the campaign
if (transitionType != TransitionType.End)
{
GameMain.Server.StartGame();
}
yield return CoroutineStatus.Success;
}
@@ -417,7 +415,7 @@ namespace Barotrauma
}
public bool CanPurchaseSub(SubmarineInfo info, Client client)
=> CanAfford(info.Price, client) && GetCampaignSubs().Contains(info);
=> CanAfford(info.GetPrice(), client) && GetCampaignSubs().Contains(info);
private readonly List<CharacterCampaignData> discardedCharacters = new List<CharacterCampaignData>();
public void DiscardClientCharacterData(Client client)
@@ -486,7 +484,8 @@ namespace Barotrauma
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection)
{
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
if (transitionType == TransitionType.End)
if (transitionType == TransitionType.End ||
(Level.Loaded.IsEndBiome && transitionType == TransitionType.ProgressToNextLocation))
{
LoadNewLevel();
}
@@ -502,6 +501,14 @@ namespace Barotrauma
}
}
}
else if (Level.Loaded.IsEndBiome)
{
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
if (transitionType == TransitionType.ProgressToNextLocation)
{
LoadNewLevel();
}
}
else if (Level.Loaded.Type == LevelData.LevelType.Outpost)
{
KeepCharactersCloseToOutpost(deltaTime);
@@ -697,10 +704,6 @@ namespace Barotrauma
if (requiredFlags.HasFlag(NetFlags.Reputation))
{
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.Reputation));
Reputation reputation = Map?.CurrentLocation?.Reputation;
msg.WriteBoolean(reputation != null);
if (reputation != null) { msg.WriteSingle(reputation.Value); }
// hopefully we'll never have more than 128 factions
msg.WriteByte((byte)Factions.Count);
foreach (Faction faction in Factions)
@@ -1014,12 +1017,13 @@ namespace Barotrauma
bool predicate(SoldItem i) => allowedToSellInventoryItems != (i.Origin == SoldItem.SellOrigin.Character);
}
var characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
foreach (var (prefab, category, _) in purchasedUpgrades)
{
UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
// unstable logging
int price = prefab.Price.GetBuyPrice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
int price = prefab.Price.GetBuyPrice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation, characterList);
int level = UpgradeManager.GetUpgradeLevel(prefab, category);
GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
}
@@ -2,7 +2,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
@@ -30,6 +32,9 @@ namespace Barotrauma
switch (header)
{
case NetworkHeader.ADD_EVERYTHING_TO_PENDING:
ProcessAddEverything(sender);
break;
case NetworkHeader.REQUEST_AFFLICTIONS:
ProcessRequestedAfflictions(inc, sender);
break;
@@ -57,7 +62,14 @@ namespace Barotrauma
NetCrewMember newCrewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
InsertPendingCrewMember(newCrewMember);
ServerSend(newCrewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
ServerSend(new NetCollection<NetCrewMember>(newCrewMember), NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessAddEverything(Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
AddEverythingToPending();
ServerSend(PendingHeals.ToNetCollection(), NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessNewRemoval(IReadMessage inc, Client client)
@@ -73,12 +85,7 @@ namespace Barotrauma
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
INetSerializableStruct writeCrewMember = new NetPendingCrew
{
CrewMembers = PendingHeals.ToArray()
};
ServerSend(writeCrewMember, NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable, targetClient: client);
ServerSend(PendingHeals.ToNetCollection(), NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable, targetClient: client);
}
private void ProcessHealing(Client client)
@@ -107,10 +114,10 @@ namespace Barotrauma
CharacterInfo? foundInfo = crewMember.FindCharacterInfo(GetCrewCharacters());
NetAffliction[] pendingAfflictions = Array.Empty<NetAffliction>();
ImmutableArray<NetAffliction> pendingAfflictions = ImmutableArray<NetAffliction>.Empty;
int infoId = 0;
if (foundInfo is { Character: { CharacterHealth: { } health } })
if (foundInfo is { Character.CharacterHealth: { } health })
{
pendingAfflictions = GetAllAfflictions(health);
infoId = foundInfo.GetIdentifierUsingOriginalName();
@@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components
msg.WriteBoolean(launch);
if (launch)
{
msg.WriteUInt16(User.ID);
msg.WriteUInt16(User?.ID ?? 0);
msg.WriteSingle(launchPos.X);
msg.WriteSingle(launchPos.Y);
msg.WriteSingle(launchRot);
@@ -23,7 +23,7 @@ namespace Barotrauma.Items.Components
{
string newOutputValue = msg.ReadString();
if (item.CanClientAccess(c))
if (item.CanClientAccess(c) && !Readonly)
{
if (newOutputValue.Length > MaxMessageLength)
{
@@ -106,6 +106,14 @@ namespace Barotrauma
$"Failed to write a ChangeProperty network event for the item \"{Name}\" ({e.Message})");
}
break;
case SetItemStatEventData setItemStatEventData:
msg.WriteByte((byte)setItemStatEventData.Stats.Count);
foreach (var (key, value) in setItemStatEventData.Stats)
{
msg.WriteNetSerializableStruct(key);
msg.WriteSingle(value);
}
break;
case UpgradeEventData upgradeEventData:
var upgrade = upgradeEventData.Upgrade;
var upgradeTargets = upgrade.TargetComponents;
@@ -243,6 +251,7 @@ namespace Barotrauma
msg.WriteBoolean(hasIdCard);
if (hasIdCard)
{
msg.WriteInt32(idCardComponent.SubmarineSpecificID);
msg.WriteString(idCardComponent.OwnerName);
msg.WriteString(idCardComponent.OwnerTags);
msg.WriteByte((byte)Math.Max(0, idCardComponent.OwnerBeardIndex+1));
@@ -152,7 +152,9 @@ namespace Barotrauma.Networking
public bool IsBanned(AccountId accountId, out string reason)
{
RemoveExpired();
var bannedPlayer = bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out AccountId id) && accountId.Equals(id));
var bannedPlayer =
bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out AccountId id) && accountId.Equals(id)) ??
bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out Address adr) && adr is SteamP2PAddress steamAdr && steamAdr.SteamId.Equals(accountId));
reason = bannedPlayer?.Reason ?? string.Empty;
return bannedPlayer != null;
}
@@ -40,6 +40,8 @@ namespace Barotrauma.Networking
public float ChatSpamTimer;
public int ChatSpamCount;
public string RejectedName;
public int RoundsSincePlayedAsTraitor;
public float KickAFKTimer;
@@ -69,6 +71,9 @@ namespace Barotrauma.Networking
public DateTime JoinTime;
public static readonly TimeSpan NameChangeCoolDown = new TimeSpan(hours: 0, minutes: 0, seconds: 30);
public DateTime LastNameChangeTime;
private CharacterInfo characterInfo;
public CharacterInfo CharacterInfo
{
@@ -2516,6 +2516,7 @@ namespace Barotrauma.Networking
msg.WriteInt32(ServerSettings.MaximumMoneyTransferRequest);
msg.WriteBoolean(IsUsingRespawnShuttle());
msg.WriteByte((byte)ServerSettings.LosMode);
msg.WriteByte((byte)ServerSettings.ShowEnemyHealthBars);
msg.WriteBoolean(includesFinalize); msg.WritePadBits();
ServerSettings.WriteMonsterEnabled(msg);
@@ -2713,9 +2714,24 @@ namespace Barotrauma.Networking
CharacterTeamType newTeam = (CharacterTeamType)inc.ReadByte();
if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameId)) { return false; }
var timeSinceNameChange = DateTime.Now - c.LastNameChangeTime;
if (timeSinceNameChange < Client.NameChangeCoolDown)
{
//only send once per second at most to prevent using this for spamming
if (timeSinceNameChange.TotalSeconds > 1)
{
var coolDownRemaining = Client.NameChangeCoolDown - timeSinceNameChange;
SendDirectChatMessage($"ServerMessage.NameChangeFailedCooldownActive~[seconds]={(int)coolDownRemaining.TotalSeconds}", c);
}
c.NameId = nameId;
c.RejectedName = newName;
return false;
}
if (!newJob.IsEmpty)
{
if (!JobPrefab.Prefabs.TryGet(newJob, out JobPrefab newJobPrefab) || newJobPrefab.HiddenJob)
if (!JobPrefab.Prefabs.TryGet(newJob, out JobPrefab newJobPrefab) || newJobPrefab.HiddenJob)
{
newJob = Identifier.Empty;
}
@@ -2731,26 +2747,25 @@ namespace Barotrauma.Networking
public bool TryChangeClientName(Client c, string newName)
{
newName = Client.SanitizeName(newName);
//update client list even if the name cannot be changed to the one sent by the client,
//so the client will be informed what their actual name is
LastClientListUpdateID++;
if (newName == c.Name || string.IsNullOrEmpty(newName)) { return false; }
if (IsNameValid(c, newName))
if (newName != c.Name && !string.IsNullOrEmpty(newName) && IsNameValid(c, newName))
{
c.LastNameChangeTime = DateTime.Now;
string oldName = c.Name;
c.Name = newName;
c.RejectedName = string.Empty;
SendChatMessage($"ServerMessage.NameChangeSuccessful~[oldname]={oldName}~[newname]={newName}", ChatMessageType.Server);
LastClientListUpdateID++;
return true;
}
else
{
//update client list even if the name cannot be changed to the one sent by the client,
//so the client will be informed what their actual name is
LastClientListUpdateID++;
return false;
}
}
private bool IsNameValid(Client c, string newName)
{
newName = Client.SanitizeName(newName);
@@ -168,9 +168,10 @@ namespace Barotrauma.Networking
if (!bufferedEvent.Character.IsIncapacitated &&
NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
{
DebugConsole.Log($"Delaying reading entity event sent by a client until the character state has been processed. Event's character state: {bufferedEvent.CharacterStateID}, last processed character state: {bufferedEvent.Character.LastProcessedID}");
continue;
}
try
{
ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
@@ -250,7 +250,9 @@ namespace Barotrauma.Networking
structToSend = new ServerPeerContentPackageOrderPacket
{
ServerName = GameMain.Server.ServerName,
ContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent || cp.Files.All(f => f is SubmarineFile))
ContentPackages = ContentPackageManager.EnabledPackages.All
.Where(cp => cp.Files.Any())
.Where(cp => cp.HasMultiplayerSyncedContent || cp.Files.All(f => f is SubmarineFile))
.Select(contentPackage => new ServerContentPackage(contentPackage, timeNow))
.ToImmutableArray()
};
@@ -272,7 +272,6 @@ namespace Barotrauma.Networking
XDocument doc = new XDocument(new XElement("serversettings"));
doc.Root.SetAttributeValue("name", ServerName);
doc.Root.SetAttributeValue("public", IsPublic);
doc.Root.SetAttributeValue("port", Port);
#if USE_STEAM
doc.Root.SetAttributeValue("queryport", QueryPort);