Build 0.18.0.0

This commit is contained in:
Markus Isberg
2022-05-13 00:55:52 +09:00
parent 15d18e6ff6
commit 7547a9b78a
218 changed files with 3881 additions and 2192 deletions
@@ -37,7 +37,7 @@ namespace Barotrauma
partial void OnPermanentStatChanged(StatTypes statType)
{
if (Character == null || Character.Removed) { return; }
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdatePermanentStatsEventData());
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdatePermanentStatsEventData(statType));
}
public void ServerWrite(IWriteMessage msg)
@@ -25,9 +25,9 @@ namespace Barotrauma
/// <summary>
/// Saves bots in multiplayer
/// </summary>
public void SaveMultiplayer(XElement root)
public XElement SaveMultiplayer(XElement parentElement)
{
XElement saveElement = new XElement("bots", new XAttribute("hasbots", HasBots));
var element = new XElement("bots", new XAttribute("hasbots", HasBots));
foreach (CharacterInfo info in characterInfos)
{
if (Level.Loaded != null)
@@ -35,13 +35,13 @@ namespace Barotrauma
if (!info.IsNewHire && (info.Character == null || info.Character.IsDead)) { continue; }
}
XElement characterElement = info.Save(saveElement);
XElement characterElement = info.Save(element);
if (info.InventoryData != null) { characterElement.Add(info.InventoryData); }
if (info.HealthData != null) { characterElement.Add(info.HealthData); }
if (info.OrderData != null) { characterElement.Add(info.OrderData); }
}
SaveActiveOrders(saveElement);
root.Add(saveElement);
parentElement?.Add(element);
return element;
}
public void ServerWriteActiveOrders(IWriteMessage msg)
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -10,6 +11,31 @@ namespace Barotrauma
protected set;
}
private static bool IsOwner(Client client) => client != null && client.Connection == GameMain.Server.OwnerConnection;
/// <summary>
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
/// </summary>
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
{
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
//or if no-one has management permissions
return
client.HasPermission(permissions) ||
client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Server.ConnectedClients.Count == 1 ||
IsOwner(client) ||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
}
public bool AllowedToManageWallets(Client client)
{
return
client.HasPermission(ClientPermissions.ManageCampaign) ||
client.HasPermission(ClientPermissions.ManageMoney) ||
IsOwner(client);
}
public override void ShowStartMessage()
{
foreach (Mission mission in Missions)
@@ -163,29 +163,6 @@ namespace Barotrauma
private static bool IsOwner(Client client) => client != null && client.Connection == GameMain.Server.OwnerConnection;
/// <summary>
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
/// </summary>
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
{
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
//or if no-one has management permissions
return
client.HasPermission(permissions) ||
client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Server.ConnectedClients.Count == 1 ||
IsOwner(client) ||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
}
public bool AllowedToManageWallets(Client client)
{
return
client.HasPermission(ClientPermissions.ManageCampaign) ||
client.HasPermission(ClientPermissions.ManageMoney) ||
IsOwner(client);
}
public void SaveExperiencePoints(Client client)
{
ClearSavedExperiencePoints(client);
@@ -200,14 +177,6 @@ namespace Barotrauma
savedExperiencePoints.RemoveAll(s => s.SteamID != 0 && client.SteamID == s.SteamID || client.EndpointMatches(s.EndPoint));
}
public void LoadPets()
{
if (petsElement != null)
{
PetBehavior.LoadPets(petsElement);
}
}
public void SavePlayers()
{
//refresh the character data of clients who are still in the server
@@ -261,8 +230,7 @@ namespace Barotrauma
characterData.ForEach(cd => cd.HasSpawned = false);
petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement);
SavePets();
//remove all items that are in someone's inventory
foreach (Character c in Character.CharacterList)
@@ -285,6 +253,8 @@ namespace Barotrauma
c.Inventory.DeleteAllItems();
}
SaveActiveOrders();
}
public void MoveDiscardedCharacterBalancesToBank()
@@ -348,44 +318,10 @@ namespace Barotrauma
if (success)
{
SavePlayers();
yield return CoroutineStatus.Running;
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
{
Submarine.MainSub = leavingSub;
GameMain.GameSession.Submarine = leavingSub;
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
foreach (Submarine sub in subsToLeaveBehind)
{
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
LinkedSubmarine.CreateDummy(leavingSub, sub);
}
}
LeaveUnconnectedSubs(leavingSub);
NextLevel = newLevel;
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
if (PendingSubmarineSwitch != null)
{
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
{
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
{
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
break;
}
}
}
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null;
}
else
{
@@ -977,6 +913,8 @@ namespace Barotrauma
{
NetWalletTransfer transfer = INetSerializableStruct.Read<NetWalletTransfer>(msg);
if (GameMain.Server is null) { return; }
switch (transfer.Sender)
{
case Some<ushort> { Value: var id }:
@@ -992,7 +930,8 @@ namespace Barotrauma
{
if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID)
{
GameMain.Server?.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
if (transfer.Amount > GameMain.Server.ServerSettings.MaximumTransferRequest) { 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;
@@ -1301,7 +1240,11 @@ namespace Barotrauma
}
// save bots
CrewManager.SaveMultiplayer(modeElement);
var crewManagerElement = CrewManager.SaveMultiplayer(modeElement);
if (ActiveOrdersElement != null)
{
crewManagerElement.Add(ActiveOrdersElement);
}
XElement savedExperiencePointsElement = new XElement("SavedExperiencePoints");
foreach (var savedExperiencePoint in savedExperiencePoints)
@@ -1,19 +1,27 @@
using Barotrauma.Networking;
using System;
namespace Barotrauma.Items.Components
{
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
{
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(docked);
if (docked)
{
msg.Write(DockingTarget.item.ID);
msg.Write(IsLocked);
}
}
public void ServerEventRead(IReadMessage msg, Client c)
{
var allowOutpostAutoDocking = (AllowOutpostAutoDocking)msg.ReadByte();
if (outpostAutoDockingPromptShown &&
(GameMain.GameSession?.Campaign?.AllowedToManageCampaign(c, ClientPermissions.ManageMap) ?? false))
{
this.allowOutpostAutoDocking = allowOutpostAutoDocking;
}
}
}
}
@@ -39,7 +39,7 @@ namespace Barotrauma.Items.Components
set;
}
public override void Move(Vector2 amount)
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
//do nothing
}
@@ -15,7 +15,8 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < Connections.Count; i++)
{
wires[i] = new List<Wire>();
for (int j = 0; j < Connections[i].MaxWires; j++)
uint wireCount = msg.ReadVariableUInt32();
for (int j = 0; j < wireCount; j++)
{
ushort wireId = msg.ReadUInt16();
@@ -91,12 +92,8 @@ namespace Barotrauma.Items.Components
//go through existing wire links
for (int i = 0; i < Connections.Count; i++)
{
int j = -1;
foreach (Wire existingWire in Connections[i].Wires)
foreach (Wire existingWire in Connections[i].Wires.ToArray())
{
j++;
if (existingWire == null) { continue; }
//existing wire not in the list of new wires -> disconnect it
if (!wires[i].Contains(existingWire))
{
@@ -163,7 +160,7 @@ namespace Barotrauma.Items.Components
}*/
}
Connections[i].SetWire(j, null);
Connections[i].DisconnectWire(existingWire);
}
}
}
@@ -42,7 +42,7 @@ namespace Barotrauma.MapCreatures.Behavior
foreach (BallastFloraBranch branch in Branches)
{
//don't notify about minuscule amounts of damage (<= 1.0f)
if (branch.AccumulatedDamage > 1.0f)
if (Math.Abs(branch.AccumulatedDamage) > 1.0f)
{
CreateNetworkMessage(new BranchDamageEventData(branch));
branch.AccumulatedDamage = 0.0f;
@@ -24,7 +24,7 @@ namespace Barotrauma.Networking
public UInt16 LastRecvCampaignUpdate = 0;
public UInt16 LastRecvCampaignSave = 0;
public Pair<UInt16, float> LastCampaignSaveSendTime;
public (UInt16 saveId, float time) LastCampaignSaveSendTime;
public readonly List<ChatMessage> ChatMsgQueue = new List<ChatMessage>();
public UInt16 LastChatMsgQueueID;
@@ -391,7 +391,7 @@ namespace Barotrauma.Networking
StartTransfer(inc.Sender, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
{
client.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
client.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
}
}
break;
@@ -955,7 +955,9 @@ namespace Barotrauma.Networking
}
if (Level.Loaded != null)
{
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
errorLines.Add("Level: " + Level.Loaded.Seed + ", "
+ string.Join("; ", Level.Loaded.EqualityCheckValues.Select(cv
=> cv.Key + "=" + cv.Value.ToString("X"))));
errorLines.Add("Entity count before generating level: " + Level.Loaded.EntityCountBeforeGenerate);
errorLines.Add("Entities:");
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate.OrderBy(e => e.CreationIndex))
@@ -1548,11 +1550,11 @@ namespace Barotrauma.Networking
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
{
//already sent an up-to-date campaign save
if (c.LastCampaignSaveSendTime != null && campaign.LastSaveID == c.LastCampaignSaveSendTime.First)
if (c.LastCampaignSaveSendTime != default && campaign.LastSaveID == c.LastCampaignSaveSendTime.saveId)
{
//the save was sent less than 5 second ago, don't attempt to resend yet
//(the client may have received it but hasn't acked us yet)
if (c.LastCampaignSaveSendTime.Second > NetTime.Now - 5.0f)
if (c.LastCampaignSaveSendTime.time > NetTime.Now - 5.0f)
{
return;
}
@@ -1561,7 +1563,7 @@ namespace Barotrauma.Networking
if (!FileSender.ActiveTransfers.Any(t => t.Connection == c.Connection && t.FileType == FileTransferType.CampaignSave))
{
FileSender.StartTransfer(c.Connection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
c.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)NetTime.Now);
c.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)NetTime.Now);
}
}
}
@@ -2193,7 +2195,7 @@ namespace Barotrauma.Networking
Level.Loaded?.SpawnNPCs();
Level.Loaded?.SpawnCorpses();
Level.Loaded?.PrepareBeaconStation();
AutoItemPlacer.PlaceIfNeeded();
AutoItemPlacer.SpawnItems();
CrewManager crewManager = campaign?.CrewManager;
@@ -2388,7 +2390,9 @@ namespace Barotrauma.Networking
}
campaign?.LoadPets();
crewManager?.LoadActiveOrders();
campaign?.LoadActiveOrders();
campaign?.CargoManager.InitPurchasedIDCards();
foreach (Submarine sub in Submarine.MainSubs)
{
@@ -2400,7 +2404,7 @@ namespace Barotrauma.Networking
spawnList.Add(new PurchasedItem(kvp.Key, kvp.Value, buyer: null));
}
CargoManager.CreateItems(spawnList, sub);
CargoManager.CreateItems(spawnList, sub, cargoManager: null);
}
TraitorManager = null;
@@ -2531,10 +2535,9 @@ namespace Barotrauma.Networking
{
msg.Write(mission.Prefab.Identifier);
}
msg.Write((byte)GameMain.GameSession.Level.EqualityCheckValues.Count);
foreach (int equalityCheckValue in GameMain.GameSession.Level.EqualityCheckValues)
foreach (Level.LevelGenStage stage in Enum.GetValues(typeof(Level.LevelGenStage)).OfType<Level.LevelGenStage>().OrderBy(s => s))
{
msg.Write(equalityCheckValue);
msg.Write(GameMain.GameSession.Level.EqualityCheckValues[stage]);
}
foreach (Mission mission in GameMain.GameSession.Missions)
{
@@ -3178,9 +3181,9 @@ namespace Barotrauma.Networking
Client recipient = connectedClients.Find(c => c.Connection == transfer.Connection);
if (transfer.FileType == FileTransferType.CampaignSave &&
(transfer.Status == FileTransferStatus.Sending || transfer.Status == FileTransferStatus.Finished) &&
recipient.LastCampaignSaveSendTime != null)
recipient.LastCampaignSaveSendTime != default)
{
recipient.LastCampaignSaveSendTime.Second = (float)Lidgren.Network.NetTime.Now;
recipient.LastCampaignSaveSendTime.time = (float)NetTime.Now;
}
}
@@ -278,12 +278,12 @@ namespace Barotrauma
static bool isValid(Item item)
{
return item.Prefab.Identifier == "idcard" || item.GetComponent<RangedWeapon>() != null || item.GetComponent<MeleeWeapon>() != null;
return item.GetComponent<IdCard>() != null || item.GetComponent<RangedWeapon>() != null || item.GetComponent<MeleeWeapon>() != null;
}
if (foundItem == null) { return; }
bool isIdCard = ((MapEntity)foundItem).Prefab.Identifier == "idcard";
bool isIdCard = foundItem.GetComponent<IdCard>() != null;
bool isWeapon = foundItem.GetComponent<RangedWeapon>() != null || foundItem.GetComponent<MeleeWeapon>() != null;
if (isIdCard)
@@ -441,32 +441,43 @@ namespace Barotrauma.Networking
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", GameServer.ClientLogName(clients[i]), clients[i].Connection?.EndPointString, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
}
if (divingSuitPrefab != null && oxyPrefab != null && RespawnShuttle != null)
if (RespawnShuttle != null)
{
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
if (divingSuitPrefab != null && oxyPrefab != null)
if (divingSuitPrefab != null)
{
var divingSuit = new Item(divingSuitPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(divingSuit));
respawnItems.Add(divingSuit);
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
divingSuit.Combine(oxyTank, user: null);
respawnItems.Add(oxyTank);
if (oxyPrefab != null && divingSuit.GetComponent<ItemContainer>() != null)
{
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
divingSuit.Combine(oxyTank, user: null);
respawnItems.Add(oxyTank);
}
}
if (scooterPrefab != null && batteryPrefab != null)
if (!(GameMain.GameSession.GameMode is CampaignMode))
{
var scooter = new Item(scooterPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
var battery = new Item(batteryPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
scooter.Combine(battery, user: null);
respawnItems.Add(scooter);
respawnItems.Add(battery);
if (scooterPrefab != null)
{
var scooter = new Item(scooterPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
respawnItems.Add(scooter);
if (batteryPrefab != null)
{
var battery = new Item(batteryPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
scooter.Combine(battery, user: null);
respawnItems.Add(battery);
}
}
}
if (respawnContainer != null)
{
AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer);
}
}
@@ -504,7 +515,7 @@ namespace Barotrauma.Networking
//add the ID card tags they should've gotten when spawning in the shuttle
foreach (Item item in character.Inventory.AllItems.Distinct())
{
if (item.Prefab.Identifier != "idcard") { continue; }
if (item.GetComponent<IdCard>() == null) { continue; }
foreach (string s in shuttleSpawnPoints[i].IdCardTags)
{
item.AddTag(s);
@@ -44,12 +44,14 @@ namespace Barotrauma
{
GameMain.Server?.SwitchSubmarine();
}
else
{
voting.RegisterRejectedVote(this);
}
voting.StopSubmarineVote(passed);
}
}
public static IVote ActiveVote;
public class TransferVote : IVote
{
public Client VoteStarter { get; }
@@ -83,12 +85,22 @@ namespace Barotrauma
toWallet.Give(TransferAmount);
}
}
else
{
voting.RegisterRejectedVote(this);
}
voting.StopMoneyTransferVote(passed);
}
}
public static IVote ActiveVote;
private static readonly Queue<IVote> pendingVotes = new Queue<IVote>();
private readonly TimeSpan rejectedVoteCooldown = new TimeSpan(0, 1, 0);
private readonly Dictionary<Client, (VoteType voteType, DateTime time)> rejectedVoteTimes = new Dictionary<Client, (VoteType voteType, DateTime time)>();
private void StartSubmarineVote(SubmarineInfo subInfo, VoteType voteType, Client sender)
{
if (ActiveVote == null)
@@ -136,6 +148,10 @@ namespace Barotrauma
public void StartTransferVote(Client starter, Client from, int transferAmount, Client to)
{
if (ShouldRejectVote(starter, VoteType.TransferMoney))
{
return;
}
if (ActiveVote == null)
{
starter.SetVote(VoteType.TransferMoney, 2);
@@ -156,6 +172,31 @@ namespace Barotrauma
}
}
private bool ShouldRejectVote(Client sender, VoteType voteType)
{
if (rejectedVoteTimes.ContainsKey(sender))
{
TimeSpan remainingCooldown = (rejectedVoteTimes[sender].time + rejectedVoteCooldown) - DateTime.Now;
if (rejectedVoteTimes[sender].voteType == voteType &&
remainingCooldown.TotalSeconds > 0)
{
GameMain.Server.SendDirectChatMessage(
TextManager.FormatServerMessage("voterejectedpleasewait", ("[time]", ((int)remainingCooldown.TotalSeconds).ToString())),
sender, ChatMessageType.ServerMessageBox);
return true;
}
}
return false;
}
protected void RegisterRejectedVote(IVote vote)
{
if (vote.VoteStarter != null)
{
rejectedVoteTimes[vote.VoteStarter] = (vote.VoteType, DateTime.Now);
}
}
public void Update(float deltaTime)
{
if (ActiveVote == null) { return; }
@@ -227,7 +268,6 @@ namespace Barotrauma
GameServer.Log(GameServer.ClientLogName(sender) + (ready ? " is ready to start the game." : " is not ready to start the game."), ServerLog.MessageType.ServerMessage);
}
break;
case VoteType.PurchaseAndSwitchSub:
case VoteType.PurchaseSub:
case VoteType.SwitchSub:
@@ -240,18 +280,25 @@ namespace Barotrauma
int amount = inc.ReadInt32();
int fromClientId = inc.ReadByte();
int toClientId = inc.ReadByte();
pendingVotes.Enqueue(new TransferVote(sender,
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
amount,
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
if (!ShouldRejectVote(sender, voteType))
{
pendingVotes.Enqueue(new TransferVote(sender,
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
amount,
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
}
}
else
{
string subName = inc.ReadString();
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
if (!ShouldRejectVote(sender, voteType))
{
StartSubmarineVote(subInfo, voteType, sender);
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign &&
(campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
{
StartSubmarineVote(subInfo, voteType, sender);
}
}
}
}