Release 1.9.7.0 - Summer Update 2025

This commit is contained in:
Regalis11
2025-06-17 16:38:11 +03:00
parent 22227f13e5
commit ea5a2bc693
297 changed files with 7344 additions and 2421 deletions
@@ -673,6 +673,7 @@ namespace Barotrauma
{
msg.WriteUInt32(CauseOfDeath.Affliction.UintIdentifier);
}
msg.WriteUInt16(CauseOfDeath.Killer?.ID ?? NullEntityID);
msg.WriteBoolean(forceAfflictionData);
if (forceAfflictionData)
{
@@ -2107,9 +2107,33 @@ namespace Barotrauma
"freecam",
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
client.UsingFreeCam = true;
GameMain.Server.SetClientCharacter(client, null);
client.SpectateOnly = true;
if (client.UsingFreeCam)
{
// Exiting freecam - try to return to previous character
Character prevCharacter = null;
if (client.PreviousCharacter != null && client.PreviousCharacter.TryGetTarget(out prevCharacter) &&
prevCharacter != null && !prevCharacter.IsDead && !prevCharacter.Removed)
{
GameMain.Server.SendConsoleMessage($"{client.Name}: Exiting freecam mode", client, Color.Yellow);
client.UsingFreeCam = false;
GameMain.Server.SetClientCharacter(client, prevCharacter);
client.SpectateOnly = false;
}
else
{
GameMain.Server.SendConsoleMessage($"{client.Name}: Could not regain control of the previous character (dead or removed).", client, Color.Red);
}
}
else
{
// Entering freecam - store current character ID
GameMain.Server.SendConsoleMessage($"{client.Name}: Entering freecam mode", client, Color.Yellow);
Character currentCharacter = client.Character;
client.PreviousCharacter = new WeakReference<Character>(currentCharacter);
client.UsingFreeCam = true;
client.SpectateOnly = true;
GameMain.Server.SetClientCharacter(client, null);
}
}
);
@@ -92,6 +92,7 @@ namespace Barotrauma
purchasedHullRepairs = value;
PurchasedHullRepairsInLatestSave |= value;
IncrementLastUpdateIdForFlag(NetFlags.Misc);
DebugConsole.NewMessage("Set PurchasedHullRepairs to " + PurchasedHullRepairs, Color.Cyan);
}
}
public override bool PurchasedLostShuttles
@@ -861,6 +862,13 @@ namespace Barotrauma
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
if (purchasedUpgradeCount > 0 || purchasedItemSwapCount > 0)
{
//if the client attempted to purchase something, increment flag regardless of whether the upgrades were actually purchased or not
//so we can sync the correct state in case the client incorrectly assumed they can buy something (e.g. lost permissions just as they were purchasing)
IncrementLastUpdateIdForFlag(NetFlags.UpgradeManager);
}
int hullRepairCost = GetHullRepairCost();
int itemRepairCost = GetItemRepairCost();
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
@@ -1100,7 +1108,7 @@ namespace Barotrauma
var characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
foreach (var (prefab, category, _) in purchasedUpgrades)
{
UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
UpgradeManager.TryPurchaseUpgrade(prefab, category, client: sender);
// unstable logging
int price = prefab.Price.GetBuyPrice(prefab, UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation, characterList);
@@ -1111,7 +1119,7 @@ namespace Barotrauma
{
if (purchasedItemSwap.ItemToInstall == null)
{
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, client: sender);
}
else
{
@@ -1502,11 +1510,13 @@ namespace Barotrauma
{
element.Add(new XAttribute("campaignid", CampaignID));
XElement modeElement = new XElement("MultiPlayerCampaign",
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("purchasedlostshuttles", PurchasedLostShuttlesInLatestSave),
new XAttribute("purchasedhullrepairs", PurchasedHullRepairsInLatestSave),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairsInLatestSave),
new XAttribute("cheatsenabled", CheatsEnabled));
DebugConsole.NewMessage("Saved PurchasedHullRepairs: "+ PurchasedHullRepairs+" (in last save "+PurchasedHullRepairsInLatestSave+")", Color.Magenta);
modeElement.Add(Settings.Save());
modeElement.Add(SaveStats());
if (GameMain.Server?.TraitorManager is TraitorManager traitorManager)
@@ -1520,6 +1530,11 @@ namespace Barotrauma
modeElement.Add(GameMain.GameSession?.EventManager.Save());
}
foreach (Identifier unlockedRecipe in GameMain.GameSession.UnlockedRecipes)
{
modeElement.Add(new XElement("unlockedrecipe", new XAttribute("identifier", unlockedRecipe)));
}
CampaignMetadata?.Save(modeElement);
Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement);
@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.WriteRangedSingle(Health, 0f, (float)MaxHealth, 8);
msg.WriteRangedSingle(Health, 0f, (float)MaxWater, 8);
if (TryExtractEventData(extraData, out EventData eventData))
{
int offset = eventData.Offset;
@@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components
{
base.ServerEventWrite(msg, c, extraData);
bool writeAttachData = attachable && body != null;
bool writeAttachData = attachable && originalBody != null;
msg.WriteBoolean(writeAttachData);
if (!writeAttachData) { return; }
@@ -22,8 +22,8 @@ namespace Barotrauma.Items.Components
}
msg.WriteBoolean(Attached);
msg.WriteSingle(body.SimPosition.X);
msg.WriteSingle(body.SimPosition.Y);
msg.WriteSingle(originalBody.SimPosition.X);
msg.WriteSingle(originalBody.SimPosition.Y);
msg.WriteUInt16(item.Submarine?.ID ?? Entity.NullEntityID);
msg.WriteUInt16(attacherId);
}
@@ -40,6 +40,9 @@ namespace Barotrauma.Items.Components
Drop(false, null);
item.SetTransform(simPosition, 0.0f, findNewHull: false);
//don't find the new hull in SetTransform, because that'd also potentially change the submarine (teleport the item outside if it's attached outside)
//instead just find the hull, so the item is considered to be in the right hull
item.CurrentHull = Hull.FindHull(item.WorldPosition, item.CurrentHull);
AttachToWall();
OnUsed.Invoke(new ItemUseInfo(item, c.Character));
@@ -0,0 +1,33 @@
#nullable enable
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
internal partial class PowerDistributor : PowerTransfer, IServerSerializable, IClientSerializable
{
#region Networking
public void ServerEventRead(IReadMessage msg, Client c)
{
SharedEventRead(msg, out EventType eventType, out PowerGroup powerGroup, out string newName, out float newRatio);
if (item.CanClientAccess(c))
{
switch (eventType)
{
case EventType.NameChange:
powerGroup.Name = newName;
break;
case EventType.RatioChange:
powerGroup.SupplyRatio = newRatio;
GameServer.Log($"{GameServer.CharacterLogName(c.Character)} changed supply ratio of power group \"{powerGroup.Name}\" to \"{powerGroup.SupplyRatio}\"", ServerLog.MessageType.ItemInteraction);
break;
}
}
item.CreateServerEvent(this, new EventData(powerGroup, eventType));
}
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData? extraData = null) => SharedEventWrite(msg, extraData);
#endregion
}
}
@@ -274,6 +274,7 @@ namespace Barotrauma
msg.WriteByte(originalSlotIndex < 0 ? (byte)255 : (byte)originalSlotIndex);
}
msg.WriteBoolean(OnInsertedEffectsAppliedOnPreviousRound);
msg.WriteByte(body == null ? (byte)0 : (byte)body.BodyType);
msg.WriteBoolean(SpawnedInCurrentOutpost);
msg.WriteBoolean(AllowStealing);
@@ -30,8 +30,8 @@ namespace Barotrauma
//don't create updates if all clients are very far from the hull
float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance;
if (!GameMain.Server.ConnectedClients.Any(c =>
c.Character != null &&
Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr))
(c.Character != null && Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr) ||
(c.SpectatePos != null && Vector2.DistanceSquared(c.SpectatePos.Value, WorldPosition) < hullUpdateDistanceSqr)) )
{
return;
}
@@ -76,6 +76,11 @@ namespace Barotrauma
}
}
public void ForceStatusUpdate()
{
statusUpdateTimer = NetConfig.SparseHullUpdateInterval;
}
public void CreateStatusEvent()
{
@@ -137,6 +142,12 @@ namespace Barotrauma
WaterVolume = newWaterVolume;
if (newFireSources.Length != FireSources.Count)
{
//number of fire sources has changed, force a network update
ForceStatusUpdate();
}
for (int i = 0; i < newFireSources.Length; i++)
{
Vector2 pos = newFireSources[i].Position;
@@ -166,9 +166,7 @@ namespace Barotrauma.Networking
}
else
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendDirectChatMessage(denyMsg, c);
BlockBySpamFilter();
}
flaggedAsSpam = true;
return;
@@ -178,14 +176,20 @@ namespace Barotrauma.Networking
if (c.ChatSpamTimer > 0.0f && !isSpamExempt)
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendDirectChatMessage(denyMsg, c);
BlockBySpamFilter();
flaggedAsSpam = true;
return;
}
flaggedAsSpam = false;
void BlockBySpamFilter()
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.BlockedBySpamFilter, null);
c.ChatSpamTimer = BlockedBySpamFilterTime;
GameMain.Server.SendDirectChatMessage(denyMsg, c);
GameServer.Log(c.Name + " blocked by spam filter", ServerLog.MessageType.ServerMessage);
}
}
public int EstimateLengthBytesServer(Client c)
@@ -138,6 +138,8 @@ namespace Barotrauma.Networking
get { return kickVoters.Count; }
}
public WeakReference<Character> PreviousCharacter;
partial void InitProjSpecific()
{
kickVoters = new List<Client>();
@@ -4222,6 +4222,9 @@ namespace Barotrauma.Networking
public void GiveAchievement(Client client, Identifier achievementIdentifier)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
DebugConsole.NewMessage($"Attempting to give the achievement {achievementIdentifier} to {client.Name}...");
client.GivenAchievements.Add(achievementIdentifier);
IWriteMessage msg = new WriteOnlyMessage();
@@ -4231,6 +4234,17 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void UnlockRecipe(Identifier identifier)
{
foreach (var client in connectedClients)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ServerPacketHeader.UNLOCKRECIPE);
msg.WriteIdentifier(identifier);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
}
public void IncrementStat(Client client, AchievementStat stat, int amount)
{
IWriteMessage msg = new WriteOnlyMessage();