Files
wwdpublic/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs
VMSolidus af499da8af Last Bugfixes For Space Wind (#2089)
# Description

This PR fixes all remaining bugs with Space Wind, while providing
extremely significant performance improvements by way of aggressive
early exits, and also completely reworking how pressure moved entities
are tracked(The server now caches entities under active air movements).
I haven't profiled it yet, but the performance improvements are
extremely noticeable on the machine I'm running in comparison to before,
which is particularly noteworthy since previous versions of space wind
caused significant frame drops in testing on my monster rig. While this
new update to space wind straight up doesn't even budge the tickrate on
my machine anymore.

I had to also rip out some of Monstermos' Guts, and deprecate the
tileripping system. In the future, Tile-ripping will be handled by MAS.
Also, MAS outright no longer requires Monstermos to work, and can
operate entirely off of the much cheaper LINDA system. However, while
LINDA is cheaper, Monstermos is needed for handling "Extra violent air".

Eventually I can do away with Monstermos entirely, and have it replaced
with an airflow system that outright does not require pathfinding
algorithms.

# Changelog

🆑
- fix: Fixed a bug where items thrown by space wind could remain in air
for as long as 500 seconds. Items thrown by space wind can now remain in
the air for a maximum of 2 seconds past the last time they were thrown.
- fix: DRAMATICALLY IMPROVED SPACE WIND PERFORMANCE.
- tweak: ShowAtmos command now shows vectors representing the exact
direction space wind at a given tile is trying to throw objects! The
length of the lines shown directly correspond to how powerful the flow
of air is at that tile. Shorter lines = shorter throws. Longer lines =
more powerful throws. These are also no longer restricted to compass
directions, and can point in arbitrarily any direction on a circle.
- tweak: Space Wind now no longer has Monstermos as a hard requirement.
It'll be as weak as a toddler without it, but if you're running your
server on a toaster, you can turn off Monstermos and still have working
space wind.
- tweak: Wreckages in space are now never airtight. I'm sorry, but this
was basically the cost I had to pay in exchange for making Atmos no
longer even on the top 20 systems for server tickrate cost.

(cherry picked from commit 2fcc806423a259fd75977b78350c2232a823739b)
2025-03-29 16:45:43 +03:00

166 lines
7.5 KiB
C#

using Content.Server.Atmos.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Humanoid;
using Content.Shared.Maps;
using Content.Shared.Projectiles;
using Content.Shared.Throwing;
using Robust.Shared.Audio;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using System.Numerics;
namespace Content.Server.Atmos.EntitySystems;
public sealed partial class AtmosphereSystem
{
private void UpdateHighPressure(float frameTime)
{
base.Update(frameTime);
var pressureQuery = EntityQueryEnumerator<MovedByPressureComponent, PhysicsComponent>();
while (pressureQuery.MoveNext(out var uid, out var pressure, out var physics))
{
if (!pressure.Throwing || _gameTiming.CurTime < pressure.ThrowingCutoffTarget)
continue;
if (TryComp(uid, out ThrownItemComponent? thrown))
{
_thrown.LandComponent(uid, thrown, physics, true);
_thrown.StopThrow(uid, thrown);
}
_physics.SetBodyStatus(uid, physics, BodyStatus.OnGround);
_physics.SetSleepingAllowed(uid, physics, true);
pressure.Throwing = false;
}
}
private void HighPressureMovements(Entity<GridAtmosphereComponent> gridAtmosphere,
TileAtmosphere tile,
EntityQuery<PhysicsComponent> bodies,
EntityQuery<TransformComponent> xforms,
EntityQuery<MovedByPressureComponent> pressureQuery,
EntityQuery<MetaDataComponent> metas,
EntityQuery<ProjectileComponent> projectileQuery,
double gravity)
{
var atmosComp = gridAtmosphere.Comp;
var oneAtmos = Atmospherics.OneAtmosphere;
// No atmos yeets, return early.
if (!SpaceWind
|| !gridAtmosphere.Comp.SpaceWindSimulation // Is the grid marked as exempt from space wind?
|| tile.Air is null || tile.Space // No Air Checks. Pressure differentials can't exist in a hard vacuum.
|| tile.Air.Pressure <= atmosComp.PressureCutoff // Below 5kpa(can't throw a base item)
|| oneAtmos - atmosComp.PressureCutoff <= tile.Air.Pressure
&& tile.Air.Pressure <= oneAtmos + atmosComp.PressureCutoff // Check within 5kpa of default pressure.
|| !TryComp(gridAtmosphere.Owner, out MapGridComponent? mapGrid)
|| !_mapSystem.TryGetTileRef(gridAtmosphere.Owner, mapGrid, tile.GridIndices, out var tileRef))
return;
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
if (!tileDef.SimulatedTurf)
return;
var partialFrictionComposition = gravity * tileDef.MobFrictionNoInput;
var pressureVector = GetPressureVectorFromTile(gridAtmosphere, tile);
if (!pressureVector.IsValid())
return;
tile.LastPressureDirection = pressureVector;
// Calculate this HERE so that we aren't running the square root of a whole Newton vector per item.
var pVecLength = pressureVector.Length();
if (pVecLength <= 1) // Then guard against extremely small vectors.
return;
pressureVector *= SpaceWindStrengthMultiplier;
if (pVecLength > 15 && !tile.Hotspot.Valid && atmosComp.SpaceWindSoundCooldown == 0)
{
var coordinates = _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.GridIndices);
var volume = Math.Clamp(pVecLength / atmosComp.SpaceWindSoundDenominator, atmosComp.SpaceWindSoundMinVolume, atmosComp.SpaceWindSoundMaxVolume);
_audio.PlayPvs(atmosComp.SpaceWindSound, coordinates, AudioParams.Default.WithVariation(0.125f).WithVolume(volume));
}
if (atmosComp.SpaceWindSoundCooldown++ > atmosComp.SpaceWindSoundCooldownCycles)
atmosComp.SpaceWindSoundCooldown = 0;
// TODO: Deprecated for now, it sucks ass and I'm disassembling monstermos because it sucks. This'll be handled by Space Wind after I'm done whiteboarding better equations for it.
// - TCJ
// HandleDecompressionFloorRip(mapGrid, otherTile, otherTile.PressureDifference);
_entSet.Clear();
_lookup.GetLocalEntitiesIntersecting(tile.GridIndex, tile.GridIndices, _entSet, 0f);
foreach (var entity in _entSet)
{
// Ideally containers would have their own EntityQuery internally or something given recursively it may need to slam GetComp<T> anyway.
// Also, don't care about static bodies (but also due to collisionwakestate can't query dynamic directly atm).
if (!bodies.TryGetComponent(entity, out var body)
|| !pressureQuery.TryGetComponent(entity, out var pressure)
|| !pressure.Enabled
|| _containers.IsEntityInContainer(entity, metas.GetComponent(entity))
|| pressure.LastHighPressureMovementAirCycle >= gridAtmosphere.Comp.UpdateCounter)
continue;
// tl;dr YEET
ExperiencePressureDifference(
(entity, pressure),
gridAtmosphere.Comp.UpdateCounter,
pressureVector,
pVecLength,
partialFrictionComposition,
projectileQuery,
xforms.GetComponent(entity),
body);
}
}
// Called from AtmosphereSystem.LINDA.cs with SpaceWind CVar check handled there.
private void ConsiderPressureDifference(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) => gridAtmosphere.HighPressureDelta.Add(tile);
public void ExperiencePressureDifference(Entity<MovedByPressureComponent> ent,
int cycle,
Vector2 pressureVector,
float pVecLength,
double partialFrictionComposition,
EntityQuery<ProjectileComponent> projectileQuery,
TransformComponent? xform = null,
PhysicsComponent? physics = null)
{
var (uid, component) = ent;
if (!Resolve(uid, ref physics, false)
|| !Resolve(uid, ref xform)
|| physics.BodyType == BodyType.Static
|| physics.LinearVelocity.Length() >= SpaceWindMaxForce)
return;
var alwaysThrow = partialFrictionComposition == 0 || physics.BodyStatus == BodyStatus.InAir;
// Coefficient of static friction in Newtons (kg * m/s^2), which might not apply under certain conditions.
var coefficientOfFriction = partialFrictionComposition * physics.Mass;
coefficientOfFriction *= _standingSystem.IsDown(uid) ? 3 : 1;
if (HasComp<HumanoidAppearanceComponent>(ent))
pressureVector *= HumanoidThrowMultiplier;
if (!alwaysThrow && pVecLength < coefficientOfFriction)
return;
// Yes this technically increases the magnitude by a small amount... I detest having to swap between "World" and "Local" vectors.
// ThrowingSystem increments linear velocity by a given vector, but we have to do this anyways because reasons.
var velocity = _transformSystem.GetWorldRotation(uid).ToWorldVec() + pressureVector;
_sharedStunSystem.TryKnockdown(uid, TimeSpan.FromSeconds(SpaceWindKnockdownTime), false);
_throwing.TryThrow(uid, velocity, physics, xform, projectileQuery,
1, doSpin: physics.AngularVelocity < SpaceWindMaxAngularVelocity);
component.LastHighPressureMovementAirCycle = cycle;
component.Throwing = true;
component.ThrowingCutoffTarget = _gameTiming.CurTime + component.CutoffTime;
}
}