v0.12.0.2

This commit is contained in:
Joonas Rikkonen
2021-02-10 17:08:21 +02:00
parent 5c80a59bdd
commit 694cdfee7b
353 changed files with 12897 additions and 5028 deletions
@@ -1,9 +1,27 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
partial class CharacterInfo
{
private readonly Dictionary<string, float> prevSentSkill = new Dictionary<string, float>();
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
{
if (!prevSentSkill.ContainsKey(skillIdentifier))
{
prevSentSkill[skillIdentifier] = prevLevel;
}
if (Math.Abs(prevSentSkill[skillIdentifier] - newLevel) > 0.01f)
{
GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateSkills });
prevSentSkill[skillIdentifier] = newLevel;
}
}
public void ServerWrite(IWriteMessage msg)
{
msg.Write(ID);
@@ -1333,7 +1333,15 @@ namespace Barotrauma
commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) =>
{
if (Screen.Selected == GameMain.GameScreen) { return; }
if (!GameMain.Server.StartGame()) NewMessage("Failed to start a new round", Color.Yellow);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign &&
GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
}
else
{
if (!GameMain.Server.StartGame()) { NewMessage("Failed to start a new round", Color.Yellow); }
}
}));
commands.Add(new Command("endgame|endround|end", "end/endgame/endround: End the current round.", (string[] args) =>
@@ -1,7 +1,4 @@
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma
{
@@ -21,31 +21,6 @@ namespace Barotrauma
}
}
public override void AssignTeamIDs(List<Client> clients)
{
List<Client> randList = new List<Client>(clients);
for (int i = 0; i < randList.Count; i++)
{
Client a = randList[i];
int oi = Rand.Range(0, randList.Count - 1);
Client b = randList[oi];
randList[i] = b;
randList[oi] = a;
}
int halfPlayers = randList.Count / 2;
for (int i = 0; i < randList.Count; i++)
{
if (i < halfPlayers)
{
randList[i].TeamID = Character.TeamType.Team1;
}
else
{
randList[i].TeamID = Character.TeamType.Team2;
}
}
}
public override void Update(float deltaTime)
{
if (!initialized)
@@ -54,11 +29,11 @@ namespace Barotrauma
crews[1].Clear();
foreach (Character character in Character.CharacterList)
{
if (character.TeamID == Character.TeamType.Team1)
if (character.TeamID == CharacterTeamType.Team1)
{
crews[0].Add(character);
}
else if (character.TeamID == Character.TeamType.Team2)
else if (character.TeamID == CharacterTeamType.Team2)
{
crews[1].Add(character);
}
@@ -88,7 +63,7 @@ namespace Barotrauma
//make sure nobody in the other team can be revived because that would be pretty weird
crews[1 - i].ForEach(c => { if (!c.IsDead) c.Kill(CauseOfDeathType.Unknown, null); });
GameMain.GameSession.WinningTeam = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2;
GameMain.GameSession.WinningTeam = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
state = 1;
break;
@@ -99,10 +74,10 @@ namespace Barotrauma
{
if (teamDead[0] && teamDead[1])
{
GameMain.GameSession.WinningTeam = Character.TeamType.None;
GameMain.GameSession.WinningTeam = CharacterTeamType.None;
if (GameMain.Server != null) { GameMain.Server.EndGame(); }
}
else if (GameMain.GameSession.WinningTeam != Character.TeamType.None)
else if (GameMain.GameSession.WinningTeam != CharacterTeamType.None)
{
GameMain.Server.EndGame();
}
@@ -6,6 +6,12 @@ namespace Barotrauma
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
msg.Write((byte)caves.Count);
foreach (var cave in caves)
{
msg.Write((byte)(Level.Loaded == null || !Level.Loaded.Caves.Contains(cave) ? 255 : Level.Loaded.Caves.IndexOf(cave)));
}
foreach (var kvp in SpawnedResources)
{
msg.Write((byte)kvp.Value.Count);
@@ -4,8 +4,11 @@ namespace Barotrauma
{
partial class NestMission : Mission
{
private Level.Cave selectedCave;
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
msg.Write((byte)(selectedCave == null || Level.Loaded == null || !Level.Loaded.Caves.Contains(selectedCave) ? 255 : Level.Loaded.Caves.IndexOf(selectedCave)));
msg.Write(nestPosition.X);
msg.Write(nestPosition.Y);
msg.Write((ushort)items.Count);
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -6,9 +7,12 @@ namespace Barotrauma
{
public void SellBackPurchasedItems(List<PurchasedItem> itemsToSell)
{
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> buyValues = GetBuyValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in itemsToSell)
{
var itemValue = GetBuyValueAtCurrentLocation(item);
var itemValue = item.Quantity * buyValues[item.ItemPrefab];
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
PurchasedItems.Remove(item);
@@ -17,9 +21,12 @@ namespace Barotrauma
public void BuyBackSoldItems(List<SoldItem> itemsToBuy)
{
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToBuy.Select(i => i.ItemPrefab));
foreach (SoldItem item in itemsToBuy)
{
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab);
var itemValue = sellValues[item.ItemPrefab];
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
Location.StoreCurrentBalance += itemValue;
campaign.Money -= itemValue;
@@ -29,10 +36,13 @@ namespace Barotrauma
public void SellItems(List<SoldItem> itemsToSell)
{
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
var canAddToRemoveQueue = (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && Entity.Spawner != null;
foreach (SoldItem item in itemsToSell)
{
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab);
var itemValue = sellValues[item.ItemPrefab];
// check if the store can afford the item and if the item hasn't been removed already
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
@@ -225,7 +225,7 @@ namespace Barotrauma
if (c.Inventory == null) { continue; }
if (Level.Loaded.Type == LevelData.LevelType.Outpost && c.Submarine != Level.Loaded.StartOutpost)
{
Map.CurrentLocation.RegisterTakenItems(c.Inventory.Items.Where(it => it != null && it.SpawnedInOutpost && it.OriginalModuleIndex > 0).Distinct());
Map.CurrentLocation.RegisterTakenItems(c.Inventory.AllItems.Where(it => it.SpawnedInOutpost && it.OriginalModuleIndex > 0));
}
if (c.Info != null && c.IsBot)
@@ -773,6 +773,9 @@ namespace Barotrauma
element.Add(new XAttribute("campaignid", CampaignID));
XElement modeElement = new XElement("MultiPlayerCampaign",
new XAttribute("money", Money),
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled));
CampaignMetadata?.Save(modeElement);
Map.Save(modeElement);
@@ -5,7 +5,6 @@ namespace Barotrauma.Items.Components
{
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
{
private UInt16 originalDockingTargetID;
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
@@ -15,7 +14,7 @@ namespace Barotrauma.Items.Components
if (docked)
{
msg.Write(originalDockingTargetID);
msg.Write(hulls != null && hulls[0] != null && hulls[1] != null && gap != null);
msg.Write(IsLocked);
}
}
}
@@ -32,6 +32,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
public Vector4 Padding
{
get;
set;
}
public override void Move(Vector2 amount)
{
//do nothing
@@ -1,10 +1,7 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -18,7 +15,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < Connections.Count; i++)
{
wires[i] = new List<Wire>();
for (int j = 0; j < Connection.MaxLinked; j++)
for (int j = 0; j < Connections[i].MaxWires; j++)
{
ushort wireId = msg.ReadUInt16();
@@ -68,9 +65,9 @@ namespace Barotrauma.Items.Components
{
item.CreateServerEvent(this);
c.Character.Inventory?.CreateNetworkEvent();
for (int i = 0; i < 2; i++)
foreach (Item heldItem in c.Character.HeldItems)
{
var selectedWire = c.Character.SelectedItems[i]?.GetComponent<Wire>();
var selectedWire = heldItem?.GetComponent<Wire>();
if (selectedWire == null) { continue; }
selectedWire.CreateNetworkEvent();
@@ -1,7 +1,5 @@
using Barotrauma.Networking;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -13,7 +11,7 @@ namespace Barotrauma.Items.Components
string[] elementValues = new string[customInterfaceElementList.Count];
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName))
if (customInterfaceElementList[i].HasPropertyName)
{
elementValues[i] = msg.ReadString();
}
@@ -28,9 +26,17 @@ namespace Barotrauma.Items.Components
{
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName))
if (customInterfaceElementList[i].HasPropertyName)
{
TextChanged(customInterfaceElementList[i], elementValues[i]);
if (!customInterfaceElementList[i].IsIntegerInput)
{
TextChanged(customInterfaceElementList[i], elementValues[i]);
}
else
{
int.TryParse(elementValues[i], out int value);
ValueChanged(customInterfaceElementList[i], value);
}
}
else if (customInterfaceElementList[i].ContinuousSignal)
{
@@ -60,7 +66,7 @@ namespace Barotrauma.Items.Components
//extradata contains an array of buttons clicked by a client (or nothing if nothing was clicked)
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName))
if (customInterfaceElementList[i].HasPropertyName)
{
msg.Write(customInterfaceElementList[i].Signal);
}
@@ -37,9 +37,16 @@ namespace Barotrauma.Items.Components
public void SyncHistory()
{
//split too long messages to multiple parts
int msgIndex = 0;
foreach (string str in messageHistory)
{
string msgToSend = str;
if (string.IsNullOrEmpty(msgToSend))
{
item.CreateServerEvent(this, new object[] { msgIndex, msgToSend });
msgIndex++;
continue;
}
if (msgToSend.Length > MaxMessageLength)
{
List<string> splitMessage = msgToSend.Split(' ').ToList();
@@ -62,20 +69,21 @@ namespace Barotrauma.Items.Components
if (!splitMessage.Any()) { break; }
tempMsg += " ";
} while (tempMsg.Length + splitMessage[0].Length < MaxMessageLength);
item.CreateServerEvent(this, new string[] { msgToSend });
item.CreateServerEvent(this, new object[] { msgIndex, tempMsg });
msgToSend = msgToSend.Remove(0, tempMsg.Length);
}
}
if (!string.IsNullOrEmpty(msgToSend))
{
item.CreateServerEvent(this, new string[] { msgToSend });
}
}
item.CreateServerEvent(this, new object[] { msgIndex, msgToSend });
}
msgIndex++;
}
}
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
if (extraData.Length > 2 && extraData[2] is string str)
if (extraData.Length > 3 && extraData[3] is string str)
{
msg.Write(str);
}
@@ -10,25 +10,30 @@ namespace Barotrauma
{
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
{
List<Item> prevItems = new List<Item>(Items);
List<Item> prevItems = new List<Item>(AllItems.Distinct());
byte itemCount = msg.ReadByte();
ushort[] newItemIDs = new ushort[itemCount];
for (int i = 0; i < itemCount; i++)
byte slotCount = msg.ReadByte();
List<ushort>[] newItemIDs = new List<ushort>[slotCount];
for (int i = 0; i < slotCount; i++)
{
newItemIDs[i] = msg.ReadUInt16();
newItemIDs[i] = new List<ushort>();
int itemCount = msg.ReadRangedInteger(0, MaxStackSize);
for (int j = 0; j < itemCount; j++)
{
newItemIDs[i].Add(msg.ReadUInt16());
}
}
if (c == null || c.Character == null) return;
if (c == null || c.Character == null) { return; }
bool accessible = c.Character.CanAccessInventory(this);
if (this is CharacterInventory && accessible)
if (this is CharacterInventory characterInventory && accessible)
{
if (Owner == null || !(Owner is Character))
if (Owner == null || !(Owner is Character ownerCharacter))
{
accessible = false;
}
else if (!((CharacterInventory)this).AccessibleWhenAlive && !((Character)Owner).IsDead)
else if (!characterInventory.AccessibleWhenAlive && !ownerCharacter.IsDead)
{
accessible = false;
}
@@ -42,28 +47,28 @@ namespace Barotrauma
CreateNetworkEvent();
for (int i = 0; i < capacity; i++)
{
if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item)) { continue; }
item.PositionUpdateInterval = 0.0f;
if (item.ParentInventory != null && item.ParentInventory != this)
foreach (ushort id in newItemIDs[i])
{
item.ParentInventory.CreateNetworkEvent();
if (!(Entity.FindEntityByID(id) is Item item)) { continue; }
item.PositionUpdateInterval = 0.0f;
if (item.ParentInventory != null && item.ParentInventory != this)
{
item.ParentInventory.CreateNetworkEvent();
}
}
}
return;
}
List<Inventory> prevItemInventories = new List<Inventory>(Items.Select(i => i?.ParentInventory));
List<Inventory> prevItemInventories = new List<Inventory>() { this };
for (int i = 0; i < capacity; i++)
{
Item newItem = newItemIDs[i] == 0 ? null : Entity.FindEntityByID(newItemIDs[i]) as Item;
prevItemInventories.Add(newItem?.ParentInventory);
if (newItemIDs[i] == 0 || (newItem != Items[i]))
foreach (Item item in slots[i].Items.ToList())
{
if (Items[i] != null)
if (!newItemIDs[i].Contains(item.ID))
{
Item droppedItem = Items[i];
Item droppedItem = item;
Entity prevOwner = Owner;
droppedItem.Drop(null);
@@ -84,15 +89,20 @@ namespace Barotrauma
droppedItem.body.SetTransform(prevOwner.SimPosition, 0.0f);
}
}
System.Diagnostics.Debug.Assert(Items[i] == null);
}
foreach (ushort id in newItemIDs[i])
{
Item newItem = id == 0 ? null : Entity.FindEntityByID(id) as Item;
prevItemInventories.Add(newItem?.ParentInventory);
}
}
for (int i = 0; i < capacity; i++)
{
if (newItemIDs[i] > 0)
foreach (ushort id in newItemIDs[i])
{
if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item) || item == Items[i]) { continue; }
if (!(Entity.FindEntityByID(id) is Item item) || slots[i].Contains(item)) { continue; }
if (GameMain.Server != null)
{
@@ -101,9 +111,9 @@ namespace Barotrauma
if (!prevItems.Contains(item) && !item.CanClientAccess(c))
{
#if DEBUG || UNSTABLE
#if DEBUG || UNSTABLE
DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {(item.ParentInventory?.Owner.ToString() ?? "null")}). No access.", Color.Yellow);
#endif
#endif
if (item.body != null && !c.PendingPositionUpdates.Contains(item))
{
c.PendingPositionUpdates.Enqueue(item);
@@ -115,9 +125,9 @@ namespace Barotrauma
TryPutItem(item, i, true, true, c.Character, false);
for (int j = 0; j < capacity; j++)
{
if (Items[j] == item && newItemIDs[j] != item.ID)
if (slots[j].Contains(item) && !newItemIDs[j].Contains(item.ID))
{
Items[j] = null;
slots[j].RemoveItem(item);
}
}
}
@@ -129,7 +139,7 @@ namespace Barotrauma
if (prevInventory != this) { prevInventory?.CreateNetworkEvent(); }
}
foreach (Item item in Items.Distinct())
foreach (Item item in AllItems.Distinct())
{
if (item == null) { continue; }
if (!prevItems.Contains(item))
@@ -148,7 +158,7 @@ namespace Barotrauma
foreach (Item item in prevItems.Distinct())
{
if (item == null) { continue; }
if (!Items.Contains(item))
if (!AllItems.Contains(item))
{
if (Owner == c.Character)
{
@@ -225,7 +225,7 @@ namespace Barotrauma
byte decalIndex = msg.ReadByte();
float decalAlpha = msg.ReadRangedSingle(0.0f, 1.0f, 255);
if (decalIndex < 0 || decalIndex >= decals.Count) { return; }
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
if (c.Character != null && c.Character.AllowInput && c.Character.HeldItems.Any(it => it.GetComponent<Sprayer>() != null))
{
decals[decalIndex].BaseAlpha = decalAlpha;
}
@@ -242,7 +242,7 @@ namespace Barotrauma
Color color = new Color(msg.ReadUInt32());
//TODO: verify the client is close enough to this hull to paint it, that the sprayer is functional and that the color matches
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
if (c.Character != null && c.Character.AllowInput && c.Character.HeldItems.Any(it => it.GetComponent<Sprayer>() != null))
{
BackgroundSections[i].SetColorStrength(colorStrength);
BackgroundSections[i].SetColor(color);
@@ -155,11 +155,17 @@ namespace Barotrauma.Networking
switch (orderTargetType)
{
case Order.OrderTargetType.Entity:
(orderTargetEntity as MapEntity)?.SetIgnoreByAI(orderMsg.Order.Identifier == "ignorethis");
if (orderTargetEntity is IIgnorable ignorableEntity)
{
ignorableEntity.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis";
}
break;
case Order.OrderTargetType.WallSection:
if (!wallSectionIndex.HasValue) { break; }
(orderTargetEntity as Structure)?.GetSection(wallSectionIndex.Value)?.SetIgnoreByAI(orderMsg.Order.Identifier == "ignorethis");
if (orderTargetEntity is Structure s && s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall)
{
ignorableWall.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis";
}
break;
}
}
@@ -35,10 +35,10 @@ namespace Barotrauma.Networking
public bool SubmarineSwitchLoad = false;
private List<Client> connectedClients = new List<Client>();
private readonly List<Client> connectedClients = new List<Client>();
//for keeping track of disconnected clients in case the reconnect shortly after
private List<Client> disconnectedClients = new List<Client>();
private readonly List<Client> disconnectedClients = new List<Client>();
//keeps track of players who've previously been playing on the server
//so kick votes persist during the session and the server can let the clients know what name this client used previously
@@ -1247,22 +1247,30 @@ namespace Barotrauma.Networking
bool range = inc.ReadBoolean();
double durationSeconds = inc.ReadDouble();
TimeSpan? banDuration = null;
if (durationSeconds > 0) { banDuration = TimeSpan.FromSeconds(durationSeconds); }
var bannedClient = connectedClients.Find(cl => cl != sender && cl.Name.Equals(bannedName, StringComparison.OrdinalIgnoreCase) && cl.Connection != OwnerConnection);
if (bannedClient != null)
{
Log("Client \"" + ClientLogName(sender) + "\" banned \"" + ClientLogName(bannedClient) + "\".", ServerLog.MessageType.ServerMessage);
if (durationSeconds > 0)
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy~[initiator]={sender.Name}" : banReason, range, TimeSpan.FromSeconds(durationSeconds));
}
else
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy~[initiator]={sender.Name}" : banReason, range);
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy~[initiator]={sender.Name}" : banReason, range, banDuration);
}
}
else
{
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={bannedName}"), sender, ChatMessageType.Console);
var bannedPreviousClient = previousPlayers.Find(p => p.Name.Equals(bannedName, StringComparison.OrdinalIgnoreCase));
if (bannedPreviousClient != null)
{
Log("Client \"" + ClientLogName(sender) + "\" banned \"" + bannedPreviousClient.Name + "\".", ServerLog.MessageType.ServerMessage);
BanPreviousPlayer(bannedPreviousClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy~[initiator]={sender.Name}" : banReason, range, banDuration);
}
else
{
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={bannedName}"), sender, ChatMessageType.Console);
}
}
break;
case ClientPermissions.Unban:
@@ -1646,6 +1654,7 @@ namespace Barotrauma.Networking
IWriteMessage tempBuffer = new ReadWriteMessage();
tempBuffer.Write((byte)ServerNetObject.ENTITY_POSITION);
tempBuffer.Write(entity is Item);
if (entity is Item)
{
((Item)entity).ServerWritePosition(tempBuffer, c);
@@ -1742,6 +1751,7 @@ namespace Barotrauma.Networking
outmsg.Write(client.NameID);
outmsg.Write(client.Name);
outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : (client.PreferredJob ?? ""));
outmsg.Write((byte)client.PreferredTeam);
outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID);
if (c.HasPermission(ClientPermissions.ServerLog))
{
@@ -2068,14 +2078,14 @@ namespace Barotrauma.Networking
//always allow the server owner to spectate even if it's disallowed in server settings
playingClients.RemoveAll(c => c.Connection == OwnerConnection && c.SpectateOnly);
if (GameMain.GameSession.GameMode.Mission != null)
if (GameMain.GameSession.GameMode is PvPMode pvpMode)
{
GameMain.GameSession.GameMode.Mission.AssignTeamIDs(playingClients);
teamCount = GameMain.GameSession.GameMode.Mission.TeamCount;
pvpMode.AssignTeamIDs(playingClients);
teamCount = 2;
}
else
{
connectedClients.ForEach(c => c.TeamID = Character.TeamType.Team1);
connectedClients.ForEach(c => c.TeamID = CharacterTeamType.Team1);
}
if (campaign != null)
@@ -2147,7 +2157,7 @@ namespace Barotrauma.Networking
//assign jobs and spawnpoints separately for each team
for (int n = 0; n < teamCount; n++)
{
var teamID = n == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2;
var teamID = n == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
Submarine.MainSubs[n].TeamID = teamID;
foreach (Item item in Item.ItemList)
@@ -2174,7 +2184,7 @@ namespace Barotrauma.Networking
//always allow the server owner to spectate even if it's disallowed in server settings
teamClients.RemoveAll(c => c.Connection == OwnerConnection && c.SpectateOnly);
if (!teamClients.Any() && n > 0) { continue; }
//if (!teamClients.Any() && n > 0) { continue; }
AssignJobs(teamClients);
@@ -2193,6 +2203,10 @@ namespace Barotrauma.Networking
{
client.CharacterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, client.Name);
}
else
{
client.CharacterInfo.ResetCurrentOrder();
}
characterInfos.Add(client.CharacterInfo);
if (client.CharacterInfo.Job == null || client.CharacterInfo.Job.Prefab != client.AssignedJob.First)
{
@@ -2518,6 +2532,7 @@ namespace Barotrauma.Networking
foreach (Client client in connectedClients)
{
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
client.Character?.ResetCurrentOrder();
client.Character = null;
client.HasSpawned = false;
client.InGame = false;
@@ -2554,13 +2569,15 @@ namespace Barotrauma.Networking
UInt16 nameId = inc.ReadUInt16();
string newName = inc.ReadString();
string newJob = inc.ReadString();
CharacterTeamType newTeam = (CharacterTeamType)inc.ReadByte();
if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameID)) { return false; }
c.NameID = nameId;
newName = Client.SanitizeName(newName);
if (newName == c.Name && newJob == c.PreferredJob) { return false; }
if (newName == c.Name && newJob == c.PreferredJob && newTeam == c.PreferredTeam) { return false; }
c.PreferredJob = newJob;
c.PreferredTeam = newTeam;
//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
@@ -2674,7 +2691,6 @@ namespace Barotrauma.Networking
lidgrenConn.IPEndPoint.Address.MapToIPv4NoThrow().ToString() :
lidgrenConn.IPEndPoint.Address.ToString();
if (range) { ip = BanList.ToRange(ip); }
serverSettings.BanList.BanPlayer(client.Name, ip, reason, duration);
}
if (client.SteamID > 0)
@@ -2683,6 +2699,32 @@ namespace Barotrauma.Networking
}
}
public void BanPreviousPlayer(PreviousPlayer previousPlayer, string reason, bool range = false, TimeSpan? duration = null)
{
if (previousPlayer == null) { return; }
//reset karma to a neutral value, so if/when the ban is revoked the client wont get immediately punished by low karma again
previousPlayer.Karma = Math.Max(previousPlayer.Karma, 50.0f);
if (!string.IsNullOrEmpty(previousPlayer.EndPoint) && (previousPlayer.SteamID == 0 || range))
{
string ip = previousPlayer.EndPoint;
if (range) { ip = BanList.ToRange(ip); }
serverSettings.BanList.BanPlayer(previousPlayer.Name, ip, reason, duration);
}
if (previousPlayer.SteamID > 0)
{
serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.SteamID, reason, duration);
}
string msg = $"ServerMessage.BannedFromServer~[client]={previousPlayer.Name}";
if (!string.IsNullOrWhiteSpace(reason))
{
msg += $"/ /ServerMessage.Reason/: /{reason}";
}
SendChatMessage(msg, ChatMessageType.Server, changeType: PlayerConnectionChangeType.Banned);
}
public override void UnbanPlayer(string playerName, string playerEndPoint)
{
if (!string.IsNullOrEmpty(playerEndPoint))
@@ -2723,8 +2765,8 @@ namespace Barotrauma.Networking
client.HasSpawned = false;
client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) msg = $"ServerMessage.ClientLeftServer~[client]={client.Name}";
if (string.IsNullOrWhiteSpace(targetmsg)) targetmsg = "ServerMessage.YouLeftServer";
if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={client.Name}"; }
if (string.IsNullOrWhiteSpace(targetmsg)) { targetmsg = "ServerMessage.YouLeftServer"; }
if (!string.IsNullOrWhiteSpace(reason))
{
msg += $"/ /ServerMessage.Reason/: /{reason}";
@@ -2934,14 +2976,14 @@ namespace Barotrauma.Networking
{
case ChatMessageType.Radio:
case ChatMessageType.Order:
if (senderCharacter == null) return;
if (senderCharacter == null) { return; }
//return if senderCharacter doesn't have a working radio
var radio = senderCharacter.Inventory?.Items.FirstOrDefault(i => i != null && i.GetComponent<WifiComponent>() != null);
if (radio == null || !senderCharacter.HasEquippedItem(radio)) return;
var radio = senderCharacter.Inventory?.AllItems.FirstOrDefault(i => i.GetComponent<WifiComponent>() != null);
if (radio == null || !senderCharacter.HasEquippedItem(radio)) { return; }
senderRadio = radio.GetComponent<WifiComponent>();
if (!senderRadio.CanTransmit()) return;
if (!senderRadio.CanTransmit()) { return; }
break;
case ChatMessageType.Dead:
//character still alive and capable of speaking -> dead chat not allowed
@@ -3156,8 +3198,8 @@ namespace Barotrauma.Networking
if (voteType != VoteType.PurchaseSub)
{
GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(targetSubmarine, true);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub, true);
}
serverSettings.Voting.StopSubmarineVote(true);
@@ -3385,7 +3427,7 @@ namespace Barotrauma.Networking
assignedClientCount.Add(jp, 0);
}
Character.TeamType teamID = Character.TeamType.None;
CharacterTeamType teamID = CharacterTeamType.None;
if (unassigned.Count > 0) { teamID = unassigned[0].TeamID; }
//if we're playing a multiplayer campaign, check which clients already have a character and a job
@@ -3547,7 +3589,7 @@ namespace Barotrauma.Networking
}
}
public void AssignBotJobs(List<CharacterInfo> bots, Character.TeamType teamID)
public void AssignBotJobs(List<CharacterInfo> bots, CharacterTeamType teamID)
{
Dictionary<JobPrefab, int> assignedPlayerCount = new Dictionary<JobPrefab, int>();
foreach (JobPrefab jp in JobPrefab.Prefabs)
@@ -3664,7 +3706,7 @@ namespace Barotrauma.Networking
{
retVal += "color:#ff9900;";
}
retVal += "metadata:" + (client.SteamID!=0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name) + "‖end‖";
retVal += "metadata:" + (client.SteamID != 0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name) + "‖end‖";
return retVal;
}
@@ -256,8 +256,28 @@ namespace Barotrauma
return;
}
}
var foundItem = Inventory.FindItemRecursive(item, it => it.Prefab.Identifier == "idcard" || it.GetComponent<RangedWeapon>() != null || it.GetComponent<MeleeWeapon>() != null);
Item foundItem = null;
if (isValid(item))
{
foundItem = item;
}
else
{
foreach (Item containedItem in item.ContainedItems)
{
if (isValid(containedItem))
{
foundItem = containedItem;
break;
}
}
}
static bool isValid(Item item)
{
return item.Prefab.Identifier == "idcard" || item.GetComponent<RangedWeapon>() != null || item.GetComponent<MeleeWeapon>() != null;
}
if (foundItem == null) { return; }
@@ -382,7 +402,7 @@ namespace Barotrauma
//smaller karma penalty for attacking someone who's aiming with a weapon
if (damage > 0.0f &&
target.IsKeyDown(InputType.Aim) &&
target.SelectedItems.Any(it => it != null && (it.GetComponent<MeleeWeapon>() != null || it.GetComponent<RangedWeapon>() != null)))
target.HeldItems.Any(it => it.GetComponent<MeleeWeapon>() != null || it.GetComponent<RangedWeapon>() != null))
{
damage *= 0.5f;
stun *= 0.5f;
@@ -473,12 +493,12 @@ namespace Barotrauma
{
//cap the damage so the karma can't decrease by more than MaxStructureDamageKarmaDecreasePerSecond per second
var clientMemory = GetClientMemory(client);
clientMemory.StructureDamageAccumulator += damageAmount;
if (clientMemory.StructureDamagePerSecond + damageAmount >= MaxStructureDamageKarmaDecreasePerSecond / StructureDamageKarmaDecrease)
{
damageAmount -= (MaxStructureDamageKarmaDecreasePerSecond / StructureDamageKarmaDecrease) - clientMemory.StructureDamagePerSecond;
damageAmount -= (clientMemory.StructureDamagePerSecond + damageAmount) - (MaxStructureDamageKarmaDecreasePerSecond / StructureDamageKarmaDecrease);
if (damageAmount <= 0.0f) { return; }
}
clientMemory.StructureDamageAccumulator += damageAmount;
}
AdjustKarma(attacker, -damageAmount * StructureDamageKarmaDecrease, "Damaged structures");
}
@@ -233,7 +233,7 @@ namespace Barotrauma.Networking
case ConnectionInitialization.ContentPackageOrder:
outMsg.Write(GameMain.Server.ServerName);
var mpContentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).Reverse().ToList();
var mpContentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
{
@@ -36,7 +36,7 @@ namespace Barotrauma.Networking
if (GameMain.Server.ServerSettings.BotSpawnMode == BotSpawnMode.Normal)
{
return Character.CharacterList
.FindAll(c => c.TeamID == Character.TeamType.Team1 && c.AIController != null && c.Info != null && c.IsDead)
.FindAll(c => c.TeamID == CharacterTeamType.Team1 && c.AIController != null && c.Info != null && c.IsDead)
.Select(c => c.Info)
.ToList();
}
@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
(!c.SpectateOnly || (!GameMain.Server.ServerSettings.AllowSpectating && GameMain.Server.OwnerConnection != c.Connection)));
var existingBots = Character.CharacterList
.FindAll(c => c.TeamID == Character.TeamType.Team1 && c.AIController != null && c.Info != null);
.FindAll(c => c.TeamID == CharacterTeamType.Team1 && c.AIController != null && c.Info != null);
int requiredBots = GameMain.Server.ServerSettings.BotCount - currPlayerCount;
requiredBots -= existingBots.Count(b => !b.IsDead);
@@ -238,13 +238,17 @@ namespace Barotrauma.Networking
//all characters are in Team 1 in game modes/missions with only one team.
//if at some point we add a game mode with multiple teams where respawning is possible, this needs to be reworked
c.TeamID = Character.TeamType.Team1;
c.TeamID = CharacterTeamType.Team1;
if (c.CharacterInfo == null) { c.CharacterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, c.Name); }
}
List<CharacterInfo> characterInfos = clients.Select(c => c.CharacterInfo).ToList();
var botsToSpawn = GetBotsToRespawn();
characterInfos.AddRange(botsToSpawn);
//bots don't respawn in the campaign
if (campaign == null)
{
var botsToSpawn = GetBotsToRespawn();
characterInfos.AddRange(botsToSpawn);
}
GameMain.Server.AssignJobs(clients);
foreach (Client c in clients)
@@ -276,7 +280,7 @@ namespace Barotrauma.Networking
characterInfos[i].CurrentOrderOption = null;
var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, characterInfos[i].Name, isRemotePlayer: !bot, hasAi: bot);
character.TeamID = Character.TeamType.Team1;
character.TeamID = CharacterTeamType.Team1;
if (bot)
{
@@ -348,9 +352,9 @@ namespace Barotrauma.Networking
}
//add the ID card tags they should've gotten when spawning in the shuttle
foreach (Item item in character.Inventory.Items)
foreach (Item item in character.Inventory.AllItems.Distinct())
{
if (item == null || item.Prefab.Identifier != "idcard") { continue; }
if (item.Prefab.Identifier != "idcard") { continue; }
foreach (string s in shuttleSpawnPoints[i].IdCardTags)
{
item.AddTag(s);
@@ -180,7 +180,7 @@ namespace Barotrauma
if (allowNew && !targetContainer.OwnInventory.IsFull())
{
existingItems.Clear();
foreach (var item in targetContainer.OwnInventory.Items)
foreach (var item in targetContainer.OwnInventory.AllItems.Distinct())
{
existingItems.Add(item);
}
@@ -205,7 +205,7 @@ namespace Barotrauma
base.Update(deltaTime);
if (target == null)
{
target = targetContainer.OwnInventory.Items.FirstOrDefault(item => item != null && item.Prefab.Identifier == (containedPrefab != null ? itemContainerId : identifier) && !existingItems.Contains(item));
target = targetContainer.ContainedItems.FirstOrDefault(item => item.Prefab.Identifier == (containedPrefab != null ? itemContainerId : identifier) && !existingItems.Contains(item));
if (target != null)
{
if (containedPrefab != null)
@@ -36,7 +36,7 @@ namespace Barotrauma
if (sabotageContainerIds.Contains(item.prefab.Identifier))
{
++totalAmount;
if (item.OwnInventory.Items.Length <= 0 || item.OwnInventory.Items.All(containedItem => containedItem != null && !validReplacementIds.Contains(containedItem.Prefab.Identifier)))
if (item.OwnInventory.AllItems.All(containedItem => !validReplacementIds.Contains(containedItem.Prefab.Identifier)))
{
continue;
}
@@ -19,10 +19,10 @@ namespace Barotrauma
// All traitor related functionality should use the following interface for generating random values
public static double RandomDouble() => Random.NextDouble();
public readonly Dictionary<Character.TeamType, Traitor.TraitorMission> Missions = new Dictionary<Character.TeamType, Traitor.TraitorMission>();
public readonly Dictionary<CharacterTeamType, Traitor.TraitorMission> Missions = new Dictionary<CharacterTeamType, Traitor.TraitorMission>();
public string GetCodeWords(Character.TeamType team) => Missions.TryGetValue(team, out var mission) ? mission.CodeWords : "";
public string GetCodeResponse(Character.TeamType team) => Missions.TryGetValue(team, out var mission) ? mission.CodeResponse : "";
public string GetCodeWords(CharacterTeamType team) => Missions.TryGetValue(team, out var mission) ? mission.CodeWords : "";
public string GetCodeResponse(CharacterTeamType team) => Missions.TryGetValue(team, out var mission) ? mission.CodeResponse : "";
public IEnumerable<Traitor> Traitors => Missions.Values.SelectMany(mission => mission.Traitors.Values);
@@ -87,18 +87,18 @@ namespace Barotrauma
{
bool missionCompleted = false;
bool gameShouldEnd = false;
Character.TeamType winningTeam = Character.TeamType.None;
CharacterTeamType winningTeam = CharacterTeamType.None;
foreach (var mission in Missions)
{
mission.Value.Update(deltaTime, () =>
{
switch (mission.Key)
{
case Character.TeamType.Team1:
winningTeam = (winningTeam == Character.TeamType.None) ? Character.TeamType.Team2 : Character.TeamType.None;
case CharacterTeamType.Team1:
winningTeam = (winningTeam == CharacterTeamType.None) ? CharacterTeamType.Team2 : CharacterTeamType.None;
break;
case Character.TeamType.Team2:
winningTeam = (winningTeam == Character.TeamType.None) ? Character.TeamType.Team1 : Character.TeamType.None;
case CharacterTeamType.Team2:
winningTeam = (winningTeam == CharacterTeamType.None) ? CharacterTeamType.Team1 : CharacterTeamType.None;
break;
default:
break;
@@ -137,13 +137,13 @@ namespace Barotrauma
startCountdown = MathHelper.Lerp(server.ServerSettings.TraitorsMinRestartDelay, server.ServerSettings.TraitorsMaxRestartDelay, (float)RandomDouble());
return;
}
if (Character.CharacterList.Count(c => !c.IsDead && c.TeamID == Character.TeamType.Team1 || c.TeamID == Character.TeamType.Team2) <= 1)
if (Character.CharacterList.Count(c => !c.IsDead && c.TeamID == CharacterTeamType.Team1 || c.TeamID == CharacterTeamType.Team2) <= 1)
{
return;
}
if (GameMain.GameSession.Mission is CombatMission)
{
var teamIds = new[] { Character.TeamType.Team1, Character.TeamType.Team2 };
var teamIds = new[] { CharacterTeamType.Team1, CharacterTeamType.Team2 };
foreach (var teamId in teamIds)
{
if (server.ConnectedClients.Count(c => c.Character != null && !c.Character.IsDead && c.TeamID == teamId) < 2)
@@ -170,11 +170,11 @@ namespace Barotrauma
{
var mission = TraitorMissionPrefab.RandomPrefab()?.Instantiate();
if (mission != null) {
if (mission.CanBeStarted(server, this, Character.TeamType.None))
if (mission.CanBeStarted(server, this, CharacterTeamType.None))
{
if (mission.Start(server, this, Character.TeamType.None))
if (mission.Start(server, this, CharacterTeamType.None))
{
Missions.Add(Character.TeamType.None, mission);
Missions.Add(CharacterTeamType.None, mission);
return;
}
}
@@ -87,13 +87,13 @@ namespace Barotrauma
return pendingObjectives.Find(objective => objective.Roles.Contains(traitor.Role));
}
protected List<Tuple<Client, Character>> FindTraitorCandidates(GameServer server, Character.TeamType team, RoleFilter traitorRoleFilter)
protected List<Tuple<Client, Character>> FindTraitorCandidates(GameServer server, CharacterTeamType team, RoleFilter traitorRoleFilter)
{
var traitorCandidates = new List<Tuple<Client, Character>>();
foreach (Client c in server.ConnectedClients)
{
if (c.Character == null || c.Character.IsDead || c.Character.Removed || !traitorRoleFilter(c.Character) ||
(team != Character.TeamType.None && c.Character.TeamID != team))
(team != CharacterTeamType.None && c.Character.TeamID != team))
{
continue;
}
@@ -115,7 +115,7 @@ namespace Barotrauma
return characters;
}
protected List<Tuple<string, Tuple<Client, Character>>> AssignTraitors(GameServer server, TraitorManager traitorManager, Character.TeamType team)
protected List<Tuple<string, Tuple<Client, Character>>> AssignTraitors(GameServer server, TraitorManager traitorManager, CharacterTeamType team)
{
List<Character> characters = FindCharacters();
#if !ALLOW_SOLO_TRAITOR
@@ -176,7 +176,7 @@ namespace Barotrauma
return assignedCandidates;
}
public bool CanBeStarted(GameServer server, TraitorManager traitorManager, Character.TeamType team)
public bool CanBeStarted(GameServer server, TraitorManager traitorManager, CharacterTeamType team)
{
foreach (var role in Roles)
{
@@ -189,7 +189,7 @@ namespace Barotrauma
return AssignTraitors(server, traitorManager, team) != null;
}
public bool Start(GameServer server, TraitorManager traitorManager, Character.TeamType team)
public bool Start(GameServer server, TraitorManager traitorManager, CharacterTeamType team)
{
var assignedCandidates = AssignTraitors(server, traitorManager, team);
if (assignedCandidates == null)