mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 01:44:35 +08:00
update debug/meta
This commit is contained in:
parent
6fcad52300
commit
67acf2cfeb
@ -1,5 +1,4 @@
|
|||||||
using DCFApixels.DragonECS.Internal;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -173,19 +172,21 @@ namespace DCFApixels.DragonECS
|
|||||||
public List<WordColor> colors = new List<WordColor>();
|
public List<WordColor> colors = new List<WordColor>();
|
||||||
public NameColor(IEnumerable<string> nameWords)
|
public NameColor(IEnumerable<string> nameWords)
|
||||||
{
|
{
|
||||||
|
int i = 0;
|
||||||
foreach (var word in nameWords)
|
foreach (var word in nameWords)
|
||||||
{
|
{
|
||||||
if (!_words.TryGetValue(word, out WordColor color))
|
if (_words.TryGetValue(word, out WordColor color) == false)
|
||||||
{
|
{
|
||||||
color = new WordColor();
|
unchecked
|
||||||
_words.Add(word, color);
|
{
|
||||||
|
color = new WordColor();
|
||||||
MetaColor newColor = new MetaColor(BitsUtility.NextXorShiftState(word.GetHashCode()));
|
color.color = new MetaColor(word).UpContrastColor();
|
||||||
newColor = new MetaColor(newColor.r, newColor.g, newColor.b);
|
_words.Add(word, color);
|
||||||
color.color = newColor.UpContrastColor() / 2;
|
}
|
||||||
}
|
}
|
||||||
color.wordsCount++;
|
color.wordsCount++;
|
||||||
colors.Add(color);
|
colors.Add(color);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private int CalcTotalWordsColor()
|
private int CalcTotalWordsColor()
|
||||||
@ -199,17 +200,31 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
public MetaColor CalcColor()
|
public MetaColor CalcColor()
|
||||||
{
|
{
|
||||||
float r = 0, g = 0, b = 0;
|
float r = 0;
|
||||||
int totalWordsCount = CalcTotalWordsColor();
|
float g = 0;
|
||||||
|
float b = 0;
|
||||||
|
float totalWordsCount = CalcTotalWordsColor();
|
||||||
for (int i = 0, iMax = colors.Count; i < iMax; i++)
|
for (int i = 0, iMax = colors.Count; i < iMax; i++)
|
||||||
{
|
{
|
||||||
var color = colors[i];
|
var color = colors[i];
|
||||||
float m = (float)color.wordsCount / totalWordsCount;
|
float m = color.wordsCount / totalWordsCount;
|
||||||
r += m * color.color.r;
|
r = m * color.color.r;
|
||||||
g += m * color.color.g;
|
g = m * color.color.g;
|
||||||
b += m * color.color.b;
|
b = m * color.color.b;
|
||||||
}
|
}
|
||||||
return new MetaColor((byte)r, (byte)g, (byte)b);
|
return UpContrastColor(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaColor UpContrastColor(float r, float g, float b)
|
||||||
|
{
|
||||||
|
float minChannel = Math.Min(Math.Min(r, g), b);
|
||||||
|
float maxChannel = Math.Max(Math.Max(r, g), b);
|
||||||
|
if (maxChannel == minChannel)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
float factor = 255f / (maxChannel - minChannel);
|
||||||
|
return new MetaColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static Dictionary<Type, NameColor> _names = new Dictionary<Type, NameColor>();
|
private static Dictionary<Type, NameColor> _names = new Dictionary<Type, NameColor>();
|
||||||
@ -226,6 +241,7 @@ namespace DCFApixels.DragonECS
|
|||||||
private static List<string> SplitString(string s)
|
private static List<string> SplitString(string s)
|
||||||
{
|
{
|
||||||
string subs;
|
string subs;
|
||||||
|
|
||||||
List<string> words = new List<string>();
|
List<string> words = new List<string>();
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = 1; i < s.Length; i++)
|
for (int i = 1; i < s.Length; i++)
|
||||||
@ -384,10 +400,6 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region GetCachedTypeMeta
|
#region GetCachedTypeMeta
|
||||||
public static TypeMetaDataCached GetMeta(this object self)
|
|
||||||
{
|
|
||||||
return GetCachedTypeMeta(self);
|
|
||||||
}
|
|
||||||
private static readonly Dictionary<Type, TypeMetaDataCached> _metaCache = new Dictionary<Type, TypeMetaDataCached>();
|
private static readonly Dictionary<Type, TypeMetaDataCached> _metaCache = new Dictionary<Type, TypeMetaDataCached>();
|
||||||
public static TypeMetaDataCached GetCachedTypeMeta(object obj)
|
public static TypeMetaDataCached GetCachedTypeMeta(object obj)
|
||||||
{
|
{
|
||||||
@ -446,13 +458,20 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TypeMetaDataCachedExtensions
|
||||||
|
{
|
||||||
|
public static TypeMetaDataCached GetMeta(this object self)
|
||||||
|
{
|
||||||
|
return EcsDebugUtility.GetCachedTypeMeta(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
public class TypeMetaDataCached
|
public class TypeMetaDataCached
|
||||||
{
|
{
|
||||||
internal readonly Type _type;
|
internal readonly Type _type;
|
||||||
|
|
||||||
internal string _name;
|
internal string _name;
|
||||||
internal MetaGroup _group;
|
internal MetaGroup _group;
|
||||||
internal MetaColor? _color;
|
internal MetaColor _color;
|
||||||
internal string _description;
|
internal string _description;
|
||||||
internal IReadOnlyCollection<string> _tags;
|
internal IReadOnlyCollection<string> _tags;
|
||||||
|
|
||||||
@ -472,7 +491,7 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_name))
|
if (_initFlags.HasFlag(InitFlag.Name) == false)
|
||||||
{
|
{
|
||||||
_name = EcsDebugUtility.GetName(_type);
|
_name = EcsDebugUtility.GetName(_type);
|
||||||
_initFlags |= InitFlag.Name;
|
_initFlags |= InitFlag.Name;
|
||||||
@ -484,7 +503,7 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_group.IsNull)
|
if (_initFlags.HasFlag(InitFlag.Group) == false)
|
||||||
{
|
{
|
||||||
_group = EcsDebugUtility.GetGroup(_type);
|
_group = EcsDebugUtility.GetGroup(_type);
|
||||||
_initFlags |= InitFlag.Group;
|
_initFlags |= InitFlag.Group;
|
||||||
@ -496,19 +515,19 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_color.HasValue == false)
|
if (_initFlags.HasFlag(InitFlag.Color) == false)
|
||||||
{
|
{
|
||||||
_color = EcsDebugUtility.GetColor(_type);
|
_color = EcsDebugUtility.GetColor(_type);
|
||||||
_initFlags |= InitFlag.Color;
|
//_initFlags |= InitFlag.Color;
|
||||||
}
|
}
|
||||||
return _color.Value;
|
return _color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string Description
|
public string Description
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_description == null)
|
if (_initFlags.HasFlag(InitFlag.Description) == false)
|
||||||
{
|
{
|
||||||
_description = EcsDebugUtility.GetDescription(_type);
|
_description = EcsDebugUtility.GetDescription(_type);
|
||||||
_initFlags |= InitFlag.Description;
|
_initFlags |= InitFlag.Description;
|
||||||
@ -520,7 +539,7 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_tags == null)
|
if (_initFlags.HasFlag(InitFlag.Tags) == false)
|
||||||
{
|
{
|
||||||
_tags = EcsDebugUtility.GetTags(_type);
|
_tags = EcsDebugUtility.GetTags(_type);
|
||||||
_initFlags |= InitFlag.Tags;
|
_initFlags |= InitFlag.Tags;
|
||||||
|
11
src/Debug/MetaAttributes/EcsMetaAttribute.cs.meta
Normal file
11
src/Debug/MetaAttributes/EcsMetaAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eb8cc656a6e80f843b8794af9f63faa8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -1,24 +1,78 @@
|
|||||||
using System;
|
using DCFApixels.DragonECS.Internal;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
namespace DCFApixels.DragonECS
|
||||||
{
|
{
|
||||||
|
public interface IMetaColor
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
public byte R { get; }
|
||||||
|
public byte G { get; }
|
||||||
|
public byte B { get; }
|
||||||
|
public byte A { get; }
|
||||||
|
public float FloatR { get; }
|
||||||
|
public float FloatG { get; }
|
||||||
|
public float FloatB { get; }
|
||||||
|
public float FloatA { get; }
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||||
public sealed class MetaColorAttribute : EcsMetaAttribute
|
public sealed class MetaColorAttribute : EcsMetaAttribute, IMetaColor
|
||||||
{
|
{
|
||||||
public readonly MetaColor color;
|
public readonly MetaColor color;
|
||||||
public byte R => color.r;
|
|
||||||
public byte G => color.g;
|
#region Properties
|
||||||
public byte B => color.b;
|
public byte R
|
||||||
public float FloatR => R / (float)byte.MaxValue;
|
{
|
||||||
public float FloatG => G / (float)byte.MaxValue;
|
get { return color.r; }
|
||||||
public float FloatB => B / (float)byte.MaxValue;
|
}
|
||||||
public MetaColorAttribute(byte r, byte g, byte b) => color = new MetaColor(r, g, b, 255);
|
public byte G
|
||||||
public MetaColorAttribute(int colorCode) => color = new MetaColor(colorCode, true);
|
{
|
||||||
|
get { return color.g; }
|
||||||
|
}
|
||||||
|
public byte B
|
||||||
|
{
|
||||||
|
get { return color.b; }
|
||||||
|
}
|
||||||
|
public byte A
|
||||||
|
{
|
||||||
|
get { return color.a; }
|
||||||
|
}
|
||||||
|
public float FloatR
|
||||||
|
{
|
||||||
|
get { return R / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatG
|
||||||
|
{
|
||||||
|
get { return G / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatB
|
||||||
|
{
|
||||||
|
get { return B / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatA
|
||||||
|
{
|
||||||
|
get { return A / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
public MetaColorAttribute(byte r, byte g, byte b)
|
||||||
|
{
|
||||||
|
color = new MetaColor(r, g, b, 255);
|
||||||
|
}
|
||||||
|
public MetaColorAttribute(int colorCode)
|
||||||
|
{
|
||||||
|
color = new MetaColor(colorCode, true);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
|
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
|
||||||
public readonly struct MetaColor
|
public readonly struct MetaColor : IMetaColor
|
||||||
{
|
{
|
||||||
|
#region Consts
|
||||||
public static readonly MetaColor BlackColor = new MetaColor(Black);
|
public static readonly MetaColor BlackColor = new MetaColor(Black);
|
||||||
/// <summary> color code Red. RGB is (255, 0, 0)</summary>
|
/// <summary> color code Red. RGB is (255, 0, 0)</summary>
|
||||||
public const int Red = (255 << 24) | (000 << 16) | (000 << 8) | 255;
|
public const int Red = (255 << 24) | (000 << 16) | (000 << 8) | 255;
|
||||||
@ -63,16 +117,50 @@ namespace DCFApixels.DragonECS
|
|||||||
public const int White = -1;
|
public const int White = -1;
|
||||||
/// <summary> color code Black. RGB is (0, 0, 0)</summary>
|
/// <summary> color code Black. RGB is (0, 0, 0)</summary>
|
||||||
public const int Black = 0;
|
public const int Black = 0;
|
||||||
|
#endregion
|
||||||
|
|
||||||
[FieldOffset(0)] public readonly int colorCode;
|
[FieldOffset(0)] public readonly int colorCode;
|
||||||
[FieldOffset(3)] public readonly byte r;
|
[FieldOffset(3)] public readonly byte r;
|
||||||
[FieldOffset(2)] public readonly byte g;
|
[FieldOffset(2)] public readonly byte g;
|
||||||
[FieldOffset(1)] public readonly byte b;
|
[FieldOffset(1)] public readonly byte b;
|
||||||
[FieldOffset(0)] public readonly byte a;
|
[FieldOffset(0)] public readonly byte a;
|
||||||
public float FloatR => r / (float)byte.MaxValue;
|
|
||||||
public float FloatG => g / (float)byte.MaxValue;
|
#region Properties
|
||||||
public float FloatB => b / (float)byte.MaxValue;
|
byte IMetaColor.R
|
||||||
public float FloatA => a / (float)byte.MaxValue;
|
{
|
||||||
|
get { return r; }
|
||||||
|
}
|
||||||
|
byte IMetaColor.G
|
||||||
|
{
|
||||||
|
get { return g; }
|
||||||
|
}
|
||||||
|
byte IMetaColor.B
|
||||||
|
{
|
||||||
|
get { return b; }
|
||||||
|
}
|
||||||
|
byte IMetaColor.A
|
||||||
|
{
|
||||||
|
get { return a; }
|
||||||
|
}
|
||||||
|
public float FloatR
|
||||||
|
{
|
||||||
|
get { return r / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatG
|
||||||
|
{
|
||||||
|
get { return g / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatB
|
||||||
|
{
|
||||||
|
get { return b / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
public float FloatA
|
||||||
|
{
|
||||||
|
get { return a / (float)byte.MaxValue; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
public MetaColor(byte r, byte g, byte b) : this()
|
public MetaColor(byte r, byte g, byte b) : this()
|
||||||
{
|
{
|
||||||
this.r = r;
|
this.r = r;
|
||||||
@ -87,11 +175,45 @@ namespace DCFApixels.DragonECS
|
|||||||
this.b = b;
|
this.b = b;
|
||||||
this.a = a;
|
this.a = a;
|
||||||
}
|
}
|
||||||
public MetaColor(int colorCode) : this() => this.colorCode = colorCode;
|
public MetaColor(int colorCode) : this()
|
||||||
public MetaColor(int colorCode, bool withoutAlpha) : this() => this.colorCode = withoutAlpha ? colorCode | 255 : colorCode;
|
{
|
||||||
public (byte, byte, byte) ToTupleRGB() => (r, g, b);
|
this.colorCode = colorCode;
|
||||||
public (byte, byte, byte, byte) ToTupleRGBA() => (r, g, b, a);
|
}
|
||||||
|
public MetaColor(int colorCode, bool withoutAlpha) : this()
|
||||||
|
{
|
||||||
|
this.colorCode = withoutAlpha ? colorCode | 255 : colorCode;
|
||||||
|
}
|
||||||
|
public MetaColor(string stringCode) : this()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
const uint MAGIC_CONST = 0xA638_783E;
|
||||||
|
uint colorCode = (uint)stringCode.GetHashCode();
|
||||||
|
colorCode ^= MAGIC_CONST;
|
||||||
|
colorCode = BitsUtility.NextXorShiftState(colorCode);
|
||||||
|
this.colorCode = (int)colorCode | 255;
|
||||||
|
this.colorCode = UpContrastColor().colorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Deconstructs
|
||||||
|
public void Deconstruct(out byte r, out byte g, out byte b)
|
||||||
|
{
|
||||||
|
r = this.r;
|
||||||
|
g = this.g;
|
||||||
|
b = this.b;
|
||||||
|
}
|
||||||
|
public void Deconstruct(out byte r, out byte g, out byte b, out byte a)
|
||||||
|
{
|
||||||
|
r = this.r;
|
||||||
|
g = this.g;
|
||||||
|
b = this.b;
|
||||||
|
a = this.a;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
public MetaColor UpContrastColor()
|
public MetaColor UpContrastColor()
|
||||||
{
|
{
|
||||||
byte minChannel = Math.Min(Math.Min(r, g), b);
|
byte minChannel = Math.Min(Math.Min(r, g), b);
|
||||||
@ -103,9 +225,97 @@ namespace DCFApixels.DragonECS
|
|||||||
float factor = 255f / (maxChannel - minChannel);
|
float factor = 255f / (maxChannel - minChannel);
|
||||||
return new MetaColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor));
|
return new MetaColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor));
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Operators
|
||||||
public static MetaColor operator /(MetaColor a, float b)
|
public static MetaColor operator /(MetaColor a, float b)
|
||||||
{
|
{
|
||||||
return new MetaColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b));
|
return new MetaColor(
|
||||||
|
(byte)(a.r / b),
|
||||||
|
(byte)(a.g / b),
|
||||||
|
(byte)(a.b / b));
|
||||||
}
|
}
|
||||||
|
public static MetaColor operator /(float b, MetaColor a)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r / b),
|
||||||
|
(byte)(a.g / b),
|
||||||
|
(byte)(a.b / b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator /(MetaColor a, MetaColor b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r / b.r),
|
||||||
|
(byte)(a.g / b.g),
|
||||||
|
(byte)(a.b / b.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetaColor operator *(MetaColor a, float b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r * b),
|
||||||
|
(byte)(a.g * b),
|
||||||
|
(byte)(a.b * b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetaColor operator *(float b, MetaColor a)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r * b),
|
||||||
|
(byte)(a.g * b),
|
||||||
|
(byte)(a.b * b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator *(MetaColor a, MetaColor b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r * b.r),
|
||||||
|
(byte)(a.g * b.g),
|
||||||
|
(byte)(a.b * b.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetaColor operator +(MetaColor a, byte b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r + b),
|
||||||
|
(byte)(a.g + b),
|
||||||
|
(byte)(a.b + b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator +(byte b, MetaColor a)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r + b),
|
||||||
|
(byte)(a.g + b),
|
||||||
|
(byte)(a.b + b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator +(MetaColor a, MetaColor b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r + b.r),
|
||||||
|
(byte)(a.g + b.g),
|
||||||
|
(byte)(a.b + b.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetaColor operator -(MetaColor a, byte b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r - b),
|
||||||
|
(byte)(a.g - b),
|
||||||
|
(byte)(a.b - b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator -(byte b, MetaColor a)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r - b),
|
||||||
|
(byte)(a.g - b),
|
||||||
|
(byte)(a.b - b));
|
||||||
|
}
|
||||||
|
public static MetaColor operator -(MetaColor a, MetaColor b)
|
||||||
|
{
|
||||||
|
return new MetaColor(
|
||||||
|
(byte)(a.r - b.r),
|
||||||
|
(byte)(a.g - b.g),
|
||||||
|
(byte)(a.b - b.b));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user