mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 09:54:35 +08:00
move EcsAspectIterator to EcsAspect.Iterator
This commit is contained in:
parent
88bc3355b7
commit
316735cf9a
453
src/EcsAspect.cs
453
src/EcsAspect.cs
@ -1,5 +1,4 @@
|
|||||||
using DCFApixels.DragonECS.Internal;
|
using DCFApixels.DragonECS.Internal;
|
||||||
using DCFApixels.DragonECS.Utils;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -13,10 +12,10 @@ namespace DCFApixels.DragonECS
|
|||||||
internal EcsMask _mask;
|
internal EcsMask _mask;
|
||||||
private bool _isInit;
|
private bool _isInit;
|
||||||
|
|
||||||
internal UnsafeArray<int> _sortIncBuffer;
|
private UnsafeArray<int> _sortIncBuffer;
|
||||||
internal UnsafeArray<int> _sortExcBuffer;
|
private UnsafeArray<int> _sortExcBuffer;
|
||||||
internal UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
private UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
||||||
internal UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
private UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
public EcsMask Mask => _mask;
|
public EcsMask Mask => _mask;
|
||||||
@ -150,13 +149,13 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Iterator
|
#region Iterator
|
||||||
public EcsAspectIterator GetIterator()
|
public Iterator GetIterator()
|
||||||
{
|
{
|
||||||
return new EcsAspectIterator(this, _source.Entities);
|
return new Iterator(this, _source.Entities);
|
||||||
}
|
}
|
||||||
public EcsAspectIterator GetIteratorFor(EcsSpan span)
|
public Iterator GetIteratorFor(EcsSpan span)
|
||||||
{
|
{
|
||||||
return new EcsAspectIterator(this, span);
|
return new Iterator(this, span);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -172,6 +171,224 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Iterator
|
||||||
|
public ref struct Iterator
|
||||||
|
{
|
||||||
|
public readonly int worldID;
|
||||||
|
public readonly EcsAspect aspect;
|
||||||
|
private EcsSpan _span;
|
||||||
|
|
||||||
|
public Iterator(EcsAspect aspect, EcsSpan span)
|
||||||
|
{
|
||||||
|
worldID = aspect.World.id;
|
||||||
|
_span = span;
|
||||||
|
this.aspect = aspect;
|
||||||
|
}
|
||||||
|
public void CopyTo(EcsGroup group)
|
||||||
|
{
|
||||||
|
group.Clear();
|
||||||
|
var enumerator = GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
group.AddInternal(enumerator.Current);
|
||||||
|
}
|
||||||
|
public int CopyTo(ref int[] array)
|
||||||
|
{
|
||||||
|
var enumerator = GetEnumerator();
|
||||||
|
int count = 0;
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (array.Length <= count)
|
||||||
|
Array.Resize(ref array, array.Length << 1);
|
||||||
|
array[count++] = enumerator.Current;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
public EcsSpan CopyToSpan(ref int[] array)
|
||||||
|
{
|
||||||
|
var enumerator = GetEnumerator();
|
||||||
|
int count = 0;
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (array.Length <= count)
|
||||||
|
Array.Resize(ref array, array.Length << 1);
|
||||||
|
array[count++] = enumerator.Current;
|
||||||
|
}
|
||||||
|
return new EcsSpan(worldID, array, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region object
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
List<int> ints = new List<int>();
|
||||||
|
foreach (var e in this)
|
||||||
|
{
|
||||||
|
ints.Add(e);
|
||||||
|
}
|
||||||
|
return CollectionUtility.EntitiesToString(ints, "it");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Enumerator
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Enumerator GetEnumerator() => new Enumerator(_span, aspect);
|
||||||
|
|
||||||
|
public unsafe ref struct Enumerator
|
||||||
|
{
|
||||||
|
#region CountComparers
|
||||||
|
private readonly struct IncCountComparer : IComparerX<int>
|
||||||
|
{
|
||||||
|
public readonly int[] counts;
|
||||||
|
public IncCountComparer(int[] counts)
|
||||||
|
{
|
||||||
|
this.counts = counts;
|
||||||
|
}
|
||||||
|
public int Compare(int a, int b)
|
||||||
|
{
|
||||||
|
return counts[a] - counts[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private readonly struct ExcCountComparer : IComparerX<int>
|
||||||
|
{
|
||||||
|
public readonly int[] counts;
|
||||||
|
public ExcCountComparer(int[] counts)
|
||||||
|
{
|
||||||
|
this.counts = counts;
|
||||||
|
}
|
||||||
|
public int Compare(int a, int b)
|
||||||
|
{
|
||||||
|
return counts[b] - counts[a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private ReadOnlySpan<int>.Enumerator _span;
|
||||||
|
private readonly int[][] _entitiesComponentMasks;
|
||||||
|
|
||||||
|
private static EcsMaskChunck* _preSortedIncBuffer;
|
||||||
|
private static EcsMaskChunck* _preSortedExcBuffer;
|
||||||
|
|
||||||
|
private UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
||||||
|
private UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
||||||
|
|
||||||
|
//private EcsAspect aspect;
|
||||||
|
|
||||||
|
public unsafe Enumerator(EcsSpan span, EcsAspect aspect)
|
||||||
|
{
|
||||||
|
//this.aspect = aspect;
|
||||||
|
_span = span.GetEnumerator();
|
||||||
|
_entitiesComponentMasks = aspect.World._entitiesComponentMasks;
|
||||||
|
_sortIncChunckBuffer = aspect._sortIncChunckBuffer;
|
||||||
|
_sortExcChunckBuffer = aspect._sortExcChunckBuffer;
|
||||||
|
|
||||||
|
#region Sort
|
||||||
|
UnsafeArray<int> _sortIncBuffer = aspect._sortIncBuffer;
|
||||||
|
UnsafeArray<int> _sortExcBuffer = aspect._sortExcBuffer;
|
||||||
|
int[] counts = aspect.World._poolComponentCounts;
|
||||||
|
|
||||||
|
if (_preSortedIncBuffer == null)
|
||||||
|
{
|
||||||
|
_preSortedIncBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
||||||
|
_preSortedExcBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sortIncChunckBuffer.Length > 1)
|
||||||
|
{
|
||||||
|
IncCountComparer incComparer = new IncCountComparer(counts);
|
||||||
|
UnsafeArraySortHalperX<int>.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer);
|
||||||
|
for (int i = 0; i < _sortIncBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
_preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++)
|
||||||
|
{
|
||||||
|
EcsMaskChunck bas = _preSortedIncBuffer[i];
|
||||||
|
int chankIndexX = bas.chankIndex;
|
||||||
|
int maskX = bas.mask;
|
||||||
|
|
||||||
|
for (int j = i + 1; j < _sortIncBuffer.Length; j++)
|
||||||
|
{
|
||||||
|
if (_preSortedIncBuffer[j].chankIndex == chankIndexX)
|
||||||
|
{
|
||||||
|
maskX |= _preSortedIncBuffer[j].mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_sortIncChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
||||||
|
while (++i < _sortIncBuffer.Length && _preSortedIncBuffer[i].chankIndex == chankIndexX)
|
||||||
|
{
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sortExcChunckBuffer.Length > 1)
|
||||||
|
{
|
||||||
|
ExcCountComparer excComparer = new ExcCountComparer(counts);
|
||||||
|
UnsafeArraySortHalperX<int>.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer);
|
||||||
|
for (int i = 0; i < _sortExcBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
_preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++)
|
||||||
|
{
|
||||||
|
EcsMaskChunck bas = _preSortedExcBuffer[i];
|
||||||
|
int chankIndexX = bas.chankIndex;
|
||||||
|
int maskX = bas.mask;
|
||||||
|
|
||||||
|
for (int j = i + 1; j < _sortExcBuffer.Length; j++)
|
||||||
|
{
|
||||||
|
if (_preSortedExcBuffer[j].chankIndex == chankIndexX)
|
||||||
|
{
|
||||||
|
maskX |= _preSortedExcBuffer[j].mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_sortExcChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
||||||
|
while (++i < _sortExcBuffer.Length && _preSortedExcBuffer[i].chankIndex == chankIndexX)
|
||||||
|
{
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
public int Current
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _span.Current;
|
||||||
|
}
|
||||||
|
//public entlong CurrentLong
|
||||||
|
//{
|
||||||
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
// get => aspect.World.GetEntityLong(_span.Current);
|
||||||
|
//}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
while (_span.MoveNext())
|
||||||
|
{
|
||||||
|
int e = _span.Current;
|
||||||
|
for (int i = 0; i < _sortIncChunckBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
var bit = _sortIncChunckBuffer.ptr[i];
|
||||||
|
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < _sortExcChunckBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
var bit = _sortExcChunckBuffer.ptr[i];
|
||||||
|
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
skip: continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
#region BuilderBase
|
#region BuilderBase
|
||||||
@ -182,222 +399,4 @@ namespace DCFApixels.DragonECS
|
|||||||
public abstract TPool Optional<TPool>() where TPool : IEcsPoolImplementation, new();
|
public abstract TPool Optional<TPool>() where TPool : IEcsPoolImplementation, new();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Iterator
|
|
||||||
public ref struct EcsAspectIterator
|
|
||||||
{
|
|
||||||
public readonly int worldID;
|
|
||||||
public readonly EcsAspect aspect;
|
|
||||||
private EcsSpan _span;
|
|
||||||
|
|
||||||
public EcsAspectIterator(EcsAspect aspect, EcsSpan span)
|
|
||||||
{
|
|
||||||
worldID = aspect.World.id;
|
|
||||||
_span = span;
|
|
||||||
this.aspect = aspect;
|
|
||||||
}
|
|
||||||
public void CopyTo(EcsGroup group)
|
|
||||||
{
|
|
||||||
group.Clear();
|
|
||||||
var enumerator = GetEnumerator();
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
group.AddInternal(enumerator.Current);
|
|
||||||
}
|
|
||||||
public int CopyTo(ref int[] array)
|
|
||||||
{
|
|
||||||
var enumerator = GetEnumerator();
|
|
||||||
int count = 0;
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
if (array.Length <= count)
|
|
||||||
Array.Resize(ref array, array.Length << 1);
|
|
||||||
array[count++] = enumerator.Current;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
public EcsSpan CopyToSpan(ref int[] array)
|
|
||||||
{
|
|
||||||
var enumerator = GetEnumerator();
|
|
||||||
int count = 0;
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
if (array.Length <= count)
|
|
||||||
Array.Resize(ref array, array.Length << 1);
|
|
||||||
array[count++] = enumerator.Current;
|
|
||||||
}
|
|
||||||
return new EcsSpan(worldID, array, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region object
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
List<int> ints = new List<int>();
|
|
||||||
foreach (var e in this)
|
|
||||||
{
|
|
||||||
ints.Add(e);
|
|
||||||
}
|
|
||||||
return CollectionUtility.EntitiesToString(ints, "it");
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Enumerator
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Enumerator GetEnumerator() => new Enumerator(_span, aspect);
|
|
||||||
|
|
||||||
public unsafe ref struct Enumerator
|
|
||||||
{
|
|
||||||
#region CountComparers
|
|
||||||
private readonly struct IncCountComparer : IComparerX<int>
|
|
||||||
{
|
|
||||||
public readonly int[] counts;
|
|
||||||
public IncCountComparer(int[] counts)
|
|
||||||
{
|
|
||||||
this.counts = counts;
|
|
||||||
}
|
|
||||||
public int Compare(int a, int b)
|
|
||||||
{
|
|
||||||
return counts[a] - counts[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private readonly struct ExcCountComparer : IComparerX<int>
|
|
||||||
{
|
|
||||||
public readonly int[] counts;
|
|
||||||
public ExcCountComparer(int[] counts)
|
|
||||||
{
|
|
||||||
this.counts = counts;
|
|
||||||
}
|
|
||||||
public int Compare(int a, int b)
|
|
||||||
{
|
|
||||||
return counts[b] - counts[a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private ReadOnlySpan<int>.Enumerator _span;
|
|
||||||
private readonly int[][] _entitiesComponentMasks;
|
|
||||||
|
|
||||||
private static EcsMaskChunck* _preSortedIncBuffer;
|
|
||||||
private static EcsMaskChunck* _preSortedExcBuffer;
|
|
||||||
|
|
||||||
private UnsafeArray<EcsMaskChunck> _sortIncChunckBuffer;
|
|
||||||
private UnsafeArray<EcsMaskChunck> _sortExcChunckBuffer;
|
|
||||||
|
|
||||||
//private EcsAspect aspect;
|
|
||||||
|
|
||||||
public unsafe Enumerator(EcsSpan span, EcsAspect aspect)
|
|
||||||
{
|
|
||||||
//this.aspect = aspect;
|
|
||||||
_span = span.GetEnumerator();
|
|
||||||
_entitiesComponentMasks = aspect.World._entitiesComponentMasks;
|
|
||||||
_sortIncChunckBuffer = aspect._sortIncChunckBuffer;
|
|
||||||
_sortExcChunckBuffer = aspect._sortExcChunckBuffer;
|
|
||||||
|
|
||||||
#region Sort
|
|
||||||
UnsafeArray<int> _sortIncBuffer = aspect._sortIncBuffer;
|
|
||||||
UnsafeArray<int> _sortExcBuffer = aspect._sortExcBuffer;
|
|
||||||
int[] counts = aspect.World._poolComponentCounts;
|
|
||||||
|
|
||||||
if (_preSortedIncBuffer == null)
|
|
||||||
{
|
|
||||||
_preSortedIncBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
|
||||||
_preSortedExcBuffer = UnmanagedArrayUtility.New<EcsMaskChunck>(256);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sortIncChunckBuffer.Length > 1)
|
|
||||||
{
|
|
||||||
IncCountComparer incComparer = new IncCountComparer(counts);
|
|
||||||
UnsafeArraySortHalperX<int>.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer);
|
|
||||||
for (int i = 0; i < _sortIncBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
_preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]);
|
|
||||||
}
|
|
||||||
for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++)
|
|
||||||
{
|
|
||||||
EcsMaskChunck bas = _preSortedIncBuffer[i];
|
|
||||||
int chankIndexX = bas.chankIndex;
|
|
||||||
int maskX = bas.mask;
|
|
||||||
|
|
||||||
for (int j = i + 1; j < _sortIncBuffer.Length; j++)
|
|
||||||
{
|
|
||||||
if (_preSortedIncBuffer[j].chankIndex == chankIndexX)
|
|
||||||
{
|
|
||||||
maskX |= _preSortedIncBuffer[j].mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sortIncChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
|
||||||
while (++i < _sortIncBuffer.Length && _preSortedIncBuffer[i].chankIndex == chankIndexX)
|
|
||||||
{
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sortExcChunckBuffer.Length > 1)
|
|
||||||
{
|
|
||||||
ExcCountComparer excComparer = new ExcCountComparer(counts);
|
|
||||||
UnsafeArraySortHalperX<int>.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer);
|
|
||||||
for (int i = 0; i < _sortExcBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
_preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++)
|
|
||||||
{
|
|
||||||
EcsMaskChunck bas = _preSortedExcBuffer[i];
|
|
||||||
int chankIndexX = bas.chankIndex;
|
|
||||||
int maskX = bas.mask;
|
|
||||||
|
|
||||||
for (int j = i + 1; j < _sortExcBuffer.Length; j++)
|
|
||||||
{
|
|
||||||
if (_preSortedExcBuffer[j].chankIndex == chankIndexX)
|
|
||||||
{
|
|
||||||
maskX |= _preSortedExcBuffer[j].mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sortExcChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX);
|
|
||||||
while (++i < _sortExcBuffer.Length && _preSortedExcBuffer[i].chankIndex == chankIndexX)
|
|
||||||
{
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
public int Current
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => _span.Current;
|
|
||||||
}
|
|
||||||
//public entlong CurrentLong
|
|
||||||
//{
|
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
// get => aspect.World.GetEntityLong(_span.Current);
|
|
||||||
//}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
while (_span.MoveNext())
|
|
||||||
{
|
|
||||||
int e = _span.Current;
|
|
||||||
for (int i = 0; i < _sortIncChunckBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
var bit = _sortIncChunckBuffer.ptr[i];
|
|
||||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask)
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < _sortExcChunckBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
var bit = _sortExcChunckBuffer.ptr[i];
|
|
||||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0)
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
skip: continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user