Compare commits

...

10 Commits

Author SHA1 Message Date
Pieter-Jan Briers
ef1fd698a0 Version: 174.0.2 2024-03-10 21:28:11 +01:00
Pieter-Jan Briers
a253c18e01 global.json force .NET 7
(cherry picked from commit 6211cf2e03)
2024-03-10 21:28:04 +01:00
Pieter-Jan Briers
4641634011 Version: 174.0.1 2024-03-10 20:50:30 +01:00
Pieter-Jan Briers
5d93618442 Backport 859f150404
(cherry picked from commit 24d5c26fa6)
(cherry picked from commit 688efac67b634c613539b783a9fb6e679948cd53)
2024-03-10 20:50:30 +01:00
ElectroJr
f5874ea402 Version: 174.0.0 2023-10-28 13:26:49 -04:00
metalgearsloth
b486ef885c Add NextAngle for System.Random (#4522) 2023-10-29 04:22:32 +11:00
metalgearsloth
9d55d77e48 Sprite GetFrame (#4528) 2023-10-29 04:21:52 +11:00
Leon Friedrich
5af3cb969c Move ActorComponent to shared (#4527) 2023-10-29 04:21:09 +11:00
metalgearsloth
429bc806dc Version: 173.1.0 2023-10-28 15:36:26 +11:00
metalgearsloth
81484699a8 Add chain shapes (#4523)
* Add chain shapes

* rar only

* that too

* weh

* a

* Update Robust.Shared/Physics/Dynamics/Contacts/Contact.cs

Co-authored-by: Moony <moony@hellomouse.net>

* Update Robust.Shared/Physics/Dynamics/Contacts/Contact.cs

Co-authored-by: Moony <moony@hellomouse.net>

---------

Co-authored-by: Moony <moony@hellomouse.net>
2023-10-28 15:29:30 +11:00
20 changed files with 363 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,6 +54,31 @@ END TEMPLATE-->
*None yet*
## 174.0.2
## 174.0.1
## 174.0.0
### Breaking changes
* ActorComponent has been moved to `Robust.Shared.Player` (namespace changed).
### New features
* Added `SpriteSystem.GetFrame()` method, which takes in an animated RSI and a time and returns a frame/texture.
* Added `IRobustRandom.NextAngle()`
## 173.1.0
### New features
* Add physics chain shapes from Box2D.
## 173.0.0
### Breaking changes

View File

@@ -366,6 +366,9 @@ namespace Robust.Client.Debugging
_debugPhysicsSystem.PointCount = 0;
}
worldHandle.UseShader(null);
worldHandle.SetTransform(Matrix3.Identity);
}
private void DrawScreen(DrawingHandleScreen screenHandle, OverlayDrawArgs args)
@@ -438,6 +441,9 @@ namespace Robust.Client.Debugging
}
}
}
screenHandle.UseShader(null);
screenHandle.SetTransform(Matrix3.Identity);
}
protected internal override void Draw(in OverlayDrawArgs args)
@@ -459,11 +465,26 @@ namespace Robust.Client.Debugging
{
switch (fixture.Shape)
{
case ChainShape cShape:
{
var count = cShape.Count;
var vertices = cShape.Vertices;
var v1 = Transform.Mul(xform, vertices[0]);
for (var i = 1; i < count; ++i)
{
var v2 = Transform.Mul(xform, vertices[i]);
worldHandle.DrawLine(v1, v2, color);
v1 = v2;
}
}
break;
case PhysShapeCircle circle:
var center = Transform.Mul(xform, circle.Position);
worldHandle.DrawCircle(center, circle.Radius, color);
break;
case EdgeShape edge:
{
var v1 = Transform.Mul(xform, edge.Vertex1);
var v2 = Transform.Mul(xform, edge.Vertex2);
worldHandle.DrawLine(v1, v2, color);
@@ -473,6 +494,7 @@ namespace Robust.Client.Debugging
worldHandle.DrawCircle(v1, 0.1f, color);
worldHandle.DrawCircle(v2, 0.1f, color);
}
}
break;
case PolygonShape poly:

View File

@@ -3,21 +3,17 @@ using System.Collections.Generic;
using Robust.Client.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
public sealed class AnimationPlayerSystem : EntitySystem, IPostInjectInit
public sealed class AnimationPlayerSystem : EntitySystem
{
private readonly List<Entity<AnimationPlayerComponent>> _activeAnimations = new();
private EntityQuery<MetaDataComponent> _metaQuery;
[Dependency] private readonly IComponentFactory _compFact = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
public override void Initialize()
{
@@ -123,13 +119,13 @@ namespace Robust.Client.GameObjects
if (compTrack.ComponentType == null)
{
_sawmill.Error("Attempted to play a component animation without any component specified.");
Log.Error("Attempted to play a component animation without any component specified.");
return;
}
if (!EntityManager.TryGetComponent(ent, compTrack.ComponentType, out var animatedComp))
{
_sawmill.Error(
Log.Error(
$"Attempted to play a component animation, but the entity {ToPrettyString(ent)} does not have the component to be animated: {compTrack.ComponentType}.");
return;
}
@@ -147,7 +143,7 @@ namespace Robust.Client.GameObjects
if (animatedComp.GetType().GetProperty(compTrack.Property) is { } property &&
property.HasCustomAttribute<AutoNetworkedFieldAttribute>())
{
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(ent)}");
Log.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(ent)}");
}
}
}
@@ -182,19 +178,18 @@ namespace Robust.Client.GameObjects
public void Stop(EntityUid uid, string key)
{
if (!TryComp<AnimationPlayerComponent>(uid, out var player)) return;
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
return;
player.PlayingAnimations.Remove(key);
}
public void Stop(EntityUid uid, AnimationPlayerComponent? component, string key)
{
if (!Resolve(uid, ref component, false)) return;
component.PlayingAnimations.Remove(key);
}
if (!Resolve(uid, ref component, false))
return;
void IPostInjectInit.PostInject()
{
_sawmill = _logManager.GetSawmill("anim");
component.PlayingAnimations.Remove(key);
}
}

View File

@@ -1,13 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using JetBrains.Annotations;
using Robust.Client.ComponentTrees;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -15,6 +18,7 @@ using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static Robust.Client.GameObjects.SpriteComponent;
namespace Robust.Client.GameObjects
@@ -183,6 +187,48 @@ namespace Robust.Client.GameObjects
{
_queuedFrameUpdate.Add(uid);
}
/// <summary>
/// Gets the specified frame for this sprite at the specified time.
/// </summary>
public Texture GetFrame(SpriteSpecifier spriteSpec, TimeSpan curTime)
{
Texture? sprite = null;
switch (spriteSpec)
{
case SpriteSpecifier.Rsi rsi:
var rsiActual = _resourceCache.GetResource<RSIResource>(rsi.RsiPath).RSI;
rsiActual.TryGetState(rsi.RsiState, out var state);
var frames = state!.GetFrames(RsiDirection.South);
var delays = state.GetDelays();
var totalDelay = delays.Sum();
var time = curTime.TotalSeconds % totalDelay;
var delaySum = 0f;
for (var i = 0; i < delays.Length; i++)
{
var delay = delays[i];
delaySum += delay;
if (time > delaySum)
continue;
sprite = frames[i];
break;
}
sprite ??= Frame0(spriteSpec);
break;
case SpriteSpecifier.Texture texture:
sprite = texture.GetTexture(_resourceCache);
break;
default:
throw new NotImplementedException();
}
return sprite;
}
}
/// <summary>

View File

@@ -17,7 +17,7 @@
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />

View File

@@ -1,13 +0,0 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.ViewVariables;
namespace Robust.Server.GameObjects
{
[RegisterComponent]
public sealed partial class ActorComponent : Component
{
[ViewVariables]
public ICommonSession PlayerSession { get; internal set; } = default!;
}
}

View File

@@ -15,6 +15,7 @@ using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Placement;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Robust.Server.Placement

View File

@@ -22,7 +22,9 @@
using System;
using System.Numerics;
using Microsoft.Extensions.ObjectPool;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Collision.Shapes;
namespace Robust.Shared.Physics.Collision;
@@ -35,6 +37,9 @@ internal sealed partial class CollisionManager : IManifoldManager
* Farseer had this as a static class with a ThreadStatic DistanceInput
*/
private ObjectPool<EdgeShape> _edgePool =
new DefaultObjectPool<EdgeShape>(new DefaultPooledObjectPolicy<EdgeShape>());
/// <summary>
/// Used for debugging contact points.
/// </summary>
@@ -131,6 +136,16 @@ internal sealed partial class CollisionManager : IManifoldManager
return numOut;
}
public EdgeShape GetContactEdge()
{
return _edgePool.Get();
}
public void ReturnEdge(EdgeShape edge)
{
_edgePool.Return(edge);
}
}
/// <summary>

View File

@@ -67,17 +67,15 @@ internal ref struct DistanceProxy
break;
case ShapeType.Chain:
throw new NotImplementedException();
/*
ChainShape chain = (ChainShape) shape;
Debug.Assert(0 <= index && index < chain.Vertices.Count);
Vertices.Clear();
Vertices.Add(chain.Vertices[index]);
Vertices.Add(index + 1 < chain.Vertices.Count ? chain.Vertices[index + 1] : chain.Vertices[0]);
ChainShape chain = (ChainShape) shape;
Debug.Assert(0 <= index && index < chain.Vertices.Length);
Radius = chain.Radius;
*/
Buffer._00 = chain.Vertices[index];
Buffer._01 = index + 1 < chain.Vertices.Length ? chain.Vertices[index + 1] : chain.Vertices[0];
Vertices = Buffer.AsSpan;
Radius = chain.Radius;
break;
case ShapeType.Edge:
EdgeShape edge = (EdgeShape) shape;

View File

@@ -4,6 +4,14 @@ namespace Robust.Shared.Physics.Collision;
internal interface IManifoldManager
{
// TODO: this SUCKS but it's better than an edge for every contact.
/// <summary>
/// Gets an edge from objectpool.
/// </summary>
EdgeShape GetContactEdge();
void ReturnEdge(EdgeShape edge);
bool TestOverlap(IPhysShape shapeA, int indexA, IPhysShape shapeB, int indexB, in Transform xfA, in Transform xfB);
void CollideCircles(ref Manifold manifold, PhysShapeCircle circleA, in Transform xfA,

View File

@@ -0,0 +1,177 @@
using System;
using System.Linq;
using System.Numerics;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Robust.Shared.Physics.Collision.Shapes;
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class ChainShape : IPhysShape
{
[DataField]
public Vector2[] Vertices = Array.Empty<Vector2>();
public int Count => Vertices.Length - 1;
public int ChildCount => Count - 1;
[DataField]
public float Radius { get; set; } = PhysicsConstants.PolygonRadius;
public ShapeType ShapeType => ShapeType.Chain;
[DataField]
public Vector2 PrevVertex;
[DataField]
public Vector2 NextVertex;
public void Clear()
{
Vertices = Array.Empty<Vector2>();
}
/// <summary>
/// Creates a circular loop with the specified radius.
/// </summary>
/// <param name="position"></param>
/// <param name="radius"></param>
/// <param name="outer">Does the chain block the outside (CCW) or inside (CW).</param>
/// <param name="count">How many multiply radius by count to get total edges.</param>
public void CreateLoop(Vector2 position, float radius, bool outer = true, float count = 16f)
{
int divisions = Math.Max(16,(int)(radius * count));
float arcLength = MathF.PI * 2 / divisions;
Span<Vector2> vertices = stackalloc Vector2[divisions];
for (int i = 0; i < divisions; i++)
{
var index = outer ? i : -i;
var startPos = new Vector2(MathF.Cos(arcLength * index) * radius, MathF.Sin(arcLength * index) * radius);
vertices[i] = startPos;
}
CreateLoop(vertices);
}
/// <summary>
/// Creates a chain loop with the specified vertices and count.
/// </summary>
public void CreateLoop(ReadOnlySpan<Vector2> vertices)
{
var count = vertices.Length;
DebugTools.Assert(Vertices.Length == 0);
DebugTools.Assert(count >= 3);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (count < 3)
{
return;
}
#if DEBUG
for (var i = 1; i < count; ++i)
{
var v1 = vertices[i-1];
var v2 = vertices[i];
// If the code crashes here, it means your vertices are too close together.
DebugTools.Assert((v1 - v2).LengthSquared() > PhysicsConstants.LinearSlop * PhysicsConstants.LinearSlop);
}
#endif
Array.Resize(ref Vertices, count + 1);
vertices.CopyTo(Vertices);
Vertices[count] = Vertices[0];
PrevVertex = Vertices[Count - 2];
NextVertex = Vertices[1];
}
public void CreateChain(ReadOnlySpan<Vector2> vertices, Vector2 prevVertex, Vector2 nextVertex)
{
var count = vertices.Length;
DebugTools.Assert(Vertices.Length == 0);
DebugTools.Assert(count >= 2);
#if DEBUG
for (var i = 1; i < count; ++i)
{
// If the code crashes here, it means your vertices are too close together.
DebugTools.Assert((vertices[i-1] - vertices[i]).LengthSquared() > PhysicsConstants.LinearSlop * PhysicsConstants.LinearSlop);
}
#endif
Array.Resize(ref Vertices, count);
vertices.CopyTo(Vertices);
PrevVertex = prevVertex;
NextVertex = nextVertex;
}
public EdgeShape GetChildEdge(ref EdgeShape edge, int index)
{
DebugTools.Assert(0 <= index && index < Count - 1);
// edge.Radius = Radius; (Let's be real we're never using this anyway).
Vector2 vertex0;
Vector2 vertex3;
if (index > 0)
{
vertex0 = Vertices[index - 1];
}
else
{
vertex0 = PrevVertex;
}
if (index < Count - 2)
{
vertex3 = Vertices[index + 2];
}
else
{
vertex3 = NextVertex;
}
edge.SetOneSided(vertex0, Vertices[index + 0], Vertices[index + 1], vertex3);
return edge;
}
public bool Equals(IPhysShape? other)
{
if (other is not ChainShape cShape)
return false;
return Equals(cShape);
}
public bool Equals(ChainShape otherChain)
{
return Count == otherChain.Count &&
NextVertex == otherChain.NextVertex &&
PrevVertex == otherChain.PrevVertex &&
Vertices.SequenceEqual(otherChain.Vertices);
}
public Box2 ComputeAABB(Transform transform, int childIndex)
{
DebugTools.Assert(childIndex < Count);
var i1 = childIndex;
var i2 = childIndex + 1;
if (i2 == Count)
{
i2 = 0;
}
var v1 = Transform.Mul(transform, Vertices[i1]);
var v2 = Transform.Mul(transform, Vertices[i2]);
var lower = Vector2.Min(v1, v2);
var upper = Vector2.Max(v1, v2);
var r = new Vector2(Radius, Radius);
return new Box2(lower - r, upper + r);
}
}

View File

@@ -287,19 +287,23 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
_manifoldManager.CollideEdgeAndPolygon(ref manifold, (EdgeShape) FixtureA!.Shape, transformA, (PolygonShape) FixtureB!.Shape, transformB);
break;
case ContactType.ChainAndCircle:
throw new NotImplementedException();
/*
ChainShape chain = (ChainShape)FixtureA.Shape;
chain.GetChildEdge(_edge, ChildIndexA);
Collision.CollisionManager.CollideEdgeAndCircle(ref manifold, _edge, ref transformA, (CircleShape)FixtureB.Shape, ref transformB);
*/
{
var chain = (ChainShape) FixtureA!.Shape;
var edge = _manifoldManager.GetContactEdge();
chain.GetChildEdge(ref edge, ChildIndexA);
_manifoldManager.CollideEdgeAndCircle(ref manifold, edge, in transformA, (PhysShapeCircle) FixtureB!.Shape, in transformB);
_manifoldManager.ReturnEdge(edge);
break;
}
case ContactType.ChainAndPolygon:
throw new NotImplementedException();
/*
ChainShape loop2 = (ChainShape)FixtureA.Shape;
loop2.GetChildEdge(_edge, ChildIndexA);
Collision.CollisionManager.CollideEdgeAndPolygon(ref manifold, _edge, ref transformA, (PolygonShape)FixtureB.Shape, ref transformB);
*/
{
var loop2 = (ChainShape) FixtureA!.Shape;
var edge = _manifoldManager.GetContactEdge();
loop2.GetChildEdge(ref edge, ChildIndexA);
_manifoldManager.CollideEdgeAndPolygon(ref manifold, edge, in transformA, (PolygonShape) FixtureB!.Shape, in transformB);
_manifoldManager.ReturnEdge(edge);
break;
}
case ContactType.Circle:
_manifoldManager.CollideCircles(ref manifold, (PhysShapeCircle) FixtureA!.Shape, in transformA, (PhysShapeCircle) FixtureB!.Shape, in transformB);
break;

View File

@@ -16,6 +16,7 @@ namespace Robust.Shared.Physics.Systems
{
switch (shape)
{
case ChainShape:
case EdgeShape:
return false;
case PhysShapeAabb aabb:
@@ -49,6 +50,11 @@ namespace Robust.Shared.Physics.Systems
// we can just cut out the middle-man
switch (shape)
{
case ChainShape:
data.Mass = 0f;
data.Center = Vector2.Zero;
data.I = 0f;
break;
case EdgeShape edge:
data.Mass = 0.0f;
data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f;
@@ -153,6 +159,11 @@ namespace Robust.Shared.Physics.Systems
// we can just cut out the middle-man
switch (shape)
{
case ChainShape:
data.Mass = 0f;
data.Center = Vector2.Zero;
data.I = 0f;
break;
case EdgeShape edge:
data.Mass = 0.0f;
data.Center = (edge.Vertex1 + edge.Vertex2) * 0.5f;

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.Player;
[RegisterComponent]
public sealed partial class ActorComponent : Component
{
[ViewVariables]
public ICommonSession PlayerSession { get; internal set; } = default!;
}

View File

@@ -28,7 +28,7 @@ public interface IRobustRandom
double NextDouble(double minValue, double maxValue) => NextDouble() * (maxValue - minValue) + minValue;
void NextBytes(byte[] buffer);
public Angle NextAngle() => NextFloat() * MathHelper.Pi * 2;
public Angle NextAngle() => NextFloat() * MathF.Tau;
public Angle NextAngle(Angle minValue, Angle maxValue) => NextFloat() * (maxValue - minValue) + minValue;
public Angle NextAngle(Angle maxValue) => NextFloat() * maxValue;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Collections;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.Shared.Random
@@ -63,6 +64,8 @@ namespace Robust.Shared.Random
return element;
}
public static Angle NextAngle(this System.Random random) => NextFloat(random) * MathF.Tau;
public static float NextFloat(this IRobustRandom random)
{
// This is pretty much the CoreFX implementation.

View File

@@ -19,7 +19,7 @@
<PackageReference Include="Linguini.Bundle" Version="0.1.3" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
</ItemGroup>
<ItemGroup>

View File

@@ -5,7 +5,6 @@ using System.Reflection;
using NUnit.Framework;
using Robust.Client.ComponentTrees;
using Robust.Client.GameObjects;
using Robust.Server.Containers;
using Robust.Server.Debugging;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
@@ -13,7 +12,6 @@ using Robust.Server.Physics;
using Robust.Shared.Configuration;
using Robust.Shared.Containers;
using Robust.Shared.ContentPack;
using Robust.Shared.Debugging;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -23,7 +21,7 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Player;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
using MapSystem = Robust.Server.GameObjects.MapSystem;

6
global.json Normal file
View File

@@ -0,0 +1,6 @@
{
"sdk": {
"version": "7.0.100",
"rollForward": "latestFeature"
}
}