Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fly, use rotating buffer over list to store last 5 positions. #20140

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
146 changes: 146 additions & 0 deletions OpenRA.Game/Primitives/RingBuffer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

using System;
using System.Collections.Generic;

namespace OpenRA.Primitives
{
/// <summary>Fixed size rorating buffer backed by an array.</summary>
public class RingBuffer<T> : ICollection<T>, IEnumerable<T>
{
readonly IComparer<T> comparer;
readonly T[] values;
int start;

public int Capacity => values.Length;
public int Count { get; private set; }
public bool IsReadOnly => false;

public RingBuffer(int capacity, IComparer<T> comparer)
{
this.comparer = comparer;
values = new T[capacity];
start = 0;
Count = 0;
}

public RingBuffer(int capacity)
: this(capacity, Comparer<T>.Default) { }

public void Add(T value)
{
values[(start + Count) % values.Length] = value;
if (Count < values.Length)
Count++;
else
start = (start + 1) % values.Length;
}

public void Clear()
{
Array.Clear(values, 0, values.Length);
start = 0;
Count = 0;
anvilvapre marked this conversation as resolved.
Show resolved Hide resolved
}

public bool Contains(T value)
{
var capacity = values.Length;
var end = start + Count;
for (var i = start; i < end; ++i)
if (comparer.Compare(values[i % capacity], value) == 0)
return true;
abcdefg30 marked this conversation as resolved.
Show resolved Hide resolved

return false;
}

public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array));

if (arrayIndex < 0)
throw new ArgumentNullException(nameof(arrayIndex));

if (arrayIndex + Count >= array.Length)
throw new ArgumentException("Invalid array capacity");

var destinationIndex = arrayIndex;
var end = start + Count;
var capacity = values.Length;
for (var i = start; i < end; ++i)
array[destinationIndex++] = values[i % capacity];
}

public bool Remove(T value)
{
var capacity = values.Length;
var end = start + Count;
for (var i = start; i < end; ++i)
anvilvapre marked this conversation as resolved.
Show resolved Hide resolved
{
if (comparer.Compare(values[i % capacity], value) == 0)
{
end--;
for (var j = i; j < end; ++j)
values[j % capacity] = values[(j + 1) % capacity];

Count--;
return true;
}
}

return false;
}

public T this[int pos]
{
get => values[(start + pos) % values.Length];

set
{
if (pos >= Count)
throw new ArgumentException($"Index out of bounds: {pos}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't check this for get.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it seemed reasonable, to avoid the overhead.


values[(start + pos) % values.Length] = value;
}
}

public T First()
{
if (Count == 0)
throw new ArgumentException("Empty buffer");

return values[start];
}

public T Last()
{
if (Count == 0)
throw new ArgumentException("Empty buffer");

return values[(start + Count - 1) % values.Length];
}

public IEnumerator<T> GetEnumerator()
{
var initState = start + Count;
for (var i = 0; i < Count; i++)
{
if (start + Count != initState)
throw new InvalidOperationException("Collection was modified; enumeration operation may not execute");
yield return values[(start + i) % values.Length];
}
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
}
2 changes: 1 addition & 1 deletion OpenRA.Game/WAngle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public WAngle(int a)
public static WAngle operator -(WAngle a) { return new WAngle(-a.Angle); }

public static bool operator ==(WAngle me, WAngle other) { return me.Angle == other.Angle; }
public static bool operator !=(WAngle me, WAngle other) { return !(me == other); }
public static bool operator !=(WAngle me, WAngle other) { return me.Angle != other.Angle; }

public override int GetHashCode() { return Angle.GetHashCode(); }

Expand Down
36 changes: 11 additions & 25 deletions OpenRA.Mods.Common/Activities/Air/Fly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#endregion

using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
Expand All @@ -29,7 +28,7 @@ public class Fly : Activity
Target target;
Target lastVisibleTarget;
bool useLastVisibleTarget;
readonly List<WPos> positionBuffer = new();
readonly RingBuffer<WPos> previousPositions = new(5);

public Fly(Actor self, in Target t, WDist nearEnough, WPos? initialTargetPosition = null, Color? targetLineColor = null)
: this(self, t, initialTargetPosition, targetLineColor)
Expand Down Expand Up @@ -63,13 +62,10 @@ public Fly(Actor self, in Target t, WPos? initialTargetPosition = null, Color? t
public static void FlyTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, in WVec moveOverride, bool idleTurn = false)
{
var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
var move = aircraft.Info.CanSlide ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing);
if (moveOverride != WVec.Zero)
move = moveOverride;
var move = moveOverride != WVec.Zero ? moveOverride : (aircraft.Info.CanSlide ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing));

var oldFacing = aircraft.Facing;
var turnSpeed = aircraft.GetTurnSpeed(idleTurn);
aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, turnSpeed);
aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.GetTurnSpeed(idleTurn));

var roll = idleTurn ? aircraft.Info.IdleRoll ?? aircraft.Info.Roll : aircraft.Info.Roll;
if (roll != WAngle.Zero)
Expand Down Expand Up @@ -104,22 +100,16 @@ public static void FlyTick(Actor self, Aircraft aircraft, WAngle desiredFacing,
// Should only be used for vertical-only movement, usually VTOL take-off or land. Terrain-induced altitude changes should always be handled by FlyTick.
public static bool VerticalTakeOffOrLandTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, bool idleTurn = false)
{
var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
var move = WVec.Zero;

var turnSpeed = idleTurn ? aircraft.IdleTurnSpeed ?? aircraft.TurnSpeed : aircraft.TurnSpeed;
aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, turnSpeed);

if (dat != desiredAltitude)
{
var maxDelta = aircraft.Info.AltitudeVelocity.Length;
var deltaZ = (desiredAltitude.Length - dat.Length).Clamp(-maxDelta, maxDelta);
move += new WVec(0, 0, deltaZ);
}
else
var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
if (dat == desiredAltitude)
return false;

aircraft.SetPosition(self, aircraft.CenterPosition + move);
var maxDelta = aircraft.Info.AltitudeVelocity.Length;
var deltaZ = (desiredAltitude.Length - dat.Length).Clamp(-maxDelta, maxDelta);
aircraft.SetPosition(self, aircraft.CenterPosition + new WVec(0, 0, deltaZ));
return true;
}

Expand Down Expand Up @@ -203,8 +193,7 @@ public override bool Tick(Actor self)

// HACK: Consider ourselves blocked if we have moved by less than 64 WDist in the last five ticks
// Stop if we are blocked and close enough
if (positionBuffer.Count >= 5 && (positionBuffer.Last() - positionBuffer[0]).LengthSquared < 4096 &&
delta.HorizontalLengthSquared <= nearEnough.LengthSquared)
if (previousPositions.Count == previousPositions.Capacity && (previousPositions.First() - previousPositions.Last()).LengthSquared < 4096 && delta.HorizontalLengthSquared <= nearEnough.LengthSquared)
return true;

// The next move would overshoot, so consider it close enough or set final position if we CanSlide
Expand Down Expand Up @@ -253,11 +242,8 @@ public override bool Tick(Actor self)
desiredFacing = aircraft.Facing;
}

positionBuffer.Add(self.CenterPosition);
if (positionBuffer.Count > 5)
positionBuffer.RemoveAt(0);

FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude);
previousPositions.Add(self.CenterPosition);
FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, WVec.Zero);

return false;
}
Expand Down
17 changes: 12 additions & 5 deletions OpenRA.Mods.Common/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ public static class Util
public static int TickFacing(int facing, int desiredFacing, int rot)
{
var leftTurn = (facing - desiredFacing) & 0xFF;
if (leftTurn < rot)
return desiredFacing & 0xFF;

var rightTurn = (desiredFacing - facing) & 0xFF;
if (Math.Min(leftTurn, rightTurn) < rot)
if (rightTurn < rot)
return desiredFacing & 0xFF;
else if (rightTurn < leftTurn)

if (rightTurn < leftTurn)
return (facing + rot) & 0xFF;
else
return (facing - rot) & 0xFF;

return (facing - rot) & 0xFF;
}

/// <summary>
Expand All @@ -46,8 +50,11 @@ public static int TickFacing(int facing, int desiredFacing, int rot)
public static WAngle TickFacing(WAngle facing, WAngle desiredFacing, WAngle step)
{
var leftTurn = (facing - desiredFacing).Angle;
if (leftTurn < step.Angle)
return desiredFacing;

var rightTurn = (desiredFacing - facing).Angle;
if (leftTurn < step.Angle || rightTurn < step.Angle)
if (rightTurn < step.Angle)
return desiredFacing;

return rightTurn < leftTurn ? facing + step : facing - step;
Expand Down