Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2023-03-13 13:32:14 -03:00
335 changed files with 11052 additions and 5179 deletions
@@ -71,6 +71,11 @@ 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);
@@ -86,7 +91,7 @@ namespace Barotrauma
msg.WriteByte((byte)0);
}
msg.WriteUInt16((ushort)ExperiencePoints);
msg.WriteInt32(ExperiencePoints);
msg.WriteRangedInteger(AdditionalTalentPoints, 0, MaxAdditionalTalentPoints);
}
}
@@ -696,6 +696,7 @@ namespace Barotrauma
{
msg.WriteIdentifier(MerchantIdentifier);
}
msg.WriteIdentifier(Faction);
int msgLengthBeforeOrders = msg.LengthBytes;
// Current orders
@@ -1428,6 +1428,44 @@ namespace Barotrauma
}));
commands.Add(new Command("forcelocationtypechange", "", (string[] args) =>
{
if (GameMain.Server == null || GameMain.GameSession?.Campaign == null) { return; }
if (args.Length < 2)
{
ThrowError("Invalid parameters. The command should be formatted as \"forcelocationtypechange [locationname] [locationtype]\". If the names consist of multiple words, you should surround them with quotation marks.");
return;
}
var location = GameMain.GameSession.Campaign.Map.Locations.FirstOrDefault(l => l.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (location == null)
{
ThrowError($"Could not find a location with the name {args[0]}.");
return;
}
var locationType = LocationType.Prefabs.FirstOrDefault(lt =>
lt.Name.Equals(args[1], StringComparison.OrdinalIgnoreCase) || lt.Identifier == args[1]);
if (location == null)
{
ThrowError($"Could not find the location type {args[1]}.");
return;
}
location.ChangeType(GameMain.GameSession.Campaign, locationType);
},
() =>
{
if (GameMain.GameSession?.Campaign == null) { return null; }
return new string[][]
{
GameMain.GameSession.Campaign.Map.Locations.Select(l => l.Name).ToArray(),
LocationType.Prefabs.Select(lt => lt.Name.Value).ToArray()
};
}));
AssignOnExecute("resetcharacternetstate", (string[] args) =>
{
if (GameMain.Server == null) { return; }
@@ -1699,13 +1737,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);
@@ -1961,7 +1993,7 @@ namespace Barotrauma
{
GameMain.Server.SendConsoleMessage("Could not find the specified character.", client, Color.Red);
}
killedCharacter?.SetAllDamage(200.0f, 0.0f, 0.0f);
killedCharacter?.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null);
}
);
@@ -41,7 +41,7 @@ namespace Barotrauma
clientsToRemove.Add(k);
}
}
if (!(clientsToRemove is null))
if (clientsToRemove is not null)
{
foreach (var k in clientsToRemove)
{
@@ -62,7 +62,7 @@ namespace Barotrauma
{
foreach (Entity e in targets)
{
if (!(e is Character character) || !character.IsRemotePlayer) { continue; }
if (e is not Character character || !character.IsRemotePlayer) { continue; }
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
if (targetClient != null)
{
@@ -85,7 +85,7 @@ namespace Barotrauma
IEnumerable<Entity> entities = ParentEvent.GetTargets(TargetTag);
foreach (Entity e in entities)
{
if (!(e is Character character) || !character.IsRemotePlayer) { continue; }
if (e is not Character character || !character.IsRemotePlayer) { continue; }
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
if (targetClient != null)
{
@@ -149,5 +149,15 @@ namespace Barotrauma
}
GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
}
public void ServerWriteSelectedOption(Client client)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.CONVERSATION_SELECTED_OPTION);
outmsg.WriteUInt16(Identifier);
outmsg.WriteByte((byte)(selectedOption + 1));
GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
}
}
}
@@ -0,0 +1,43 @@
using Barotrauma.Networking;
using System.Collections.Generic;
namespace Barotrauma
{
partial class MissionAction : EventAction
{
private static readonly HashSet<Mission> missionsUnlockedThisRound = new HashSet<Mission>();
public static void ResetMissionsUnlockedThisRound()
{
missionsUnlockedThisRound.Clear();
}
public static void NotifyMissionsUnlockedThisRound(Client client)
{
foreach (Mission mission in missionsUnlockedThisRound)
{
NotifyMissionUnlock(mission, client);
}
}
private static void NotifyMissionUnlock(Mission mission)
{
foreach (Client client in GameMain.Server.ConnectedClients)
{
NotifyMissionUnlock(mission, client);
}
}
private static void NotifyMissionUnlock(Mission mission, Client client)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION);
outmsg.WriteIdentifier(mission.Prefab.Identifier);
outmsg.WriteInt32(GameMain.GameSession?.Map?.Locations.IndexOf(mission.Locations[0]) ?? -1);
outmsg.WriteInt32(GameMain.GameSession?.Map?.Locations.IndexOf(mission.Locations[1]) ?? -1);
outmsg.WriteString(mission.Name.Value);
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
}
}
}
@@ -14,12 +14,12 @@ namespace Barotrauma
foreach (Event ev in activeEvents)
{
if (!(ev is ScriptedEvent scriptedEvent)) { continue; }
if (ev is not ScriptedEvent scriptedEvent) { continue; }
var actions = FindActions(scriptedEvent);
foreach (EventAction action in actions.Select(a => a.Item2))
{
if (!(action is ConversationAction convAction) || convAction.Identifier != actionId) { continue; }
if (action is not ConversationAction convAction || convAction.Identifier != actionId) { continue; }
if (!convAction.TargetClients.Contains(sender))
{
#if DEBUG || UNSTABLE
@@ -42,6 +42,14 @@ namespace Barotrauma
else
{
convAction.SelectedOption = selectedOption;
if (convAction.Options.Any() && !convAction.GetEndingOptions().Contains(selectedOption))
{
foreach (Client c in convAction.TargetClients)
{
if (c == sender) { continue; }
convAction.ServerWriteSelectedOption(c);
}
}
}
}
return;
@@ -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);
}
}
}
@@ -88,9 +88,6 @@ namespace Barotrauma
Console.WriteLine("Loading game settings");
GameSettings.Init();
Console.WriteLine("Loading MD5 hash cache");
Md5Hash.Cache.Load();
Console.WriteLine("Initializing SteamManager");
SteamManager.Initialize();
@@ -189,7 +186,7 @@ namespace Barotrauma
for (int i = 0; i < CommandLineArgs.Length; i++)
{
switch (CommandLineArgs[i].Trim())
switch (CommandLineArgs[i].Trim().ToLowerInvariant())
{
case "-name":
name = CommandLineArgs[i + 1];
@@ -262,7 +259,7 @@ namespace Barotrauma
for (int i = 0; i < CommandLineArgs.Length; i++)
{
switch (CommandLineArgs[i].Trim())
switch (CommandLineArgs[i].Trim().ToLowerInvariant())
{
case "-playstyle":
Enum.TryParse(CommandLineArgs[i + 1], out PlayStyle playStyle);
@@ -284,6 +281,14 @@ namespace Barotrauma
Server.ServerSettings.KarmaPreset = karmaPresetName;
i++;
break;
case "-language":
LanguageIdentifier language = CommandLineArgs[i + 1].ToLanguageIdentifier();
if (ServerLanguageOptions.Options.Any(o => o.Identifier == language))
{
Server.ServerSettings.Language = language;
}
i++;
break;
}
}
}
@@ -85,6 +85,7 @@ namespace Barotrauma
{
if (purchasedHullRepairs == value) { return; }
purchasedHullRepairs = value;
PurchasedHullRepairsInLatestSave |= value;
IncrementLastUpdateIdForFlag(NetFlags.Misc);
}
}
@@ -95,6 +96,7 @@ namespace Barotrauma
{
if (purchasedLostShuttles == value) { return; }
purchasedLostShuttles = value;
PurchasedLostShuttlesInLatestSave |= value;
IncrementLastUpdateIdForFlag(NetFlags.Misc);
}
}
@@ -105,6 +107,7 @@ namespace Barotrauma
{
if (purchasedItemRepairs == value) { return; }
purchasedItemRepairs = value;
PurchasedItemRepairsInLatestSave |= value;
IncrementLastUpdateIdForFlag(NetFlags.Misc);
}
}
@@ -337,11 +340,12 @@ namespace Barotrauma
IsFirstRound = true;
break;
case TransitionType.ProgressToNextEmptyLocation:
Map.Visit(Map.CurrentLocation);
TotalPassedLevels++;
break;
}
Map.ProgressWorld(transitionType, GameMain.GameSession.RoundDuration);
Map.ProgressWorld(this, transitionType, GameMain.GameSession.RoundDuration);
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
if (success)
@@ -391,17 +395,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.TryStartGame();
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.TryStartGame();
}
yield return CoroutineStatus.Success;
}
@@ -424,7 +425,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)
@@ -493,7 +494,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();
}
@@ -509,6 +511,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);
@@ -704,10 +714,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)
@@ -823,54 +829,37 @@ namespace Barotrauma
Bank.ForceUpdate();
}
if (purchasedHullRepairs != PurchasedHullRepairs)
if (purchasedHullRepairs && !PurchasedHullRepairs)
{
switch (purchasedHullRepairs)
if (GetBalance(sender) >= hullRepairCost)
{
case true when GetBalance(sender) >= hullRepairCost:
TryPurchase(sender, hullRepairCost);
PurchasedHullRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
break;
case false:
PurchasedHullRepairs = false;
personalWallet.Refund(hullRepairCost);
break;
TryPurchase(sender, hullRepairCost);
PurchasedHullRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
}
}
if (purchasedItemRepairs != PurchasedItemRepairs)
if (purchasedItemRepairs && !PurchasedItemRepairs)
{
switch (purchasedItemRepairs)
if (GetBalance(sender) >= itemRepairCost)
{
case true when GetBalance(sender) >= itemRepairCost:
TryPurchase(sender, itemRepairCost);
PurchasedItemRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
break;
case false:
PurchasedItemRepairs = false;
personalWallet.Refund(itemRepairCost);
break;
TryPurchase(sender, itemRepairCost);
PurchasedItemRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
}
}
if (purchasedLostShuttles != PurchasedLostShuttles)
if (purchasedLostShuttles && !PurchasedLostShuttles)
{
if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
{
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
}
else if (purchasedLostShuttles && TryPurchase(sender, shuttleRetrieveCost))
else if (TryPurchase(sender, shuttleRetrieveCost))
{
PurchasedLostShuttles = true;
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
}
else if (!purchasedItemRepairs)
{
PurchasedLostShuttles = false;
personalWallet.Refund(shuttleRetrieveCost);
}
}
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
@@ -1021,12 +1010,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);
}
@@ -1057,49 +1047,47 @@ namespace Barotrauma
if (GameMain.Server is null) { return; }
switch (transfer.Sender)
if (transfer.Sender.TryUnwrap(out var id))
{
case Some<ushort> { Value: var id }:
if (id != sender.CharacterID && !AllowedToManageWallets(sender)) { return; }
if (id != sender.CharacterID && !AllowedToManageWallets(sender)) { return; }
Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; }
Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; }
TransferMoney(wallet);
break;
case None<ushort> _:
if (!AllowedToManageWallets(sender))
TransferMoney(wallet);
}
else
{
if (!AllowedToManageWallets(sender))
{
if (transfer.Receiver.TryUnwrap(out var receiverId) && receiverId == sender.CharacterID)
{
if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID)
{
if (transfer.Amount > GameMain.Server.ServerSettings.MaximumMoneyTransferRequest) { return; }
GameMain.Server.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
GameServer.Log($"{sender.Name} started a vote to transfer {transfer.Amount} mk from the bank.", ServerLog.MessageType.Money);
}
return;
if (transfer.Amount > GameMain.Server.ServerSettings.MaximumMoneyTransferRequest) { return; }
GameMain.Server.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
GameServer.Log($"{sender.Name} started a vote to transfer {transfer.Amount} mk from the bank.", ServerLog.MessageType.Money);
}
return;
}
TransferMoney(Bank);
break;
TransferMoney(Bank);
}
void TransferMoney(Wallet from)
{
if (!from.TryDeduct(transfer.Amount)) { return; }
switch (transfer.Receiver)
if (transfer.Receiver.TryUnwrap(out var id))
{
case Some<ushort> { Value: var id }:
Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; }
Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; }
wallet.Give(transfer.Amount);
GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {wallet.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money);
break;
case None<ushort> _:
Bank.Give(transfer.Amount);
GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {Bank.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money);
break;
wallet.Give(transfer.Amount);
GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {wallet.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money);
}
else
{
Bank.Give(transfer.Amount);
GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {Bank.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money);
}
}
@@ -24,7 +24,11 @@ namespace Barotrauma
public DateTimeOffset Expiry;
}
private readonly Dictionary<Client, RateLimitInfo> rateLimits = new Dictionary<Client, RateLimitInfo>();
private readonly record struct AfflictionSubscriber(Client Subscriber, CharacterInfo Target, DateTimeOffset Expiry);
private readonly List<AfflictionSubscriber> afflictionSubscribers = new();
private readonly Dictionary<Client, RateLimitInfo> rateLimits = new();
public void ServerRead(IReadMessage inc, Client sender)
{
@@ -35,6 +39,9 @@ namespace Barotrauma
case NetworkHeader.ADD_EVERYTHING_TO_PENDING:
ProcessAddEverything(sender);
break;
case NetworkHeader.UNSUBSCRIBE_ME:
RemoveClientSubscription(sender);
break;
case NetworkHeader.REQUEST_AFFLICTIONS:
ProcessRequestedAfflictions(inc, sender);
break;
@@ -72,6 +79,17 @@ namespace Barotrauma
ServerSend(PendingHeals.ToNetCollection(), NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void RemoveClientSubscription(Client client)
{
foreach (AfflictionSubscriber sub in afflictionSubscribers.ToList())
{
if (sub.Subscriber == client || sub.Expiry < DateTimeOffset.Now)
{
afflictionSubscribers.Remove(sub);
}
}
}
private void ProcessNewRemoval(IReadMessage inc, Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
@@ -129,6 +147,14 @@ namespace Barotrauma
Afflictions = pendingAfflictions
};
if (foundInfo is not null)
{
RemoveClientSubscription(client);
// the client subscribes to the afflictions of the crew member for the next minute
afflictionSubscribers.Add(new AfflictionSubscriber(client, foundInfo, DateTimeOffset.Now.AddMinutes(1)));
}
ServerSend(writeCrewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable, client);
}
@@ -8,10 +8,12 @@ namespace Barotrauma.Items.Components
private readonly struct EventData : IEventData
{
public readonly bool Launch;
public readonly byte SpreadCounter;
public EventData(bool launch)
public EventData(bool launch, byte spreadCounter = 0)
{
Launch = launch;
SpreadCounter = spreadCounter;
}
}
@@ -32,6 +34,7 @@ namespace Barotrauma.Items.Components
msg.WriteSingle(launchPos.X);
msg.WriteSingle(launchPos.Y);
msg.WriteSingle(launchRot);
msg.WriteByte(eventData.SpreadCounter);
}
bool stuck = StickTarget != null && !item.Removed && !StickTargetRemoved();
@@ -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)
{
@@ -253,6 +253,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));
@@ -140,8 +140,7 @@ namespace Barotrauma.Networking
return Option<BannedPlayer>.Some(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
}
bannedPlayers.AddRange(doc.Root.Elements().Select(loadFromElement)
.OfType<Some<BannedPlayer>>().Select(o => o.Value));
bannedPlayers.AddRange(doc.Root.Elements().Select(loadFromElement).NotNone());
}
private void RemoveExpired()
@@ -1129,6 +1129,7 @@ namespace Barotrauma.Networking
{
//check if midround syncing is needed due to missed unique events
if (!midroundSyncingDone) { entityEventManager.InitClientMidRoundSync(c); }
MissionAction.NotifyMissionsUnlockedThisRound(c);
c.InGame = true;
}
}
@@ -2388,10 +2389,7 @@ namespace Barotrauma.Networking
List<WayPoint> spawnWaypoints = null;
List<WayPoint> mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSubs[n]).ToList();
if (Level.Loaded?.StartOutpost != null &&
Level.Loaded.Type == LevelData.LevelType.Outpost &&
(Level.Loaded.StartOutpost.Info.OutpostGenerationParams?.SpawnCrewInsideOutpost ?? false) &&
Level.Loaded.StartOutpost.GetConnectedSubs().Any(s => s.Info.Type == SubmarineType.Player))
if (Level.Loaded != null && Level.Loaded.ShouldSpawnCrewInsideOutpost())
{
spawnWaypoints = WayPoint.WayPointList.FindAll(wp =>
wp.SpawnType == SpawnType.Human &&
@@ -2466,7 +2464,6 @@ namespace Barotrauma.Networking
spawnedCharacter.Info.InventoryData = new XElement("inventory");
spawnedCharacter.Info.StartItemsGiven = true;
spawnedCharacter.SaveInventory();
// talents are only avilable for players in online sessions, but modders or someone else might want to have them loaded anyway
spawnedCharacter.LoadTalents();
}
}
@@ -3023,7 +3020,7 @@ namespace Barotrauma.Networking
client.WaitForNextRoundRespawn = null;
client.InGame = false;
if (client.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
if (client.AccountId.TryUnwrap<SteamId>(out var steamId)) { SteamManager.StopAuthSession(steamId); }
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(client));
if (previousPlayer == null)
@@ -3366,12 +3363,13 @@ namespace Barotrauma.Networking
if (checkActiveVote && Voting.ActiveVote != null)
{
#warning TODO: this is mostly the same as Voting.Update, deduplicate (if/when refactoring the Voting class?)
var inGameClients = GameMain.Server.ConnectedClients.Where(c => c.InGame);
if (inGameClients.Count() == 1)
if (inGameClients.Count() == 1 && inGameClients.First() == Voting.ActiveVote.VoteStarter)
{
Voting.ActiveVote.Finish(Voting, passed: true);
}
else
else if (inGameClients.Any())
{
var eligibleClients = inGameClients.Where(c => c != Voting.ActiveVote.VoteStarter);
int yes = eligibleClients.Count(c => c.GetVote<int>(Voting.ActiveVote.VoteType) == 2);
@@ -3441,12 +3439,11 @@ namespace Barotrauma.Networking
public void SwitchSubmarine()
{
if (!(Voting.ActiveVote is Voting.SubmarineVote subVote)) { return; }
if (Voting.ActiveVote is not Voting.SubmarineVote subVote) { return; }
SubmarineInfo targetSubmarine = subVote.Sub;
VoteType voteType = Voting.ActiveVote.VoteType;
Client starter = Voting.ActiveVote.VoteStarter;
int deliveryFee = 0;
switch (voteType)
{
@@ -3456,7 +3453,6 @@ namespace Barotrauma.Networking
GameMain.GameSession.PurchaseSubmarine(targetSubmarine, starter);
break;
case VoteType.SwitchSub:
deliveryFee = subVote.DeliveryFee;
break;
default:
return;
@@ -3464,7 +3460,7 @@ namespace Barotrauma.Networking
if (voteType != VoteType.PurchaseSub)
{
GameMain.GameSession.SwitchSubmarine(targetSubmarine, subVote.TransferItems, deliveryFee, starter);
GameMain.GameSession.SwitchSubmarine(targetSubmarine, subVote.TransferItems, starter);
}
Voting.StopSubmarineVote(true);
@@ -4030,8 +4026,7 @@ namespace Barotrauma.Networking
}
public void Quit()
{
{
if (started)
{
started = false;
@@ -4043,7 +4038,7 @@ namespace Barotrauma.Networking
ServerSettings.SaveSettings();
ModSender.Dispose();
ModSender?.Dispose();
if (ServerSettings.SaveServerLogs)
{
@@ -158,7 +158,7 @@ namespace Barotrauma
else if (client.Karma < 40.0f)
herpesStrength = 30.0f;
var existingAffliction = client.Character.CharacterHealth.GetAffliction<AfflictionSpaceHerpes>("spaceherpes");
var existingAffliction = client.Character.CharacterHealth.GetAffliction<AfflictionSpaceHerpes>(AfflictionPrefab.SpaceHerpesType);
if (existingAffliction == null && herpesStrength > 0.0f)
{
client.Character.CharacterHealth.ApplyAffliction(null, new Affliction(herpesAffliction, herpesStrength));
@@ -170,7 +170,7 @@ namespace Barotrauma
existingAffliction.Strength = herpesStrength;
if (herpesStrength <= 0.0f)
{
client.Character.CharacterHealth.ReduceAfflictionOnAllLimbs("invertcontrols".ToIdentifier(), 100.0f);
client.Character.CharacterHealth.ReduceAfflictionOnAllLimbs(AfflictionPrefab.InvertControlsType, 100.0f);
}
}
@@ -358,8 +358,8 @@ namespace Barotrauma
}
}
bool targetIsHusk = target.CharacterHealth?.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;
bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;
bool targetIsHusk = target.CharacterHealth?.GetAffliction<AfflictionHusk>(AfflictionPrefab.HuskInfectionType)?.State == AfflictionHusk.InfectionState.Active;
bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction<AfflictionHusk>(AfflictionPrefab.HuskInfectionType)?.State == AfflictionHusk.InfectionState.Active;
//huskified characters count as enemies to healthy characters and vice versa
if (targetIsHusk != attackerIsHusk) { isEnemy = true; }
@@ -614,7 +614,7 @@ namespace Barotrauma
if (amount < 0.0f)
{
float? herpesStrength = client.Character?.CharacterHealth.GetAfflictionStrength("spaceherpes");
float? herpesStrength = client.Character?.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.SpaceHerpesType);
var clientMemory = GetClientMemory(client);
clientMemory.KarmaDecreasesInPastMinute.RemoveAll(ta => ta.Time + 60.0f < Timing.TotalTime);
float aggregate = clientMemory.KarmaDecreasesInPastMinute.Select(ta => ta.Amount).DefaultIfEmpty().Aggregate((a, b) => a + b);
@@ -308,7 +308,7 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
PendingClient? pendingClient = pendingClients.Find(c => c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId);
PendingClient? pendingClient = pendingClients.Find(c => c.AccountInfo.AccountId.TryUnwrap<SteamId>(out var id) && id.Value == steamId);
DebugConsole.Log($"{steamId} validation: {status}, {(pendingClient != null)}");
if (pendingClient is null)
@@ -316,7 +316,7 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK) { return; }
if (connectedClients.Find(c
=> c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId)
=> c.AccountInfo.AccountId.TryUnwrap<SteamId>(out var id) && id.Value == steamId)
is LidgrenConnection connection)
{
Disconnect(connection, PeerDisconnectPacket.SteamAuthError(status));
@@ -390,7 +390,7 @@ namespace Barotrauma.Networking
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(lidgrenConn);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
if (conn.AccountInfo.AccountId.TryUnwrap<SteamId>(out var steamId)) { SteamManager.StopAuthSession(steamId); }
}
lidgrenConn.NetConnection.Disconnect(peerDisconnectPacket.ToLidgrenStringRepresentation());
@@ -71,7 +71,7 @@ namespace Barotrauma.Networking
protected List<NetworkConnection> connectedClients = null!;
protected List<PendingClient> pendingClients = null!;
protected ServerSettings serverSettings = null!;
protected Option<int> ownerKey = null!;
protected Option<int> ownerKey = Option.None;
protected NetworkConnection? OwnerConnection;
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc, ConnectionInitialization initializationStep)
@@ -295,7 +295,7 @@ namespace Barotrauma.Networking
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted && pendingClient.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId })
if (pendingClient.AuthSessionStarted && pendingClient.AccountInfo.AccountId.TryUnwrap<SteamId>(out var steamId))
{
Steam.SteamManager.StopAuthSession(steamId);
pendingClient.Connection.SetAccountInfo(AccountInfo.None);
@@ -239,7 +239,10 @@ namespace Barotrauma.Networking
foreach (Door door in shuttleDoors)
{
if (door.IsOpen) door.TrySetState(false, false, true);
if (door.IsOpen)
{
door.TrySetState(open: false, isNetworkMessage: false, sendNetworkMessage: true);
}
}
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
@@ -51,7 +51,7 @@ namespace Barotrauma.Networking
=> LastUpdateIdForFlag.Keys
.Where(k => IsFlagRequired(c, k))
.Aggregate(NetFlags.None, (f1, f2) => f1 | f2);
partial void InitProjSpecific()
{
LoadSettings();
@@ -176,7 +176,11 @@ namespace Barotrauma.Networking
netProperties[key].Read(incMsg);
if (!netProperties[key].PropEquals(prevValue, netProperties[key]))
{
GameServer.Log(GameServer.ClientLogName(c) + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage);
GameServer.Log(
NetworkMember.ClientLogName(c)
+ $" changed {netProperties[key].Name}"
+ $" to {netProperties[key].Value}",
ServerLog.MessageType.ServerMessage);
}
propertiesChanged = true;
}
@@ -330,6 +334,10 @@ namespace Barotrauma.Networking
{
LosMode = GameSettings.CurrentConfig.Graphics.LosMode;
}
if (string.IsNullOrEmpty(doc.Root.GetAttributeString("language", "")))
{
Language = ServerLanguageOptions.PickLanguage(GameSettings.CurrentConfig.Language);
}
AutoRestart = doc.Root.GetAttributeBool("autorestart", false);
@@ -28,13 +28,11 @@ namespace Barotrauma
public SubmarineInfo Sub;
public bool TransferItems;
public int DeliveryFee;
public SubmarineVote(Client starter, SubmarineInfo subInfo, bool transferItems, int deliveryFee, VoteType voteType)
public SubmarineVote(Client starter, SubmarineInfo subInfo, bool transferItems, VoteType voteType)
{
Sub = subInfo;
TransferItems = transferItems;
DeliveryFee = deliveryFee;
VoteType = voteType;
State = VoteState.Started;
VoteStarter = starter;
@@ -81,10 +79,10 @@ namespace Barotrauma
if (passed)
{
Wallet fromWallet = From == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : From.Character?.Wallet;
if (fromWallet.TryDeduct(TransferAmount))
if (fromWallet != null && fromWallet.TryDeduct(TransferAmount))
{
Wallet toWallet = To == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : To.Character?.Wallet;
toWallet.Give(TransferAmount);
toWallet?.Give(TransferAmount);
}
}
else
@@ -109,7 +107,6 @@ namespace Barotrauma
sender,
subInfo,
transferItems,
voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0,
voteType);
StartOrEnqueueVote(subVote);
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
@@ -206,12 +203,16 @@ namespace Barotrauma
// Do not take unanswered into account for total
int yes = eligibleClients.Count(c => c.GetVote<int>(ActiveVote.VoteType) == 2);
int no = eligibleClients.Count(c => c.GetVote<int>(ActiveVote.VoteType) == 1);
int total = Math.Max(yes + no, 1);
bool passed =
yes / (float)total >= GameMain.NetworkMember.ServerSettings.VoteRequiredRatio ||
inGameClients.Count() == 1;
int total = yes + no;
bool passed = false;
//total can be zero if the client who initiated the vote has left
if (total > 0)
{
passed =
yes / (float)total >= GameMain.NetworkMember.ServerSettings.VoteRequiredRatio ||
inGameClients.Count() == 1;
}
ActiveVote.Finish(this, passed);
}
}
@@ -436,7 +437,6 @@ namespace Barotrauma
var subVote = ActiveVote as SubmarineVote;
msg.WriteString(subVote.Sub.Name);
msg.WriteBoolean(subVote.TransferItems);
msg.WriteInt16((short)subVote.DeliveryFee);
break;
}
break;
@@ -209,20 +209,24 @@ namespace Barotrauma
public void RandomizeSettings()
{
if (GameMain.Server.ServerSettings.RandomizeSeed) LevelSeed = ToolBox.RandomSeed(8);
if (GameMain.Server.ServerSettings.RandomizeSeed) { LevelSeed = ToolBox.RandomSeed(8); }
if (GameMain.Server.ServerSettings.SubSelectionMode == SelectionMode.Random)
//don't touch any of these settings if a campaign is running!
if (GameMain.GameSession?.Campaign == null)
{
var nonShuttles = SubmarineInfo.SavedSubmarines.Where(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus) && c.IsPlayer).ToList();
SelectedSub = nonShuttles[Rand.Range(0, nonShuttles.Count)];
}
if (GameMain.Server.ServerSettings.ModeSelectionMode == SelectionMode.Random)
{
var allowedGameModes = Array.FindAll(GameModes, m => !m.IsSinglePlayer && m != GameModePreset.MultiPlayerCampaign);
SelectedModeIdentifier = allowedGameModes[Rand.Range(0, allowedGameModes.Length)].Identifier;
}
if (GameMain.Server.ServerSettings.SubSelectionMode == SelectionMode.Random)
{
var nonShuttles = SubmarineInfo.SavedSubmarines.Where(c => !c.HasTag(SubmarineTag.Shuttle) && !c.HasTag(SubmarineTag.HideInMenus) && c.IsPlayer).ToList();
SelectedSub = nonShuttles[Rand.Range(0, nonShuttles.Count)];
}
if (GameMain.Server.ServerSettings.ModeSelectionMode == SelectionMode.Random)
{
var allowedGameModes = Array.FindAll(GameModes, m => !m.IsSinglePlayer && m != GameModePreset.MultiPlayerCampaign);
SelectedModeIdentifier = allowedGameModes[Rand.Range(0, allowedGameModes.Length)].Identifier;
}
GameMain.Server.ServerSettings.SelectNonHiddenSubmarine();
GameMain.Server.ServerSettings.SelectNonHiddenSubmarine();
}
}
}
}
@@ -72,6 +72,7 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.SetKey("gamestarted", server.GameStarted.ToString());
Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier.Value);
Steamworks.SteamServer.SetKey("playstyle", server.ServerSettings.PlayStyle.ToString());
Steamworks.SteamServer.SetKey("language", server.ServerSettings.Language.ToString());
Steamworks.SteamServer.DedicatedServer = true;