Removed LaneSpan, may be add later as diffrent movemnet system.
This commit is contained in:
1
GlobalSuppressions.cs.uid
Normal file
1
GlobalSuppressions.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://oaa3naabb0bi
|
||||||
1
src/Arch.Extended/Arch.Persistence/Binary.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Binary.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b22vip5klbxnq
|
||||||
1
src/Arch.Extended/Arch.Persistence/Json.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Json.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ddnds4ritwn2f
|
||||||
1
src/Arch.Extended/Arch.Persistence/Serializer.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Serializer.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://8hihhfy6hol
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dbb3dlqyskwqs
|
||||||
@@ -32,15 +32,13 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
|||||||
// Direction.Back,
|
// Direction.Back,
|
||||||
Width,
|
Width,
|
||||||
PortAccess.BiDirectional),
|
PortAccess.BiDirectional),
|
||||||
new BeltTEnd(ConveyorEnd.Start),
|
new BeltTEnd(ConveyorEnd.Start));
|
||||||
new LaneSpan(0, 1));
|
|
||||||
ConveyorLogic.EndPort = ConveyorLogic.CreatePort(
|
ConveyorLogic.EndPort = ConveyorLogic.CreatePort(
|
||||||
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
|
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
|
||||||
// Direction.Front,
|
// Direction.Front,
|
||||||
Width,
|
Width,
|
||||||
PortAccess.BiDirectional),
|
PortAccess.BiDirectional),
|
||||||
new BeltTEnd(ConveyorEnd.End),
|
new BeltTEnd(ConveyorEnd.End));
|
||||||
new LaneSpan(0, 1));
|
|
||||||
|
|
||||||
ConveyorLogic.OtherPorts = [
|
ConveyorLogic.OtherPorts = [
|
||||||
ConveyorLogic.CreatePort(
|
ConveyorLogic.CreatePort(
|
||||||
@@ -48,15 +46,13 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
|||||||
// Direction.Right,
|
// Direction.Right,
|
||||||
1,
|
1,
|
||||||
PortAccess.In),
|
PortAccess.In),
|
||||||
new BeltTOffset(ConveyorLogic.Length/2),
|
new BeltTOffset(ConveyorLogic.Length/2)),
|
||||||
LaneSpan.One),
|
|
||||||
ConveyorLogic.CreatePort(
|
ConveyorLogic.CreatePort(
|
||||||
new(VoxelTransform.RotateY90CCW(),
|
new(VoxelTransform.RotateY90CCW(),
|
||||||
// Direction.Left,
|
// Direction.Left,
|
||||||
1,
|
1,
|
||||||
PortAccess.In),
|
PortAccess.In),
|
||||||
new BeltTOffset(ConveyorLogic.Length/2),
|
new BeltTOffset(ConveyorLogic.Length/2))
|
||||||
LaneSpan.One)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public interface IMovementConveyor
|
|||||||
IBeltMovement GetMovementPolicy();
|
IBeltMovement GetMovementPolicy();
|
||||||
// IList<ConveyorSlice> Items { get; }
|
// IList<ConveyorSlice> Items { get; }
|
||||||
IOption<IBeltPort> GetPortFacing(IBeltPort slot);
|
IOption<IBeltPort> GetPortFacing(IBeltPort slot);
|
||||||
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, LaneSpan laneSpan, HashSet<IMovementConveyor>? visted = null);
|
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, HashSet<IMovementConveyor>? visted = null);
|
||||||
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan);
|
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT);
|
||||||
}
|
}
|
||||||
public interface IBeltMovement
|
public interface IBeltMovement
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ public sealed class IndividualMovement : IBeltMovement
|
|||||||
{
|
{
|
||||||
var itemRef = enumerator.Current;
|
var itemRef = enumerator.Current;
|
||||||
float maxMove = conveyor.SpeedMagnitude * delta;
|
float maxMove = conveyor.SpeedMagnitude * delta;
|
||||||
var space = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(), itemRef.Position, maxMove, LaneSpan.One);
|
var space = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(), itemRef.Position, maxMove);
|
||||||
|
|
||||||
|
|
||||||
// space -= ITEMSIZE;//AcountForSPacing
|
// space -= ITEMSIZE;//AcountForSPacing
|
||||||
@@ -85,7 +85,7 @@ public sealed class IndividualMovement : IBeltMovement
|
|||||||
{
|
{
|
||||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||||
if (facing.HasValue(out var slot) && slot.TryInsert(itemRef.Value, LaneSpan.One, 0))
|
if (facing.HasValue(out var slot) && slot.TryInsert(itemRef.Value, 0))
|
||||||
{
|
{
|
||||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||||
@@ -211,9 +211,8 @@ public sealed class StrictMovement : IBeltMovement
|
|||||||
if (enumerator.MoveNext())
|
if (enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
lastPos = enumerator.Current.Position;
|
lastPos = enumerator.Current.Position;
|
||||||
freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),lastPos,maxMove,LaneSpan.One,[]);
|
freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),lastPos,maxMove,[]);
|
||||||
}
|
}
|
||||||
var move = MathF.Min(maxMove, freeSpace.Distance);
|
|
||||||
if (lastPos > conveyor.Length || lastPos < 0)
|
if (lastPos > conveyor.Length || lastPos < 0)
|
||||||
{
|
{
|
||||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||||
@@ -223,7 +222,7 @@ public sealed class StrictMovement : IBeltMovement
|
|||||||
{
|
{
|
||||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
|
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, 0))
|
||||||
{
|
{
|
||||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||||
@@ -251,7 +250,7 @@ public sealed class StrictMovement : IBeltMovement
|
|||||||
{
|
{
|
||||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
|
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, 0))
|
||||||
{
|
{
|
||||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||||
@@ -268,31 +267,7 @@ public struct ConveyorSlice(IBeltItem item, float beltT = 0)
|
|||||||
{
|
{
|
||||||
public IBeltItem Item = item;
|
public IBeltItem Item = item;
|
||||||
public float BeltT = beltT;
|
public float BeltT = beltT;
|
||||||
public LaneSpan LaneSpan = LaneSpan.One;
|
public override readonly string ToString() => $"{Item}, {BeltT}";
|
||||||
public override string ToString() => $"{Item}, {BeltT}, {LaneSpan}";
|
|
||||||
|
|
||||||
}
|
|
||||||
public readonly struct LaneSpan : IEquatable<LaneSpan>
|
|
||||||
{
|
|
||||||
public readonly ushort Start;
|
|
||||||
public readonly ushort End;
|
|
||||||
public readonly int Width => End - Start;
|
|
||||||
public static readonly LaneSpan One = new(0, 1);
|
|
||||||
public static readonly LaneSpan Zero = new(0, 0);
|
|
||||||
public LaneSpan(ushort start, ushort end)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static bool OverLaps(LaneSpan a, LaneSpan b) => a.Start < b.End && b.Start < a.End;
|
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static LaneSpan Shifted(LaneSpan a, int amount) => checked(new LaneSpan((ushort)(a.Start + amount), (ushort)(a.End + amount)));
|
|
||||||
public static bool Encapsulates(LaneSpan a, LaneSpan b) => b.Start >= a.Start && b.End <= b.End;
|
|
||||||
public override string ToString() => $"(Start:{Start}, End:{End})";
|
|
||||||
public bool Equals(LaneSpan other) => Start == other.Start && End == other.End;
|
|
||||||
public static bool operator ==(LaneSpan a, LaneSpan b) => a.Equals(b);
|
|
||||||
public static bool operator !=(LaneSpan a, LaneSpan b) => !a.Equals(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BeltDirection : sbyte
|
public enum BeltDirection : sbyte
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
BeltDirection beltDirection,
|
BeltDirection beltDirection,
|
||||||
float itemBeltT,
|
float itemBeltT,
|
||||||
float maxDistToCheck,
|
float maxDistToCheck,
|
||||||
LaneSpan laneSpan,
|
|
||||||
HashSet<IMovementConveyor>? visited = null)
|
HashSet<IMovementConveyor>? visited = null)
|
||||||
{
|
{
|
||||||
if (beltDirection == BeltDirection.NotMoving)
|
if (beltDirection == BeltDirection.NotMoving)
|
||||||
@@ -120,11 +119,11 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
bool towardStart = beltDirection == BeltDirection.TowardStart;
|
bool towardStart = beltDirection == BeltDirection.TowardStart;
|
||||||
|
|
||||||
// 1️⃣ Try to find next item on this conveyor
|
// 1️⃣ Try to find next item on this conveyor
|
||||||
var nextItem = FindNextLocalItem(towardStart, itemBeltT, laneSpan);
|
var nextItem = FindNextLocalItem(towardStart, itemBeltT);
|
||||||
// GD.Print(nextItem);
|
// GD.Print(nextItem);
|
||||||
if (nextItem.HasValue(out var item))
|
if (nextItem.HasValue(out var item))
|
||||||
{
|
{
|
||||||
return new ItemBeltObstacle(MathF.Abs(itemBeltT - item.BeltT) - ITEMSIZE, item, laneSpan);
|
return new ItemBeltObstacle(MathF.Abs(itemBeltT - item.BeltT) - ITEMSIZE, item);
|
||||||
// return MathF.Abs(itemBeltT - item.BeltT);//TODO UseLaneSpan inadditon
|
// return MathF.Abs(itemBeltT - item.BeltT);//TODO UseLaneSpan inadditon
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +132,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
towardStart,
|
towardStart,
|
||||||
itemBeltT,
|
itemBeltT,
|
||||||
maxDistToCheck,
|
maxDistToCheck,
|
||||||
laneSpan,
|
|
||||||
visited
|
visited
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -142,8 +140,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
//ChatGPT Assisted
|
//ChatGPT Assisted
|
||||||
private IOption<ConveyorSlice> FindNextLocalItem(
|
private IOption<ConveyorSlice> FindNextLocalItem(
|
||||||
bool towardStart,
|
bool towardStart,
|
||||||
float itemBeltT,
|
float itemBeltT)
|
||||||
LaneSpan laneSpan)
|
|
||||||
{
|
{
|
||||||
var sequence = towardStart
|
var sequence = towardStart
|
||||||
? _items.EnumerateTowardStart(itemBeltT)
|
? _items.EnumerateTowardStart(itemBeltT)
|
||||||
@@ -168,7 +165,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
bool towardStart,
|
bool towardStart,
|
||||||
float itemBeltT,
|
float itemBeltT,
|
||||||
float maxDistToCheck,
|
float maxDistToCheck,
|
||||||
LaneSpan laneSpan,
|
|
||||||
HashSet<IMovementConveyor> visited)
|
HashSet<IMovementConveyor> visited)
|
||||||
{
|
{
|
||||||
var port = towardStart ? StartPort : EndPort;
|
var port = towardStart ? StartPort : EndPort;
|
||||||
@@ -190,13 +186,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
var o = GetPortFacing(port);
|
var o = GetPortFacing(port);
|
||||||
if (o.HasValue(out var otherport))
|
if (o.HasValue(out var otherport))
|
||||||
{
|
{
|
||||||
var mappedLane2 = MapLaneOrFail(port, otherport, laneSpan);
|
|
||||||
if (mappedLane2.HasValue(out var lane2))
|
|
||||||
{
|
|
||||||
return new PortBeltObstacle(distanceToBoundary, lane2, otherport);
|
|
||||||
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary+1,lane1,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
|
||||||
|
|
||||||
}
|
|
||||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||||
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary,laneSpan,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary,laneSpan,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
||||||
}
|
}
|
||||||
@@ -208,12 +197,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
var mappedLane = MapLaneOrFail(port, conveyorPort, laneSpan);
|
|
||||||
if (!mappedLane.HasValue(out var lane))
|
|
||||||
{
|
|
||||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextDirection = DirectionAwayFromEnd(end.End);
|
var nextDirection = DirectionAwayFromEnd(end.End);
|
||||||
var startT = end.End == ConveyorEnd.Start
|
var startT = end.End == ConveyorEnd.Start
|
||||||
? 0f
|
? 0f
|
||||||
@@ -231,7 +214,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
nextDirection,
|
nextDirection,
|
||||||
startT,
|
startT,
|
||||||
maxDistToCheck,
|
maxDistToCheck,
|
||||||
lane,
|
|
||||||
visited
|
visited
|
||||||
);
|
);
|
||||||
return recursiveDistance with { Distance = recursiveDistance.Distance + distanceToBoundary };
|
return recursiveDistance with { Distance = recursiveDistance.Distance + distanceToBoundary };
|
||||||
@@ -252,52 +234,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
: None<ConveyorPort>.Of());
|
: None<ConveyorPort>.Of());
|
||||||
}
|
}
|
||||||
public IOption<IBeltPort> GetPortFacing(IBeltPort slot) => slot.GetPortFacing(GridRegistry);
|
public IOption<IBeltPort> GetPortFacing(IBeltPort slot) => slot.GetPortFacing(GridRegistry);
|
||||||
private IOption<LaneSpan> MapLaneOrFail(
|
|
||||||
IBeltPort from,
|
|
||||||
IBeltPort to,
|
|
||||||
LaneSpan incoming)
|
|
||||||
{
|
|
||||||
// return None<LaneSpan>.Of();
|
|
||||||
bool mirror = false;
|
|
||||||
// full mapping of the source port
|
|
||||||
var mapped = from.MapLaneSpanToFacingPort(to);
|
|
||||||
|
|
||||||
if (mapped == LaneSpan.Zero)
|
|
||||||
return LaneSpan.Zero.ToOption();
|
|
||||||
|
|
||||||
int sourceWidth = from.Profile.Width;
|
|
||||||
int targetWidth = mapped.Width;
|
|
||||||
|
|
||||||
int startOffset = incoming.Start; // start relative to source port
|
|
||||||
int spanLength = incoming.Width;
|
|
||||||
|
|
||||||
int newStart;
|
|
||||||
if (mirror)
|
|
||||||
{
|
|
||||||
// mirrored: right side of incoming aligns with right side of mapped
|
|
||||||
newStart = mapped.End - (startOffset + spanLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// normal: left side of incoming aligns with left side of mapped
|
|
||||||
newStart = mapped.Start + startOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newEnd = newStart + spanLength;
|
|
||||||
|
|
||||||
// clamp to mapped range
|
|
||||||
newStart = Math.Max(mapped.Start, newStart);
|
|
||||||
newEnd = Math.Min(mapped.End, newEnd);
|
|
||||||
|
|
||||||
// GD.Print(incoming, new LaneSpan((ushort)newStart, (ushort)newEnd));
|
|
||||||
if (newStart >= newEnd)
|
|
||||||
{
|
|
||||||
// GD.Print("none");
|
|
||||||
return None<LaneSpan>.Of();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new LaneSpan((ushort)newStart, (ushort)newEnd).ToOption();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BeltDirection DirectionAwayFromEnd(ConveyorEnd conveyorEnd) => conveyorEnd switch { ConveyorEnd.Start => BeltDirection.TowardEnd, ConveyorEnd.End => BeltDirection.TowardStart, _ => throw new NotSupportedException() };
|
private BeltDirection DirectionAwayFromEnd(ConveyorEnd conveyorEnd) => conveyorEnd switch { ConveyorEnd.Start => BeltDirection.TowardEnd, ConveyorEnd.End => BeltDirection.TowardStart, _ => throw new NotSupportedException() };
|
||||||
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardEnd() => Items.EnumerateTowardEnd();
|
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardEnd() => Items.EnumerateTowardEnd();
|
||||||
@@ -323,25 +259,25 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan)
|
public ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT)
|
||||||
{
|
{
|
||||||
// var towardStartEnd = beltT is ItemConveyor.BeltTEnd end && end.End == ItemConveyor.ConveyorEnd.End;
|
// var towardStartEnd = beltT is ItemConveyor.BeltTEnd end && end.End == ItemConveyor.ConveyorEnd.End;
|
||||||
bool accept(IBeltItem item, float offset, LaneSpan lane)
|
bool accept(IBeltItem item, float offset)
|
||||||
{
|
{
|
||||||
if (beltT is BeltTEnd end)
|
if (beltT is BeltTEnd end)
|
||||||
{
|
{
|
||||||
var startBeltT = end.End == ConveyorEnd.Start ? 0 : Length;
|
var startBeltT = end.End == ConveyorEnd.Start ? 0 : Length;
|
||||||
var obstacle = GetDistanceToNextItem(end.End.Swap().DirectionTo(), startBeltT, offset, lane);
|
var obstacle = GetDistanceToNextItem(end.End.Swap().DirectionTo(), startBeltT, offset);
|
||||||
var distanceAllowed = obstacle.Distance;
|
var distanceAllowed = obstacle.Distance;
|
||||||
return ITEMSIZE < distanceAllowed;
|
return ITEMSIZE < distanceAllowed;
|
||||||
}
|
}
|
||||||
else if (beltT is BeltTOffset endOffset)
|
else if (beltT is BeltTOffset endOffset)
|
||||||
{
|
{
|
||||||
return HasClearance(endOffset.T, lane);
|
return HasClearance(endOffset.T);
|
||||||
}
|
}
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
bool tryInsert(IBeltItem item, float offset, LaneSpan laneSpan)
|
bool tryInsert(IBeltItem item, float offset)
|
||||||
{
|
{
|
||||||
if (beltT is BeltTEnd end)
|
if (beltT is BeltTEnd end)
|
||||||
{
|
{
|
||||||
@@ -362,7 +298,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
else if (beltT is BeltTOffset endOffset)
|
else if (beltT is BeltTOffset endOffset)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!HasClearance(endOffset.T, LaneSpan.One))
|
if (!HasClearance(endOffset.T))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -386,20 +322,18 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
tryInsert);
|
tryInsert);
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
private bool HasClearance(float centerT, LaneSpan span)
|
private bool HasClearance(float centerT)
|
||||||
{
|
{
|
||||||
var lower = GetDistanceToNextItem(
|
var lower = GetDistanceToNextItem(
|
||||||
BeltDirection.TowardStart,
|
BeltDirection.TowardStart,
|
||||||
centerT,
|
centerT,
|
||||||
ITEMSIZE,
|
ITEMSIZE
|
||||||
span
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var upper = GetDistanceToNextItem(
|
var upper = GetDistanceToNextItem(
|
||||||
BeltDirection.TowardEnd,
|
BeltDirection.TowardEnd,
|
||||||
centerT,
|
centerT,
|
||||||
ITEMSIZE,
|
ITEMSIZE
|
||||||
span
|
|
||||||
);
|
);
|
||||||
|
|
||||||
float lowerAllowed =
|
float lowerAllowed =
|
||||||
@@ -420,7 +354,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
|||||||
}
|
}
|
||||||
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
|
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
|
||||||
public record class BeltObstacle(float Distance);
|
public record class BeltObstacle(float Distance);
|
||||||
public record ItemBeltObstacle(float Distance, ConveyorSlice Item, LaneSpan LaneSpan) : BeltObstacle(Distance);
|
public record ItemBeltObstacle(float Distance, ConveyorSlice Item) : BeltObstacle(Distance);
|
||||||
public record BoundaryBeltObstacle(float DistanceToBoundary) : BeltObstacle(DistanceToBoundary);
|
public record BoundaryBeltObstacle(float DistanceToBoundary) : BeltObstacle(DistanceToBoundary);
|
||||||
public record PortBeltObstacle(float DistanceToBoundary, LaneSpan LaneSpan, IBeltPort BeltPort) : BoundaryBeltObstacle(DistanceToBoundary);
|
public record PortBeltObstacle(float DistanceToBoundary, IBeltPort BeltPort) : BoundaryBeltObstacle(DistanceToBoundary);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public partial class Balancer : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
|
|||||||
{
|
{
|
||||||
foreach (var portOther in OutputPorts)
|
foreach (var portOther in OutputPorts)
|
||||||
{
|
{
|
||||||
if (portOther.GetPortFacing(GridRegistry).Map(f => f.CanAccept(item, LaneSpan.One, 0)).Or(false))
|
if (portOther.GetPortFacing(GridRegistry).Map(f => f.CanAccept(item, 0)).Or(false))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ public partial class Balancer : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
|
|||||||
for (var i = 0; i < _outPuts.Count; i++)
|
for (var i = 0; i < _outPuts.Count; i++)
|
||||||
{
|
{
|
||||||
var facing = _outPuts[i].GetPortFacing(GridRegistry);
|
var facing = _outPuts[i].GetPortFacing(GridRegistry);
|
||||||
if (facing.HasValue(out var face) && face.CanAccept(item, LaneSpan.One, 0) && face.TryInsert(item, LaneSpan.One, 0))
|
if (facing.HasValue(out var face) && face.CanAccept(item, 0) && face.TryInsert(item, 0))
|
||||||
{
|
{
|
||||||
var c = _outPuts[i];
|
var c = _outPuts[i];
|
||||||
_outPuts.RemoveAt(i);
|
_outPuts.RemoveAt(i);
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ public partial class BeltPort : Node3D, IBeltPort
|
|||||||
base._ExitTree();
|
base._ExitTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanAccept(IBeltItem item, LaneSpan laneSpan, float beltT) => Access.HasFlag(PortAccess.In) && InsertLogic.CanAccept(this, item);
|
public bool CanAccept(IBeltItem item, float beltT) => Access.HasFlag(PortAccess.In) && InsertLogic.CanAccept(this, item);
|
||||||
public bool TryInsert(IBeltItem item, LaneSpan laneSpan, float beltT) => CanAccept(item, laneSpan, beltT) && InsertLogic.CanInsert(this, item);
|
public bool TryInsert(IBeltItem item, float beltT) => CanAccept(item, beltT) && InsertLogic.CanInsert(this, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IBeltItemInsertLogic
|
public interface IBeltItemInsertLogic
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ public partial class ItemSpawner : Node3D, IProvide<IBeltPortHost>
|
|||||||
}
|
}
|
||||||
var itemBlueprint = ItemFactory.GetBlueprint(ItemName);
|
var itemBlueprint = ItemFactory.GetBlueprint(ItemName);
|
||||||
var dummyItem = new TestItem();
|
var dummyItem = new TestItem();
|
||||||
if (beltPort.CanAccept(dummyItem, LaneSpan.One, 0))
|
if (beltPort.CanAccept(dummyItem, 0))
|
||||||
{
|
{
|
||||||
var ctx = new BlueprintContext() { BluePrintId = new BlueprintId(itemBlueprint), World = World };
|
var ctx = new BlueprintContext() { BluePrintId = new BlueprintId(itemBlueprint), World = World };
|
||||||
dummyItem.Item = itemBlueprint.Factory(ctx);
|
dummyItem.Item = itemBlueprint.Factory(ctx);
|
||||||
if (!beltPort.TryInsert(dummyItem, LaneSpan.One, 0))
|
if (!beltPort.TryInsert(dummyItem, 0))
|
||||||
{
|
{
|
||||||
World.Destroy(dummyItem.Item);//TODO this should not call, but not sure
|
World.Destroy(dummyItem.Item);//TODO this should not call, but not sure
|
||||||
GD.PushWarning("Item failed to insert into port and removed item, item was destroyed but make sure item to prevent overhead.");
|
GD.PushWarning("Item failed to insert into port and removed item, item was destroyed but make sure item to prevent overhead.");
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
|
|||||||
if (_itemBeingHeld.TryGet<Tags>(out var tags) && tags.Contains(TagRegistry.GetTag("cooked")))
|
if (_itemBeingHeld.TryGet<Tags>(out var tags) && tags.Contains(TagRegistry.GetTag("cooked")))
|
||||||
{
|
{
|
||||||
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
||||||
if (port.HasValue(out var v) && _itemBeingHeld.Id != -1 && _itemBeingHeld.IsAlive() && v.TryInsert(new TestItem() { Item = _itemBeingHeld }, LaneSpan.One, 0))
|
if (port.HasValue(out var v) && _itemBeingHeld.Id != -1 && _itemBeingHeld.IsAlive() && v.TryInsert(new TestItem() { Item = _itemBeingHeld }, 0))
|
||||||
{
|
{
|
||||||
_itemBeingHeld = Entity.Null;
|
_itemBeingHeld = Entity.Null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public partial class SlicerTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVox
|
|||||||
if (sliced.Any())
|
if (sliced.Any())
|
||||||
{
|
{
|
||||||
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
||||||
if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = sliced[0] }, LaneSpan.One, 0))
|
if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = sliced[0] }, 0))
|
||||||
{
|
{
|
||||||
GD.Print(sliced[0].Get<Carbohydrates>());
|
GD.Print(sliced[0].Get<Carbohydrates>());
|
||||||
sliced.RemoveAt(0);
|
sliced.RemoveAt(0);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public partial class StackerTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVo
|
|||||||
if (_output.Id != -1)
|
if (_output.Id != -1)
|
||||||
{
|
{
|
||||||
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
|
||||||
if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = _output }, LaneSpan.One, 0))
|
if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = _output }, 0))
|
||||||
{
|
{
|
||||||
_output = Entity.Null;
|
_output = Entity.Null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,13 +97,11 @@ public interface IBeltPort
|
|||||||
|
|
||||||
bool CanAccept(
|
bool CanAccept(
|
||||||
IBeltItem item,
|
IBeltItem item,
|
||||||
LaneSpan laneSpan,
|
|
||||||
float beltT
|
float beltT
|
||||||
);
|
);
|
||||||
|
|
||||||
bool TryInsert(
|
bool TryInsert(
|
||||||
IBeltItem item,
|
IBeltItem item,
|
||||||
LaneSpan laneSpan,
|
|
||||||
float beltT
|
float beltT
|
||||||
);
|
);
|
||||||
IEnumerable<Vector3I> Points()
|
IEnumerable<Vector3I> Points()
|
||||||
@@ -113,22 +111,21 @@ public interface IBeltPort
|
|||||||
yield return (Profile.LocalOffset.Right * i) + Profile.LocalOffset.Origin;
|
yield return (Profile.LocalOffset.Right * i) + Profile.LocalOffset.Origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaneSpan LaneSpan => new(0, (ushort)Profile.Width);
|
|
||||||
}
|
}
|
||||||
public sealed class ConveyorPort : IBeltPort
|
public sealed class ConveyorPort : IBeltPort
|
||||||
{
|
{
|
||||||
public BeltPortProfile Profile { get; }
|
public BeltPortProfile Profile { get; }
|
||||||
public IMovementConveyor Conveyor { get; }
|
public IMovementConveyor Conveyor { get; }
|
||||||
public BeltT BeltT { get; }
|
public BeltT BeltT { get; }
|
||||||
private readonly Func<IBeltItem, float, LaneSpan, bool>? _canAccept;
|
private readonly Func<IBeltItem, float, bool>? _canAccept;
|
||||||
private readonly Func<IBeltItem, float, LaneSpan, bool>? _accept;
|
private readonly Func<IBeltItem, float, bool>? _accept;
|
||||||
|
|
||||||
public ConveyorPort(
|
public ConveyorPort(
|
||||||
IMovementConveyor conveyor,
|
IMovementConveyor conveyor,
|
||||||
BeltPortProfile profile,
|
BeltPortProfile profile,
|
||||||
BeltT beltT,
|
BeltT beltT,
|
||||||
Func<IBeltItem, float, LaneSpan, bool>? canAccept,
|
Func<IBeltItem, float, bool>? canAccept,
|
||||||
Func<IBeltItem, float, LaneSpan, bool>? accept)
|
Func<IBeltItem, float, bool>? accept)
|
||||||
{
|
{
|
||||||
Conveyor = conveyor;
|
Conveyor = conveyor;
|
||||||
Profile = profile;
|
Profile = profile;
|
||||||
@@ -139,7 +136,6 @@ public sealed class ConveyorPort : IBeltPort
|
|||||||
|
|
||||||
public bool CanAccept(
|
public bool CanAccept(
|
||||||
IBeltItem item,
|
IBeltItem item,
|
||||||
LaneSpan laneSpan,
|
|
||||||
float beltT)
|
float beltT)
|
||||||
{
|
{
|
||||||
if (!Profile.Access.HasFlag(PortAccess.In))
|
if (!Profile.Access.HasFlag(PortAccess.In))
|
||||||
@@ -151,28 +147,19 @@ public sealed class ConveyorPort : IBeltPort
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return _canAccept(item, beltT);
|
||||||
// LaneSpan checks live here, not in delegates
|
|
||||||
if (!LaneSpan.Encapsulates(
|
|
||||||
new LaneSpan(0, (ushort)Profile.Width),
|
|
||||||
laneSpan))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return _canAccept(item, beltT, laneSpan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryInsert(
|
public bool TryInsert(
|
||||||
IBeltItem item,
|
IBeltItem item,
|
||||||
LaneSpan laneSpan,
|
|
||||||
float beltT)
|
float beltT)
|
||||||
{
|
{
|
||||||
if (!CanAccept(item, laneSpan, beltT))
|
if (!CanAccept(item, beltT))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _accept!(item, beltT, laneSpan);
|
return _accept!(item, beltT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Flags]
|
[Flags]
|
||||||
@@ -184,88 +171,3 @@ public enum PortAccess : byte
|
|||||||
InOut = In | Out,
|
InOut = In | Out,
|
||||||
BiDirectional = InOut
|
BiDirectional = InOut
|
||||||
}
|
}
|
||||||
public static class SlotExtension
|
|
||||||
{
|
|
||||||
public static LaneSpan MapLaneSpanToFacingPort(this IBeltPort self, IBeltPort other) => MapSlotToFacingSlot(self.Profile.LocalOffset, self.Profile.Width, other.Profile.LocalOffset, other.Profile.Width);
|
|
||||||
|
|
||||||
public static LaneSpan MapSlotToFacingSlot(
|
|
||||||
GridTransform3D fromTx,
|
|
||||||
int fromWidth,
|
|
||||||
GridTransform3D toTx,
|
|
||||||
int toWidth)
|
|
||||||
{
|
|
||||||
ushort minLane = ushort.MaxValue;
|
|
||||||
ushort maxLane = ushort.MinValue;
|
|
||||||
|
|
||||||
// In port-local space:
|
|
||||||
// +Z = forward
|
|
||||||
// +X = width axis
|
|
||||||
// origin = port center / lane 0 start
|
|
||||||
// (adjust if your convention differs)
|
|
||||||
for (int i = 0; i < fromWidth; i++)
|
|
||||||
{
|
|
||||||
var fromLocalLane = new Vector3I(i, 0, 0);
|
|
||||||
|
|
||||||
var fromLocalFacing = fromLocalLane + Vector3I.Forward;
|
|
||||||
|
|
||||||
var worldFacing = fromTx.LocalToWorld(fromLocalFacing);
|
|
||||||
|
|
||||||
var toLocal = toTx.WorldToLocal(worldFacing);
|
|
||||||
|
|
||||||
int laneIndex = toLocal.X;
|
|
||||||
|
|
||||||
bool inWidth = laneIndex >= 0 && laneIndex < toWidth;
|
|
||||||
bool onFacePlane = toLocal.Z == 0; // directly entering target face
|
|
||||||
|
|
||||||
if (!inWidth || !onFacePlane)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
minLane = (ushort)Math.Min(minLane, laneIndex);
|
|
||||||
maxLane = (ushort)Math.Max(maxLane, laneIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minLane > maxLane)
|
|
||||||
return LaneSpan.Zero;
|
|
||||||
return new LaneSpan(minLane, (ushort)(maxLane + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Map a sub-span from 'from' port into the 'to' port space
|
|
||||||
public static LaneSpan MapLaneSpan(this IBeltPort from, IBeltPort to, LaneSpan incoming)
|
|
||||||
{
|
|
||||||
ushort minLane = ushort.MaxValue;
|
|
||||||
ushort maxLane = ushort.MinValue;
|
|
||||||
|
|
||||||
// Loop over each lane in the incoming span
|
|
||||||
for (int i = incoming.Start; i < incoming.End; i++)
|
|
||||||
{
|
|
||||||
// from-local lane
|
|
||||||
var fromLocalLane = new Vector3I(i, 0, 0);
|
|
||||||
|
|
||||||
// shift forward along +Z to "face" the target
|
|
||||||
var fromLocalFacing = fromLocalLane + Vector3I.Forward;
|
|
||||||
|
|
||||||
// world space
|
|
||||||
var worldFacing = from.Profile.LocalOffset.LocalToWorld(fromLocalFacing);
|
|
||||||
|
|
||||||
// target local space
|
|
||||||
var toLocal = to.Profile.LocalOffset.WorldToLocal(worldFacing);
|
|
||||||
|
|
||||||
int laneIndex = toLocal.X;
|
|
||||||
|
|
||||||
bool inWidth = laneIndex >= 0 && laneIndex < to.Profile.Width;
|
|
||||||
bool onFacePlane = toLocal.Z == 0; // entering target face
|
|
||||||
|
|
||||||
if (!inWidth || !onFacePlane)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
minLane = (ushort)Math.Min(minLane, laneIndex);
|
|
||||||
maxLane = (ushort)Math.Max(maxLane, laneIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minLane > maxLane)
|
|
||||||
return LaneSpan.Zero;
|
|
||||||
|
|
||||||
return new LaneSpan(minLane, (ushort)(maxLane + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
namespace FoodFactory;
|
namespace FoodFactory;
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using Arch.Core;
|
using Arch.Core;
|
||||||
using Arch.Core.Extensions;
|
using Arch.Core.Extensions;
|
||||||
using Arch.Core.Utils;
|
using Arch.Core.Utils;
|
||||||
using Arch.LowLevel;
|
using Arch.LowLevel;
|
||||||
using Arch.Persistence;
|
using Arch.Persistence;
|
||||||
|
using Chickensoft.Introspection;
|
||||||
|
using Chickensoft.SaveFileBuilder;
|
||||||
using FoodFactory.Items;
|
using FoodFactory.Items;
|
||||||
using FoodFactory.Math;
|
using FoodFactory.Math;
|
||||||
using Godot;
|
using Godot;
|
||||||
@@ -61,7 +64,35 @@ public class Test
|
|||||||
// GD.Print(worldJson);
|
// GD.Print(worldJson);
|
||||||
var otherWorld = serializer.FromJson(worldJson);
|
var otherWorld = serializer.FromJson(worldJson);
|
||||||
|
|
||||||
|
// ISaveChunk<GameData> saveChunk =new SaveChunk<GameData>(
|
||||||
|
// chunk =>
|
||||||
|
// {
|
||||||
|
// var gameData = new GameData()
|
||||||
|
// {
|
||||||
|
// World = chunk.GetChunkSaveData<World>()
|
||||||
|
// };
|
||||||
|
// return gameData;
|
||||||
|
// },
|
||||||
|
// (chunk, data) =>
|
||||||
|
// {
|
||||||
|
// chunk.LoadChunkSaveData(data.World);
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// ISaveFile<GameData> saveFile = new SaveFile<GameData>(
|
||||||
|
// saveChunk,
|
||||||
|
// async data =>
|
||||||
|
// {
|
||||||
|
// var json = JsonSerializer.Serialize(data);
|
||||||
|
// var access = Godot.FileAccess.Open("user://TestSave.jsonc",Godot.FileAccess.ModeFlags.Write);
|
||||||
|
// access.StoreBuffer(json);
|
||||||
|
// },
|
||||||
|
// async () =>
|
||||||
|
// {
|
||||||
|
// var access = Godot.FileAccess.Open("user://TestSave.jsonc",Godot.FileAccess.ModeFlags.Read);
|
||||||
|
// var json = access.GetBuffer((long)access.GetLength());
|
||||||
|
// return JsonSerializer.Deserialize<GameData>(json);
|
||||||
|
// }
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class TagsSerializer : IJsonFormatter<Tags>
|
public class TagsSerializer : IJsonFormatter<Tags>
|
||||||
@@ -278,3 +309,9 @@ public class BucketStorage<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Meta, Id("game_data")]
|
||||||
|
public partial record GameData
|
||||||
|
{
|
||||||
|
public required World World { get; init; }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user