v1.4.4.1 (Blood in the Water Update)

This commit is contained in:
Regalis11
2024-04-24 18:09:05 +03:00
parent 89b91d1c3e
commit ff1b8951a7
397 changed files with 15250 additions and 6479 deletions
@@ -16,7 +16,7 @@ namespace Barotrauma
public void ApplyDeathEffects()
{
RespawnManager.ReduceCharacterSkills(this);
RespawnManager.ReduceCharacterSkillsOnDeath(this);
RemoveSavedStatValuesOnDeath();
CauseOfDeath = null;
}
@@ -68,8 +68,7 @@ namespace Barotrauma
msg.WriteColorR8G8B8(Head.SkinColor);
msg.WriteColorR8G8B8(Head.HairColor);
msg.WriteColorR8G8B8(Head.FacialHairColor);
msg.WriteString(ragdollFileName);
msg.WriteIdentifier(HumanPrefabIds.NpcIdentifier);
msg.WriteIdentifier(MinReputationToHire.factionId);
if (!MinReputationToHire.factionId.IsEmpty)
@@ -80,11 +79,13 @@ namespace Barotrauma
{
msg.WriteUInt32(Job.Prefab.UintIdentifier);
msg.WriteByte((byte)Job.Variant);
var skills = Job.Prefab.Skills.OrderBy(s => s.Identifier);
var skills = Job.GetSkills().OrderBy(s => s.Identifier);
msg.WriteByte((byte)skills.Count());
foreach (SkillPrefab skillPrefab in skills)
foreach (var skill in skills)
{
msg.WriteSingle(Job.GetSkill(skillPrefab.Identifier)?.Level ?? 0.0f);
msg.WriteIdentifier(skill.Identifier);
msg.WriteSingle(skill.Level);
}
}
else
@@ -75,7 +75,7 @@ namespace Barotrauma
NetConfig.HighPrioCharacterPositionUpdateInterval,
priority);
if (IsDead)
if (IsDead && !AnimController.IsDraggedWithRope)
{
interval = Math.Max(interval * 2, 0.1f);
}
@@ -590,6 +590,31 @@ namespace Barotrauma
}
}
break;
case LatchedOntoTargetEventData latchedOntoTargetEventData:
msg.WriteBoolean(latchedOntoTargetEventData.IsLatched);
if (latchedOntoTargetEventData.IsLatched)
{
msg.WriteSingle(SimPosition.X);
msg.WriteSingle(SimPosition.Y);
msg.WriteSingle(latchedOntoTargetEventData.AttachSurfaceNormal.X);
msg.WriteSingle(latchedOntoTargetEventData.AttachSurfaceNormal.Y);
msg.WriteSingle(latchedOntoTargetEventData.AttachPos.X);
msg.WriteSingle(latchedOntoTargetEventData.AttachPos.Y);
msg.WriteInt32(latchedOntoTargetEventData.TargetLevelWallIndex);
if (latchedOntoTargetEventData.TargetStructureID != NullEntityID)
{
msg.WriteUInt16(latchedOntoTargetEventData.TargetStructureID);
}
else if (latchedOntoTargetEventData.TargetCharacterID != NullEntityID)
{
msg.WriteUInt16(latchedOntoTargetEventData.TargetCharacterID);
}
else
{
msg.WriteUInt16(NullEntityID);
}
}
break;
default:
throw new Exception($"Malformed character event: did not expect {eventData.GetType().Name}");
}
@@ -1185,7 +1185,9 @@ namespace Barotrauma
if (GameMain.Server == null) { return; }
GameMain.Server.ServerSettings.SetPassword(args.Length > 0 ? args[0] : "");
NewMessage(client.Name + " " + (GameMain.Server.ServerSettings.HasPassword ? " changed the server password to \"" + args[0] + "\"." : " removed password protection from the server."));
GameMain.Server.SendConsoleMessage(GameMain.Server.ServerSettings.HasPassword ? "Changed the server password." : "Removed password protection from the server.", client);
GameMain.Server.SendChatMessage(
TextManager.GetWithVariable(GameMain.Server.ServerSettings.HasPassword ? "PasswordChangedByClient" : "PasswordRemovedByClient", "[clientname]", client.Name).Value,
ChatMessageType.Server);
});
commands.Add(new Command("setmaxplayers|maxplayers", "setmaxplayers [max players]: Sets the maximum player count of the server that's being hosted.", (string[] args) =>
@@ -1278,12 +1280,11 @@ namespace Barotrauma
commands.Add(new Command("servername", "servername [name]: Change the name of the server.", (string[] args) =>
{
GameMain.Server.ServerName = string.Join(" ", args);
GameMain.NetLobbyScreen.ChangeServerName(string.Join(" ", args));
}));
commands.Add(new Command("servermsg", "servermsg [message]: Change the message displayed in the server lobby.", (string[] args) =>
{
GameMain.NetLobbyScreen.ChangeServerMessage(string.Join(" ", args));
GameMain.Server.ServerSettings.ServerMessageText = string.Join(" ", args);
}));
commands.Add(new Command("seed|levelseed", "seed/levelseed: Changes the level seed for the next round.", (string[] args) =>
@@ -48,6 +48,7 @@ namespace Barotrauma
spawnInfo[target].OriginalInventoryID,
spawnInfo[target].OriginalItemContainerIndex,
spawnInfo[target].OriginalSlotIndex);
msg.WriteUInt16(target.ParentTarget?.Item?.ID ?? Entity.NullEntityID);
}
msg.WriteByte((byte)spawnInfo[target].ExecutedEffectIndices.Count);
@@ -470,6 +470,11 @@ namespace Barotrauma
return characterData.Find(cd => cd.MatchesClient(client));
}
public CharacterCampaignData GetCharacterData(CharacterInfo characterInfo)
{
return characterData.Find(cd => cd.CharacterInfo == characterInfo);
}
public CharacterCampaignData SetClientCharacterData(Client client)
{
characterData.RemoveAll(cd => cd.MatchesClient(client));
@@ -970,7 +975,7 @@ namespace Barotrauma
{
int desiredQuantity = purchasedItem.Quantity;
if (prevPurchasedItems.TryGetValue(storeId, out var alreadyPurchasedList) &&
alreadyPurchasedList.FirstOrDefault(p => p.ItemPrefab == purchasedItem.ItemPrefab) is { } alreadyPurchased)
alreadyPurchasedList.FirstOrDefault(p => p.ItemPrefab == purchasedItem.ItemPrefab && p.DeliverImmediately == purchasedItem.DeliverImmediately) is { } alreadyPurchased)
{
desiredQuantity -= alreadyPurchased.Quantity;
}
@@ -1198,14 +1203,13 @@ namespace Barotrauma
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
Location location = map?.CurrentLocation;
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
CharacterInfo firedCharacter = null;
if (location != null && AllowedToManageCampaign(sender, ClientPermissions.ManageHires))
{
if (fireCharacter)
{
firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
if (firedCharacter != null && (firedCharacter.Character?.IsBot ?? true))
{
CrewManager.FireCharacter(firedCharacter);
@@ -1221,7 +1225,7 @@ namespace Barotrauma
CharacterInfo characterInfo = null;
if (existingCrewMember && CrewManager != null)
{
characterInfo = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
characterInfo = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
}
else if(!existingCrewMember && location.HireManager != null)
{
@@ -1251,10 +1255,7 @@ namespace Barotrauma
{
foreach (CharacterInfo hireInfo in location.HireManager.PendingHires)
{
if (TryHireCharacter(location, hireInfo, sender.Character, sender))
{
hiredCharacters.Add(hireInfo);
}
TryHireCharacter(location, hireInfo, client: sender);
}
}
@@ -1271,7 +1272,7 @@ namespace Barotrauma
}
pendingHireInfos.Add(match);
if (pendingHireInfos.Count + CrewManager.CharacterInfos.Count() >= CrewManager.MaxCrewSize)
if (pendingHireInfos.Count + CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
{
break;
}
@@ -1281,7 +1282,7 @@ namespace Barotrauma
location.HireManager.AvailableCharacters.ForEachMod(info =>
{
if(!location.HireManager.PendingHires.Contains(info))
if (!location.HireManager.PendingHires.Contains(info))
{
location.HireManager.RenameCharacter(info, info.OriginalName);
}
@@ -1292,11 +1293,11 @@ namespace Barotrauma
// bounce back
if (renameCharacter && existingCrewMember)
{
SendCrewState(hiredCharacters, (renamedIdentifier, newName), firedCharacter);
SendCrewState((renamedIdentifier, newName), firedCharacter);
}
else
{
SendCrewState(hiredCharacters, default, firedCharacter);
SendCrewState(firedCharacter: firedCharacter);
}
}
@@ -1310,7 +1311,7 @@ namespace Barotrauma
/// the client and the server when there's only one person on the server but when a second person joins both of
/// their available hires are different from the server.
/// </remarks>
public void SendCrewState(List<CharacterInfo> hiredCharacters, (int id, string newName) renamedCrewMember, CharacterInfo firedCharacter)
public void SendCrewState((int id, string newName) renamedCrewMember = default, CharacterInfo firedCharacter = null)
{
List<CharacterInfo> availableHires = new List<CharacterInfo>();
List<CharacterInfo> pendingHires = new List<CharacterInfo>();
@@ -1332,21 +1333,19 @@ namespace Barotrauma
hire.ServerWrite(msg);
msg.WriteInt32(hire.Salary);
}
msg.WriteUInt16((ushort)pendingHires.Count);
foreach (CharacterInfo pendingHire in pendingHires)
{
msg.WriteInt32(pendingHire.GetIdentifierUsingOriginalName());
}
msg.WriteUInt16((ushort)(hiredCharacters?.Count ?? 0));
if(hiredCharacters != null)
var hiredCharacters = CrewManager.GetCharacterInfos().Where(ci => ci.IsNewHire);
msg.WriteUInt16((ushort)hiredCharacters.Count());
foreach (CharacterInfo info in hiredCharacters)
{
foreach (CharacterInfo info in hiredCharacters)
{
info.ServerWrite(msg);
msg.WriteInt32(info.Salary);
}
info.ServerWrite(msg);
msg.WriteInt32(info.Salary);
}
bool validRenaming = renamedCrewMember.id > -1 && !string.IsNullOrEmpty(renamedCrewMember.newName);
@@ -27,6 +27,9 @@ namespace Barotrauma.Items.Components
//opening a partially stuck door makes it less stuck
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
ActionType actionType = open ? ActionType.OnOpen : ActionType.OnClose;
item.ApplyStatusEffects(actionType, deltaTime: 1.0f);
if (sendNetworkMessage)
{
item.CreateServerEvent(this, new EventData(forcedOpen));
@@ -11,20 +11,20 @@ namespace Barotrauma.Items.Components
if (!Snapped)
{
msg.WriteUInt16(target?.ID ?? Entity.NullEntityID);
if (source is Entity entity && !entity.Removed)
switch (source)
{
msg.WriteUInt16(entity?.ID ?? Entity.NullEntityID);
msg.WriteByte((byte)0);
}
else if (source is Limb limb && limb.character != null && !limb.character.Removed)
{
msg.WriteUInt16(limb.character?.ID ?? Entity.NullEntityID);
msg.WriteByte((byte)limb.character.AnimController.Limbs.IndexOf(limb));
}
else
{
msg.WriteUInt16(Entity.NullEntityID);
msg.WriteByte((byte)0);
case Entity { Removed: false } entity:
msg.WriteUInt16(entity.ID);
msg.WriteByte((byte)0);
break;
case Limb { character.Removed: false } limb:
msg.WriteUInt16(limb.character.ID);
msg.WriteByte((byte)limb.character.AnimController.Limbs.IndexOf(limb));
break;
default:
msg.WriteUInt16(Entity.NullEntityID);
msg.WriteByte((byte)0);
break;
}
}
}
@@ -100,7 +100,7 @@ namespace Barotrauma.Items.Components
return (msg, deliveryMethod);
}
public void CreateServerEvent(INetSerializableStruct data)
public void CreateServerEvent(INetSerializableStruct data)
=> item.CreateServerEvent(this, new CircuitBoxEventData(data));
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData? extraData = null)
@@ -120,7 +120,7 @@ namespace Barotrauma.Items.Components
case CircuitBoxOpcode.AddComponent:
{
var data = INetSerializableStruct.Read<CircuitBoxAddComponentEvent>(msg);
if (!item.CanClientAccess(c)) { break; }
if (!CanAccessAndUnlocked(c)) { break; }
var prefab = ItemPrefab.Prefabs.Find(p => p.UintIdentifier == data.PrefabIdentifier);
if (prefab is null)
@@ -158,14 +158,14 @@ namespace Barotrauma.Items.Components
var data = INetSerializableStruct.Read<CircuitBoxMoveComponentEvent>(msg);
if (!item.CanClientAccess(c)) { break; }
MoveNodesInternal(data.TargetIDs, data.IOs, data.MoveAmount);
MoveNodesInternal(data.TargetIDs, data.IOs, data.LabelIDs, data.MoveAmount);
CreateServerEvent(data);
break;
}
case CircuitBoxOpcode.DeleteComponent:
{
var data = INetSerializableStruct.Read<CircuitBoxRemoveComponentEvent>(msg);
if (!data.TargetIDs.Any() || !item.CanClientAccess(c)) { break; }
if (!data.TargetIDs.Any() || !CanAccessAndUnlocked(c)) { break; }
CreateRefundItemsForUsedResources(data.TargetIDs, c.Character);
GameServer.Log($"{NetworkMember.ClientLogName(c)} removed {GetLogComponentName(data.TargetIDs)} from circuit box.", ServerLog.MessageType.Wiring);
@@ -180,6 +180,7 @@ namespace Barotrauma.Items.Components
SelectComponentsInternal(data.TargetIDs, c.CharacterID, data.Overwrite);
SelectInputOutputInternal(data.IOs, c.CharacterID, data.Overwrite);
SelectLabelsInternal(data.LabelIDs, c.CharacterID, data.Overwrite);
BroadcastSelectionStatus();
break;
}
@@ -195,7 +196,7 @@ namespace Barotrauma.Items.Components
case CircuitBoxOpcode.AddWire:
{
var data = INetSerializableStruct.Read<CircuitBoxClientAddWireEvent>(msg);
if (!item.CanClientAccess(c)) { break; }
if (!CanAccessAndUnlocked(c)) { break; }
var prefab = ItemPrefab.Prefabs.Find(p => p.UintIdentifier == data.SelectedWirePrefabIdentifier);
if (prefab is null)
@@ -229,13 +230,56 @@ namespace Barotrauma.Items.Components
case CircuitBoxOpcode.RemoveWire:
{
var data = INetSerializableStruct.Read<CircuitBoxRemoveWireEvent>(msg);
if (!data.TargetIDs.Any() || !item.CanClientAccess(c)) { break; }
if (!data.TargetIDs.Any() || !CanAccessAndUnlocked(c)) { break; }
GameServer.Log($"{NetworkMember.ClientLogName(c)} removed {GetLogWireName(data.TargetIDs)} from circuit box.", ServerLog.MessageType.Wiring);
RemoveWireInternal(data.TargetIDs);
CreateServerEvent(data);
break;
}
case CircuitBoxOpcode.RenameLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxRenameLabelEvent>(msg);
if (!CanAccessAndUnlocked(c)) { break; }
RenameLabelInternal(data.LabelId, data.Color, data.NewHeader, data.NewBody);
CreateServerEvent(data);
break;
}
case CircuitBoxOpcode.AddLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxAddLabelEvent>(msg);
if (!CanAccessAndUnlocked(c)) { break; }
ushort id = ICircuitBoxIdentifiable.FindFreeID(Labels);
if (id is ICircuitBoxIdentifiable.NullComponentID)
{
ThrowError("Unable to add label because there are no available IDs left.", c);
return;
}
AddLabelInternal(id, data.Color, data.Position, data.Header, data.Body);
CreateServerEvent(new CircuitBoxServerAddLabelEvent(id, data.Position, new Vector2(256), data.Color, data.Header, data.Body));
break;
}
case CircuitBoxOpcode.RemoveLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxRemoveLabelEvent>(msg);
if (!CanAccessAndUnlocked(c)) { break; }
RemoveLabelInternal(data.TargetIDs);
CreateServerEvent(data);
break;
}
case CircuitBoxOpcode.ResizeLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxResizeLabelEvent>(msg);
if (!CanAccessAndUnlocked(c)) { break; }
ResizeLabelInternal(data.ID, data.Position, data.Size);
CreateServerEvent(data with { Size = Vector2.Max(data.Size, CircuitBoxLabelNode.MinSize) });
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(header), header, "This opcode cannot be handled using entity events");
}
@@ -253,6 +297,8 @@ namespace Barotrauma.Items.Components
return wire.BackingWire.TryUnwrap(out var backingWire) ? backingWire.Name : "a wire";
}
bool CanAccessAndUnlocked(Client client) => item.CanClientAccess(client) && !Locked;
}
/// <summary>
@@ -280,6 +326,7 @@ namespace Barotrauma.Items.Components
CircuitBoxInitializeStateFromServerEvent data = new(
Components: Components.Select(EventFromComponent).ToImmutableArray(),
Wires: Wires.Select(EventFromWire).ToImmutableArray(),
Labels: Labels.Select(EventFromLabel).ToImmutableArray(),
InputPos: inputPos,
OutputPos: outputPos);
@@ -297,6 +344,9 @@ namespace Barotrauma.Items.Components
var request = new CircuitBoxClientAddWireEvent(wire.Color, from, to, wire.UsedItemPrefab.UintIdentifier);
return new CircuitBoxServerCreateWireEvent(request, wire.ID, backingWire);
}
static CircuitBoxServerAddLabelEvent EventFromLabel(CircuitBoxLabelNode label)
=> new(label.ID, label.Position, label.Size, label.Color, label.HeaderText, label.BodyText);
}
// we don't care about updating the view on server
@@ -314,8 +364,9 @@ namespace Barotrauma.Items.Components
var nodes = Components.Select(static c => new CircuitBoxIdSelectionPair(c.ID, c.IsSelected ? Option.Some(c.SelectedBy) : Option.None)).ToImmutableArray();
var wires = Wires.Select(static w => new CircuitBoxIdSelectionPair(w.ID, w.IsSelected ? Option.Some(w.SelectedBy) : Option.None)).ToImmutableArray();
var ios = InputOutputNodes.Select(static n => new CircuitBoxTypeSelectionPair(n.NodeType, n.IsSelected ? Option.Some(n.SelectedBy) : Option.None)).ToImmutableArray();
var labels = Labels.Select(static n => new CircuitBoxIdSelectionPair(n.ID, n.IsSelected ? Option.Some(n.SelectedBy) : Option.None)).ToImmutableArray();
CreateServerEvent(new CircuitBoxServerUpdateSelection(nodes, wires, ios));
CreateServerEvent(new CircuitBoxServerUpdateSelection(nodes, wires, ios, labels));
}
}
}
@@ -13,8 +13,6 @@ namespace Barotrauma
public void ServerEventRead(IReadMessage msg, Client c)
{
List<Item> prevItems = new List<Item>(AllItems.Distinct());
if (!receivedItemIds.TryGetValue(c, out List<ushort>[] receivedItemIdsFromClient))
{
receivedItemIdsFromClient = new List<ushort>[capacity];
@@ -60,8 +58,21 @@ namespace Barotrauma
return;
}
List<Inventory> prevItemInventories = new List<Inventory>() { this };
//we need to check which of the items the client can access at this point, before we start shuffling things around
//otherwise if you're e.g. holding an item to access a cabinet, and picking up an item from the cabinet unequips the item you're holding,
//you would fail to pick up the item because it gets unequipped before checking whether you can access the cabinet.
Dictionary<Item, bool> canAccessItem = new Dictionary<Item, bool>();
for (int i = 0; i < capacity; i++)
{
foreach (ushort id in receivedItemIdsFromClient[i])
{
if (Entity.FindEntityByID(id) is not Item item) { continue; }
canAccessItem[item] = item.CanClientAccess(c);
}
}
List<Item> prevItems = new List<Item>(AllItems.Distinct());
List<Inventory> prevItemInventories = new List<Inventory>() { this };
for (int i = 0; i < capacity; i++)
{
foreach (Item item in slots[i].Items.ToList())
@@ -119,7 +130,7 @@ namespace Barotrauma
var holdable = item.GetComponent<Holdable>();
if (holdable != null && !holdable.CanBeDeattached()) { continue; }
if (!prevItems.Contains(item) && !item.CanClientAccess(c) &&
if (!prevItems.Contains(item) && !canAccessItem[item] &&
(c.Character == null || item.PreviousParentInventory == null || !c.Character.CanAccessInventory(item.PreviousParentInventory)))
{
#if DEBUG || UNSTABLE
@@ -1,10 +1,22 @@
using System;
using Barotrauma.Networking;
using Barotrauma.Networking;
using System;
namespace Barotrauma
{
partial class Submarine
{
public readonly struct SetLayerEnabledEventData : NetEntityEvent.IData
{
public readonly Identifier Layer;
public readonly bool Enabled;
public SetLayerEnabledEventData(Identifier layer, bool enabled)
{
Layer = layer;
Enabled = enabled;
}
}
public void ServerWritePosition(ReadWriteMessage tempBuffer, Client c)
{
subBody.Body.ServerWrite(tempBuffer);
@@ -12,7 +24,15 @@ namespace Barotrauma
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
throw new Exception($"Error while writing a network event for the submarine \"{Info.Name} ({ID})\". Submarines are not even supposed to send events!");
if (extraData is SetLayerEnabledEventData setLayerEnabledEventData)
{
msg.WriteIdentifier(setLayerEnabledEventData.Layer);
msg.WriteBoolean(setLayerEnabledEventData.Enabled);
}
else
{
throw new Exception($"Error while writing a network event for the submarine \"{Info.Name} ({ID})\". Unrecognized event data: {extraData?.GetType().Name ?? "null"}");
}
}
}
}
@@ -349,7 +349,7 @@ namespace Barotrauma.Networking
{
UInt32 id = incMsg.ReadUInt32();
BannedPlayer? bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
if (bannedPlayer != null && c.HasPermission(ClientPermissions.Unban))
{
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.AddressOrAccountId + ")", ServerLog.MessageType.ConsoleUsage);
RemoveBan(bannedPlayer);
@@ -282,15 +282,19 @@ namespace Barotrauma.Networking
SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient);
}
SendChatMessage($"ServerMessage.JoinedServer~[client]={ClientLogName(newClient)}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined);
SendChatMessage($"ServerMessage.JoinedServer~[client]={ClientLogName(newClient)}", ChatMessageType.Server, changeType: PlayerConnectionChangeType.Joined);
ServerSettings.ServerDetailsChanged = true;
if (previousPlayer != null && previousPlayer.Name != newClient.Name)
{
string prevNameSanitized = previousPlayer.Name.Replace("‖", "");
SendChatMessage($"ServerMessage.PreviousClientName~[client]={ClientLogName(newClient)}~[previousname]={prevNameSanitized}", ChatMessageType.Server, null);
SendChatMessage($"ServerMessage.PreviousClientName~[client]={ClientLogName(newClient)}~[previousname]={prevNameSanitized}", ChatMessageType.Server);
previousPlayer.Name = newClient.Name;
}
if (!ServerSettings.ServerMessageText.IsNullOrEmpty())
{
SendDirectChatMessage((TextManager.Get("servermotd") + '\n' + ServerSettings.ServerMessageText).Value, newClient, ChatMessageType.Server);
}
var savedPermissions = ServerSettings.ClientPermissions.Find(scp =>
scp.AddressOrAccountId.TryGet(out AccountId accountId)
@@ -434,12 +438,12 @@ namespace Barotrauma.Networking
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
}
else if (subAtLevelEnd && !(GameMain.GameSession?.GameMode is CampaignMode))
else if (subAtLevelEnd && GameMain.GameSession?.GameMode is not CampaignMode)
{
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
}
else if (isCrewDead && RespawnManager == null)
else if (isCrewDead && (RespawnManager == null || !RespawnManager.CanRespawnAgain))
{
#if !DEBUG
if (endRoundTimer <= 0.0f)
@@ -1136,6 +1140,10 @@ namespace Barotrauma.Networking
//check if midround syncing is needed due to missed unique events
if (!midroundSyncingDone) { entityEventManager.InitClientMidRoundSync(c); }
MissionAction.NotifyMissionsUnlockedThisRound(c);
if (GameMain.GameSession.Campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.SendCrewState();
}
c.InGame = true;
}
}
@@ -2322,7 +2330,7 @@ namespace Barotrauma.Networking
if (campaign != null)
{
campaign.CargoManager.CreatePurchasedItems();
campaign.SendCrewState(null, default, null);
campaign.SendCrewState();
}
Level.Loaded?.SpawnNPCs();
@@ -2792,8 +2800,6 @@ namespace Barotrauma.Networking
logMsg = message.TextWithSender;
}
Log(logMsg, ServerLog.MessageType.Chat);
base.AddChatMessage(message);
}
private bool ReadClientNameChange(Client c, IReadMessage inc)
@@ -78,6 +78,11 @@ namespace Barotrauma.Networking
public bool IsProcessed;
/// <summary>
/// Does the client need to be controlling a character for the server to consider the event valid?
/// </summary>
public bool RequireCharacter = true;
public BufferedEvent(Client sender, Character senderCharacter, UInt16 characterStateID, IClientSerializable targetEntity, ReadWriteMessage data)
{
this.Sender = sender;
@@ -157,15 +162,19 @@ namespace Barotrauma.Networking
{
if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
{
bufferedEvent.IsProcessed = true;
continue;
if (bufferedEvent.RequireCharacter)
{
bufferedEvent.IsProcessed = true;
continue;
}
}
//delay reading the events until the inputs for the corresponding frame have been processed
//UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
//atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
if (!bufferedEvent.Character.IsIncapacitated &&
if (bufferedEvent.Character != null &&
!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}");
@@ -503,7 +512,12 @@ namespace Barotrauma.Networking
byte[] temp = msg.ReadBytes(msgLength - 2);
buffer.WriteBytes(temp, 0, msgLength - 2);
buffer.BitPosition = 0;
BufferEvent(new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer));
BufferEvent(
new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer)
{
//hull updates can be sent without a character to allow editing water and fire in spectator mode
RequireCharacter = entity is not Hull
});
sender.LastSentEntityEventID++;
}
@@ -272,7 +272,8 @@ namespace Barotrauma.Networking
ServerName = GameMain.Server.ServerName,
ContentPackages = contentPackages
.Select(contentPackage => new ServerContentPackage(contentPackage, timeNow))
.ToImmutableArray()
.ToImmutableArray(),
AllowModDownloads = serverSettings.AllowModDownloads
};
break;
@@ -323,8 +323,8 @@ namespace Barotrauma.Networking
var clients = GetClientsToRespawn().ToList();
foreach (Client c in clients)
{
//get rid of the existing character
c.Character?.DespawnNow();
// Get rid of the existing character
if (c.Character is Character character) { character.DespawnNow(); }
c.WaitForNextRoundRespawn = null;
@@ -369,12 +369,9 @@ namespace Barotrauma.Networking
{
divingSuitPrefab = ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuitdeep"));
}
if (divingSuitPrefab == null)
{
divingSuitPrefab =
divingSuitPrefab ??=
ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuit")) ??
ItemPrefab.Find(null, "divingsuit".ToIdentifier());
}
ItemPrefab oxyPrefab = ItemPrefab.Find(null, "oxygentank".ToIdentifier());
ItemPrefab scooterPrefab = ItemPrefab.Find(null, "underwaterscooter".ToIdentifier());
ItemPrefab batteryPrefab = ItemPrefab.Find(null, "batterycell".ToIdentifier());
@@ -387,6 +384,7 @@ namespace Barotrauma.Networking
characterInfos[i].ClearCurrentOrders();
CharacterCampaignData characterCampaignData = null;
bool forceSpawnInMainSub = false;
if (!bot)
{
@@ -398,16 +396,16 @@ namespace Barotrauma.Networking
clients[i].PendingName = null;
}
var matchingData = campaign?.GetClientCharacterData(clients[i]);
if (matchingData != null)
characterCampaignData = campaign?.GetClientCharacterData(clients[i]);
if (characterCampaignData != null)
{
if (!matchingData.HasSpawned)
if (!characterCampaignData.HasSpawned)
{
forceSpawnInMainSub = true;
}
else
{
ReduceCharacterSkills(characterInfos[i]);
ReduceCharacterSkillsOnDeath(characterInfos[i]);
characterInfos[i].RemoveSavedStatValuesOnDeath();
characterInfos[i].CauseOfDeath = null;
}
@@ -415,6 +413,7 @@ namespace Barotrauma.Networking
}
var character = Character.Create(characterInfos[i], (forceSpawnInMainSub ? mainSubSpawnPoints[i] : shuttleSpawnPoints[i]).WorldPosition, characterInfos[i].Name, isRemotePlayer: !bot, hasAi: bot);
characterCampaignData?.ApplyWalletData(character);
character.TeamID = CharacterTeamType.Team1;
character.LoadTalents();
@@ -505,11 +504,10 @@ namespace Barotrauma.Networking
}
var characterData = campaign?.GetClientCharacterData(clients[i]);
// NOTE: This was where Reaper's tax got applied
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned)
{
//we need to reapply the previous respawn penalty affliction or successive deaths won't make it stack
characterData.ApplyHealthData(character, (AfflictionPrefab ap) => ap == GetRespawnPenaltyAfflictionPrefab());
GiveRespawnPenaltyAffliction(character);
ReduceCharacterSkillsOnDeath(characterInfos[i], applyExtraSkillLoss: true);
}
if (characterData == null || characterData.HasSpawned)
{
@@ -541,14 +539,37 @@ namespace Barotrauma.Networking
}
}
public static void ReduceCharacterSkills(CharacterInfo characterInfo)
/// <summary>
/// Reduce any skill gains the character may have made over the job's default
/// skill levels by percentages defined in server settings. There are two
/// reductions, a base one that always applies, and an extra loss that only
/// applies when the player chooses to respawn ASAP rather than wait.
/// </summary>
public static void ReduceCharacterSkillsOnDeath(CharacterInfo characterInfo, bool applyExtraSkillLoss = false)
{
if (characterInfo?.Job == null) { return; }
float resistanceMultiplier;
float skillLossPercentage;
if (applyExtraSkillLoss)
{
DebugConsole.Log($"Calculating extra skill loss on respawn for {characterInfo.Name}:");
resistanceMultiplier = characterInfo.LastResistanceMultiplierSkillLossRespawn;
skillLossPercentage = SkillLossPercentageOnImmediateRespawn;
}
else
{
DebugConsole.Log($"Calculating base skill loss on death for {characterInfo.Name}:");
resistanceMultiplier = characterInfo.LastResistanceMultiplierSkillLossDeath;
skillLossPercentage = SkillLossPercentageOnDeath;
}
skillLossPercentage *= resistanceMultiplier;
foreach (Skill skill in characterInfo.Job.GetSkills())
{
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier);
if (skillPrefab == null || skill.Level < skillPrefab.LevelRange.End) { continue; }
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, SkillLossPercentageOnDeath / 100.0f);
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, skillLossPercentage / 100.0f);
}
}
@@ -95,15 +95,6 @@ namespace Barotrauma.Networking
{
NetFlags requiredFlags = GetRequiredFlags(c);
outMsg.WriteByte((byte)requiredFlags);
if (requiredFlags.HasFlag(NetFlags.Name))
{
outMsg.WriteString(ServerName);
}
if (requiredFlags.HasFlag(NetFlags.Message))
{
outMsg.WriteString(ServerMessageText);
}
outMsg.WriteByte((byte)PlayStyle);
outMsg.WriteByte((byte)MaxPlayers);
outMsg.WriteBoolean(HasPassword);
@@ -122,8 +113,7 @@ namespace Barotrauma.Networking
WriteHiddenSubs(outMsg);
}
if (c.HasPermission(Networking.ClientPermissions.ManageSettings)
&& NetIdUtils.IdMoreRecent(
if (NetIdUtils.IdMoreRecent(
newID: LastUpdateIdForFlag[NetFlags.Properties],
oldID: c.LastRecvServerSettingsUpdate))
{
@@ -147,20 +137,6 @@ namespace Barotrauma.Networking
bool changed = false;
if (flags.HasFlag(NetFlags.Name))
{
string serverName = incMsg.ReadString();
if (ServerName != serverName) { changed = true; }
ServerName = serverName;
}
if (flags.HasFlag(NetFlags.Message))
{
string serverMessageText = incMsg.ReadString();
if (ServerMessageText != serverMessageText) { changed = true; }
ServerMessageText = serverMessageText;
}
if (flags.HasFlag(NetFlags.Properties))
{
bool propertiesChanged = ReadExtraCargo(incMsg);
@@ -217,42 +193,9 @@ namespace Barotrauma.Networking
int andBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
GameMain.NetLobbyScreen.MissionType = (MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
bool changedTraitorProbability = incMsg.ReadBoolean();
float traitorProbability = incMsg.ReadSingle();
if (changedTraitorProbability)
{
TraitorProbability = traitorProbability;
}
//the byte indicates the direction we're changing the value, subtract one to get negative values from a byte
TraitorDangerLevel = TraitorDangerLevel + incMsg.ReadByte() - 1;
int botCount = BotCount + incMsg.ReadByte() - 1;
if (botCount < 0) { botCount = MaxBotCount; }
if (botCount > MaxBotCount) { botCount = 0; }
BotCount = botCount;
int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
if (botSpawnMode < 0) { botSpawnMode = 1; }
if (botSpawnMode > 1) { botSpawnMode = 0; }
BotSpawnMode = (BotSpawnMode)botSpawnMode;
float levelDifficulty = incMsg.ReadSingle();
if (levelDifficulty >= 0.0f) { SelectedLevelDifficulty = levelDifficulty; }
bool changedUseRespawnShuttle = incMsg.ReadBoolean();
bool useRespawnShuttle = incMsg.ReadBoolean();
if (changedUseRespawnShuttle)
{
UseRespawnShuttle = useRespawnShuttle;
}
bool changedAutoRestart = incMsg.ReadBoolean();
bool autoRestart = incMsg.ReadBoolean();
if (changedAutoRestart)
{
AutoRestart = autoRestart;
}
changed |= true;
UpdateFlag(NetFlags.Misc);
}
@@ -292,8 +235,6 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("enableupnp", EnableUPnP);
doc.Root.SetAttributeValue("autorestart", autoRestart);
doc.Root.SetAttributeValue("LevelDifficulty", ((int)selectedLevelDifficulty).ToString());
doc.Root.SetAttributeValue("ServerMessage", ServerMessageText);
doc.Root.SetAttributeValue("HiddenSubs", string.Join(",", HiddenSubs));
@@ -304,6 +245,8 @@ namespace Barotrauma.Networking
SerializableProperty.SerializeProperties(this, doc.Root, true);
doc.Root.Add(CampaignSettings.Save());
doc.Root.SetAttributeValue("DisabledMonsters", string.Join(",", MonsterEnabled.Where(kvp => !kvp.Value).Select(kvp => kvp.Key.Value)));
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{
Indent = true,
@@ -351,9 +294,6 @@ namespace Barotrauma.Networking
AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
AllowModeVoting = ModeSelectionMode == SelectionMode.Vote;
selectedLevelDifficulty = doc.Root.GetAttributeFloat("LevelDifficulty", 20.0f);
GameMain.NetLobbyScreen.SetLevelDifficulty(selectedLevelDifficulty);
GameMain.NetLobbyScreen.SetTraitorProbability(traitorProbability);
HiddenSubs.UnionWith(doc.Root.GetAttributeStringArray("HiddenSubs", Array.Empty<string>()));
@@ -448,6 +388,14 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetBotCount(BotCount);
MonsterEnabled ??= CharacterPrefab.Prefabs.Select(p => (p.Identifier, true)).ToDictionary();
var disabledMonsters = doc.Root.GetAttributeIdentifierArray("DisabledMonsters", Array.Empty<Identifier>());
foreach (var disabledMonster in disabledMonsters)
{
if (MonsterEnabled.ContainsKey(disabledMonster))
{
MonsterEnabled[disabledMonster] = false;
}
}
foreach (XElement element in doc.Root.Elements())
{
@@ -112,13 +112,13 @@ namespace Barotrauma.Networking
if (recipientSpectating)
{
if (recipient.SpectatePos == null) { return true; }
distanceFactor = MathHelper.Clamp(Vector2.Distance(sender.Character.WorldPosition, recipient.SpectatePos.Value) / ChatMessage.SpeakRange, 0.0f, 1.0f);
distanceFactor = MathHelper.Clamp(Vector2.Distance(sender.Character.WorldPosition, recipient.SpectatePos.Value) / ChatMessage.SpeakRangeVOIP, 0.0f, 1.0f);
return distanceFactor < 1.0f;
}
else
{
//otherwise do a distance check
float garbleAmount = ChatMessage.GetGarbleAmount(recipient.Character, sender.Character, ChatMessage.SpeakRange);
float garbleAmount = ChatMessage.GetGarbleAmount(recipient.Character, sender.Character, ChatMessage.SpeakRangeVOIP);
distanceFactor = garbleAmount;
return garbleAmount < 1.0f;
}
@@ -106,24 +106,6 @@ namespace Barotrauma
}
}
public void ChangeServerName(string n)
{
GameMain.Server.ServerSettings.ServerName = n; lastUpdateID++;
}
public void ChangeServerMessage(string m)
{
GameMain.Server.ServerSettings.ServerMessageText = m; lastUpdateID++;
}
public List<JobPrefab> JobPreferences
{
get
{
return null;
}
}
public NetLobbyScreen()
{
LevelSeed = ToolBox.RandomSeed(8);
@@ -170,7 +152,7 @@ namespace Barotrauma
}
set
{
if (levelSeed == value) return;
if (levelSeed == value) { return; }
lastUpdateID++;
levelSeed = value;
@@ -200,6 +182,12 @@ namespace Barotrauma
{
GameMain.GameSession = null;
}
if (GameMain.Server.ServerSettings.SelectedSubmarine.IsNullOrEmpty())
{
//if no sub is selected in the settings,
//select the random sub we selected in the constructor
GameMain.Server.ServerSettings.SelectedSubmarine = SelectedSub?.Name;
}
}
public void RandomizeSettings()
@@ -334,7 +334,7 @@ namespace Barotrauma
private void CreateTraitorEvent(EventManager eventManager, TraitorEventPrefab selectedPrefab, Client traitor)
{
if (selectedPrefab.TryCreateInstance<TraitorEvent>(out var newEvent))
if (selectedPrefab.TryCreateInstance<TraitorEvent>(eventManager.RandomSeed, out var newEvent))
{
var secondaryTraitors = SelectSecondaryTraitors(newEvent, traitor);