2023-06-26 02:53:55 +08:00
using DCFApixels.DragonECS.Internal ;
using System ;
2023-06-01 19:13:04 +08:00
using System.Collections ;
using System.Collections.Generic ;
2024-01-05 22:16:48 +08:00
using System.ComponentModel ;
2023-05-28 06:29:04 +08:00
using System.Diagnostics ;
2023-06-26 02:53:55 +08:00
using System.Linq ;
2023-02-13 21:11:54 +08:00
using System.Runtime.CompilerServices ;
2023-03-30 20:47:39 +08:00
using System.Runtime.InteropServices ;
2023-05-07 00:50:02 +08:00
2024-09-04 12:11:35 +08:00
//_dense заполняется с индекса 1
//в операциях изменяющих состояние группы нельзя итерироваться по this, либо осторожно учитывать этот момент
2023-05-26 04:25:09 +08:00
namespace DCFApixels.DragonECS
{
2024-04-28 19:43:10 +08:00
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices ;
2024-04-29 17:46:32 +08:00
[Il2CppSetOption(Option.NullChecks, false)]
2024-04-28 19:43:10 +08:00
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
2023-03-30 20:47:39 +08:00
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
2024-01-05 23:49:29 +08:00
[DebuggerTypeProxy(typeof(EcsGroup.DebuggerProxy))]
2023-03-30 20:47:39 +08:00
public readonly ref struct EcsReadonlyGroup
2023-02-14 03:26:34 +08:00
{
2023-03-30 20:47:39 +08:00
private readonly EcsGroup _source ;
2023-04-24 00:19:07 +08:00
2023-04-01 22:29:34 +08:00
#region Properties
2024-03-02 04:20:34 +08:00
public bool IsNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source = = null ; }
}
2023-12-24 15:40:19 +08:00
public int WorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source . WorldID ; }
2023-12-24 15:40:19 +08:00
}
2023-04-24 00:31:03 +08:00
public EcsWorld World
2023-04-09 02:52:39 +08:00
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source . World ; }
2023-04-09 02:52:39 +08:00
}
2023-03-30 20:47:39 +08:00
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source . Count ; }
2023-03-30 20:47:39 +08:00
}
2023-04-01 20:45:37 +08:00
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source . CapacityDense ; }
2023-04-01 20:45:37 +08:00
}
2024-03-02 06:07:50 +08:00
public EcsLongsSpan Longs
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source . Longs ; }
}
2023-04-15 00:23:46 +08:00
public bool IsReleazed
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source . IsReleased ; }
2023-04-15 00:23:46 +08:00
}
2023-05-07 00:50:02 +08:00
public int this [ int index ]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
get { return _source [ index ] ; }
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsReadonlyGroup ( EcsGroup source )
{
_source = source ;
2023-05-07 00:50:02 +08:00
}
2023-04-01 22:29:34 +08:00
#endregion
#region Methods
2023-03-30 20:47:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool Has ( int entityID ) { return _source . Has ( entityID ) ; }
2023-03-30 20:47:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public int IndexOf ( int entityID ) { return _source . IndexOf ( entityID ) ; }
2023-06-01 19:13:04 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-03 17:30:26 +08:00
public void CopyTo ( int [ ] array , int arrayIndex ) { _source . CopyTo ( array , arrayIndex ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public EcsGroup Clone ( ) { return _source . Clone ( ) ; }
2023-06-01 19:13:04 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 06:07:50 +08:00
public EcsSpan Slice ( int start ) { return _source . Slice ( start ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsSpan Slice ( int start , int length ) { return _source . Slice ( start , length ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public EcsSpan ToSpan ( ) { return _source . ToSpan ( ) ; }
2023-07-02 21:41:06 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 06:07:50 +08:00
public int [ ] ToArray ( ) { return _source . ToArray ( ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-08-31 14:51:13 +08:00
public int ToArray ( ref int [ ] dynamicBuffer ) { return _source . ToArray ( ref dynamicBuffer ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToCollection ( ICollection < int > collection ) { _source . ToCollection ( collection ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 06:07:50 +08:00
public EcsGroup . Enumerator GetEnumerator ( ) { return _source . GetEnumerator ( ) ; }
2023-06-01 19:13:04 +08:00
2023-05-07 00:50:02 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public int First ( ) { return _source . First ( ) ; }
2023-05-07 00:50:02 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public int Last ( ) { return _source . Last ( ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool SetEquals ( EcsGroup group ) { return _source . SetEquals ( group ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool SetEquals ( EcsReadonlyGroup group ) { return _source . SetEquals ( group . _source ) ; }
2023-12-24 16:04:24 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool SetEquals ( EcsSpan span ) { return _source . SetEquals ( span ) ; }
2024-09-03 17:30:26 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool SetEquals ( IEnumerable < int > other ) { return _source . SetEquals ( other ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool Overlaps ( EcsGroup group ) { return _source . Overlaps ( group ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool Overlaps ( EcsReadonlyGroup group ) { return _source . Overlaps ( group . _source ) ; }
2023-12-24 16:04:24 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool Overlaps ( EcsSpan span ) { return _source . Overlaps ( span ) ; }
2024-09-03 17:30:26 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Overlaps ( IEnumerable < int > other ) { return _source . Overlaps ( other ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool IsSubsetOf ( EcsGroup group ) { return _source . IsSubsetOf ( group ) ; }
2024-01-29 01:09:17 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool IsSubsetOf ( EcsReadonlyGroup group ) { return _source . IsSubsetOf ( group . _source ) ; }
2024-09-03 17:30:26 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSubsetOf ( EcsSpan span ) { return _source . IsSubsetOf ( span ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSubsetOf ( IEnumerable < int > other ) { return _source . IsSubsetOf ( other ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSubsetOf ( EcsGroup group ) { return _source . IsProperSubsetOf ( group ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSubsetOf ( EcsReadonlyGroup group ) { return _source . IsProperSubsetOf ( group . _source ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSubsetOf ( EcsSpan span ) { return _source . IsProperSubsetOf ( span ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSubsetOf ( IEnumerable < int > other ) { return _source . IsProperSubsetOf ( other ) ; }
2023-12-24 15:54:34 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool IsSupersetOf ( EcsGroup group ) { return _source . IsSupersetOf ( group ) ; }
2024-01-29 01:09:17 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public bool IsSupersetOf ( EcsReadonlyGroup group ) { return _source . IsSupersetOf ( group . _source ) ; }
2024-09-03 17:30:26 +08:00
public bool IsSupersetOf ( EcsSpan span ) { return _source . IsSupersetOf ( span ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSupersetOf ( IEnumerable < int > other ) { return _source . IsSupersetOf ( other ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSupersetOf ( EcsGroup group ) { return _source . IsProperSupersetOf ( group ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSupersetOf ( EcsReadonlyGroup group ) { return _source . IsProperSupersetOf ( group . _source ) ; }
public bool IsProperSupersetOf ( EcsSpan span ) { return _source . IsProperSupersetOf ( span ) ; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSupersetOf ( IEnumerable < int > other ) { return _source . IsProperSupersetOf ( other ) ; }
2023-04-01 22:29:34 +08:00
#endregion
2023-04-08 23:01:10 +08:00
#region Internal
2023-04-09 02:52:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 06:07:50 +08:00
internal EcsGroup GetSource_Internal ( ) { return _source ; }
2023-04-08 23:01:10 +08:00
#endregion
2023-04-15 00:23:46 +08:00
2023-12-31 13:07:53 +08:00
#region Other
2024-01-05 22:16:48 +08:00
public override string ToString ( )
{
return _source ! = null ? _source . ToString ( ) : "NULL" ;
}
#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член
[Obsolete("Equals() on EcsGroup will always throw an exception. Use the equality operator instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
2024-03-02 04:20:34 +08:00
public override bool Equals ( object obj ) { throw new NotSupportedException ( ) ; }
2024-01-05 22:16:48 +08:00
[Obsolete("GetHashCode() on EcsGroup will always throw an exception.")]
[EditorBrowsable(EditorBrowsableState.Never)]
2024-03-02 04:20:34 +08:00
public override int GetHashCode ( ) { throw new NotSupportedException ( ) ; }
2024-01-05 22:16:48 +08:00
#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член
2023-12-23 20:17:28 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public static implicit operator EcsSpan ( EcsReadonlyGroup a ) { return a . ToSpan ( ) ; }
2023-05-28 06:29:04 +08:00
#endregion
2023-02-14 03:26:34 +08:00
}
2024-04-28 19:43:10 +08:00
#if ENABLE_IL2CPP
2024-11-08 17:21:36 +08:00
[Il2CppSetOption(Option.NullChecks, false)]
2024-04-28 19:43:10 +08:00
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
2023-05-28 06:29:04 +08:00
[DebuggerTypeProxy(typeof(DebuggerProxy))]
2024-09-05 17:26:08 +08:00
//TODO переработать EcsGroup в структуру-обертку, чтобы когда вызывается Release то можно было занулить эту структуру, а может не перерабатывать, есть проблема с боксингом
2024-09-03 17:30:26 +08:00
public class EcsGroup : IDisposable , IEnumerable < int > , IEntityStorage , ISet < int >
2023-02-13 21:11:54 +08:00
{
2023-04-20 20:03:26 +08:00
private EcsWorld _source ;
2023-03-30 10:46:57 +08:00
private int [ ] _dense ;
2024-09-03 17:30:26 +08:00
private int [ ] _sparse ; //Старший бит занят временной маркировкой в операциях над множествами
2024-03-02 04:20:34 +08:00
private int _count = 0 ;
2023-05-28 06:29:04 +08:00
internal bool _isReleased = true ;
2023-04-15 00:23:46 +08:00
2023-02-13 21:11:54 +08:00
#region Properties
2024-04-16 12:46:09 +08:00
public short WorldID
2023-12-24 15:40:19 +08:00
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-10-31 16:27:53 +08:00
get { return _source . ID ; }
2023-12-24 15:40:19 +08:00
}
2023-12-20 19:15:48 +08:00
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:25:18 +08:00
get { return _source ; }
2023-12-20 19:15:48 +08:00
}
2023-03-30 10:46:57 +08:00
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:25:18 +08:00
get { return _count ; }
2023-03-30 10:46:57 +08:00
}
2023-04-01 20:45:37 +08:00
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:25:18 +08:00
get { return _dense . Length ; }
2023-04-01 20:45:37 +08:00
}
2023-03-30 20:47:39 +08:00
public EcsReadonlyGroup Readonly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:25:18 +08:00
get { return new EcsReadonlyGroup ( this ) ; }
2023-03-30 20:47:39 +08:00
}
2024-03-02 06:07:50 +08:00
public EcsLongsSpan Longs
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new EcsLongsSpan ( this ) ; }
}
2023-05-26 00:24:38 +08:00
public bool IsReleased
2023-04-15 00:23:46 +08:00
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:25:18 +08:00
get { return _isReleased ; }
2023-04-15 00:23:46 +08:00
}
2024-09-07 17:18:35 +08:00
bool ICollection < int > . IsReadOnly { get { return false ; } }
2024-09-03 17:30:26 +08:00
2023-05-07 00:50:02 +08:00
public int this [ int index ]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:20:34 +08:00
if ( index < 0 | | index > = Count ) { Throw . ArgumentOutOfRange ( ) ; }
2023-05-07 00:50:02 +08:00
#endif
2023-06-12 12:49:41 +08:00
return _dense [ + + index ] ;
2023-05-07 00:50:02 +08:00
}
2024-11-24 18:23:30 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
// TODO добавить лок енумератора на изменение
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( index < 0 | | index > = Count ) { Throw . ArgumentOutOfRange ( ) ; }
#endif
var oldValue = _dense [ index ] ;
_dense [ index ] = value ;
_sparse [ oldValue ] = 0 ;
}
2023-05-07 00:50:02 +08:00
}
2023-02-13 21:11:54 +08:00
#endregion
2023-06-10 00:55:00 +08:00
#region Constrcutors / Dispose
2023-04-17 22:58:52 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-20 20:03:26 +08:00
public static EcsGroup New ( EcsWorld world )
2023-04-01 20:45:37 +08:00
{
2023-06-01 20:14:34 +08:00
return world . GetFreeGroup ( ) ;
2023-04-08 21:29:18 +08:00
}
2024-03-02 21:59:02 +08:00
internal EcsGroup ( EcsWorld world , int denseCapacity )
2023-04-08 21:29:18 +08:00
{
2023-04-15 00:23:46 +08:00
_source = world ;
2023-04-08 21:29:18 +08:00
_source . RegisterGroup ( this ) ;
2024-03-02 21:59:02 +08:00
_dense = new int [ denseCapacity ] ;
2023-04-18 19:35:42 +08:00
_sparse = new int [ world . Capacity ] ;
2023-02-13 21:11:54 +08:00
}
2024-03-02 04:20:34 +08:00
public void Dispose ( )
{
_source . ReleaseGroup ( this ) ;
}
2023-02-13 21:11:54 +08:00
#endregion
2023-06-01 19:13:04 +08:00
#region Has / IndexOf
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-24 16:48:18 +08:00
public bool Has ( int entityID )
2023-03-30 10:46:57 +08:00
{
2024-09-03 17:30:26 +08:00
return _sparse [ entityID ] ! = 0 ;
2023-04-01 20:45:37 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf ( int entityID )
{
return _sparse [ entityID ] ;
2023-03-30 10:46:57 +08:00
}
#endregion
2023-04-08 21:29:18 +08:00
#region Add / Remove
2024-03-02 04:20:34 +08:00
public void AddUnchecked ( int entityID )
{
2024-08-23 22:31:43 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( Has ( entityID ) ) { Throw . Group_AlreadyContains ( entityID ) ; }
#endif
2024-03-02 04:20:34 +08:00
Add_Internal ( entityID ) ;
}
2023-12-22 18:11:41 +08:00
public bool Add ( int entityID )
2023-02-13 21:11:54 +08:00
{
2023-12-22 18:11:41 +08:00
if ( Has ( entityID ) )
2024-03-02 04:20:34 +08:00
{
2023-12-22 18:11:41 +08:00
return false ;
2024-03-02 04:20:34 +08:00
}
Add_Internal ( entityID ) ;
2023-12-22 18:11:41 +08:00
return true ;
2023-04-01 20:45:37 +08:00
}
2023-04-01 22:18:40 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-08-23 22:31:43 +08:00
private void Add_Internal ( int entityID )
2023-04-01 20:45:37 +08:00
{
if ( + + _count > = _dense . Length )
2024-03-02 04:20:34 +08:00
{
2023-03-30 10:46:57 +08:00
Array . Resize ( ref _dense , _dense . Length < < 1 ) ;
2024-03-02 04:20:34 +08:00
}
2023-03-30 10:46:57 +08:00
_dense [ _count ] = entityID ;
_sparse [ entityID ] = _count ;
2023-02-13 21:11:54 +08:00
}
2024-03-02 04:20:34 +08:00
public void RemoveUnchecked ( int entityID )
{
2024-09-04 12:02:18 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( Has ( entityID ) = = false ) { Throw . Group_DoesNotContain ( entityID ) ; }
#endif
2024-03-02 04:20:34 +08:00
Remove_Internal ( entityID ) ;
}
2023-12-22 18:11:41 +08:00
public bool Remove ( int entityID )
2023-04-01 20:45:37 +08:00
{
2024-03-02 04:20:34 +08:00
if ( Has ( entityID ) = = false )
{
2023-12-22 18:11:41 +08:00
return false ;
2024-03-02 04:20:34 +08:00
}
Remove_Internal ( entityID ) ;
2023-12-22 18:11:41 +08:00
return true ;
2023-04-01 20:45:37 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-04 12:02:18 +08:00
private void Remove_Internal ( int entityID )
2023-02-13 21:11:54 +08:00
{
2023-03-30 10:46:57 +08:00
_dense [ _sparse [ entityID ] ] = _dense [ _count ] ;
_sparse [ _dense [ _count - - ] ] = _sparse [ entityID ] ;
_sparse [ entityID ] = 0 ;
2023-02-13 21:11:54 +08:00
}
2023-04-24 16:48:18 +08:00
public void RemoveUnusedEntityIDs ( )
{
2024-03-02 04:57:52 +08:00
foreach ( var entityID in this )
2023-04-24 16:48:18 +08:00
{
2024-03-02 04:57:52 +08:00
if ( _source . IsUsed ( entityID ) = = false )
2024-03-02 04:20:34 +08:00
{
2024-03-02 04:57:52 +08:00
Remove_Internal ( entityID ) ;
2024-03-02 04:20:34 +08:00
}
2023-04-24 16:48:18 +08:00
}
}
2023-02-13 21:11:54 +08:00
#endregion
2023-05-27 15:59:46 +08:00
#region Clear
2023-04-15 00:23:46 +08:00
public void Clear ( )
{
2024-03-02 06:07:50 +08:00
if ( _count = = 0 )
2024-03-02 04:57:52 +08:00
{
return ;
}
2024-03-02 21:18:45 +08:00
for ( int i = 1 ; i < = _count ; i + + )
2024-03-02 04:20:34 +08:00
{
2024-03-02 21:14:30 +08:00
_sparse [ _dense [ i ] ] = 0 ;
2024-03-02 04:20:34 +08:00
}
2024-03-02 21:14:30 +08:00
_count = 0 ;
2023-04-15 00:23:46 +08:00
}
#endregion
2024-03-02 21:57:25 +08:00
#region Upsize
public void Upsize ( int minSize )
{
if ( minSize > = _dense . Length )
{
Array . Resize ( ref _dense , ArrayUtility . NormalizeSizeToPowerOfTwo_ClampOverflow ( minSize ) ) ;
}
}
2024-03-03 22:46:09 +08:00
2024-03-02 21:57:25 +08:00
#endregion
2024-03-02 17:12:35 +08:00
#region CopyFrom / Clone / Slice / ToSpan / ToArray
2023-04-08 23:01:10 +08:00
public void CopyFrom ( EcsGroup group )
2023-04-08 21:29:18 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:20:34 +08:00
if ( group . World ! = _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-09 02:52:39 +08:00
#endif
2023-05-30 18:30:10 +08:00
if ( _count > 0 )
2024-03-02 04:20:34 +08:00
{
2023-04-17 22:58:52 +08:00
Clear ( ) ;
2024-03-02 04:20:34 +08:00
}
2024-08-23 22:31:43 +08:00
foreach ( var entityID in group )
2024-03-02 04:20:34 +08:00
{
2024-08-23 22:31:43 +08:00
Add_Internal ( entityID ) ;
2024-03-02 04:20:34 +08:00
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyFrom ( EcsReadonlyGroup group )
{
CopyFrom ( group . GetSource_Internal ( ) ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyFrom ( EcsSpan span )
{
if ( _count > 0 )
{
Clear ( ) ;
}
2024-03-02 04:22:29 +08:00
for ( int i = 0 ; i < span . Count ; i + + )
2024-03-02 04:20:34 +08:00
{
Add_Internal ( span [ i ] ) ;
}
2023-04-09 02:52:39 +08:00
}
2024-03-02 06:07:50 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-03 17:30:26 +08:00
public void CopyTo ( int [ ] array , int arrayIndex )
{
foreach ( var item in this )
{
array [ arrayIndex + + ] = item ;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-15 00:23:46 +08:00
public EcsGroup Clone ( )
2023-04-09 02:52:39 +08:00
{
2023-06-01 20:14:34 +08:00
EcsGroup result = _source . GetFreeGroup ( ) ;
2023-04-15 00:23:46 +08:00
result . CopyFrom ( this ) ;
return result ;
2023-04-08 23:01:10 +08:00
}
2024-03-02 06:07:50 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsSpan Slice ( int start )
2023-12-31 13:07:53 +08:00
{
2024-08-23 23:00:40 +08:00
return Slice ( start , _count - start ) ;
2023-12-31 13:07:53 +08:00
}
2024-03-02 06:07:50 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsSpan Slice ( int start , int length )
2023-06-01 19:13:04 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-08-23 23:00:40 +08:00
if ( start < 0 | | start + length > _count ) { Throw . ArgumentOutOfRange ( ) ; }
2023-06-01 19:13:04 +08:00
#endif
2024-08-23 23:00:40 +08:00
return new EcsSpan ( WorldID , _dense , start + 1 , length ) ;
2023-06-01 19:13:04 +08:00
}
2024-03-02 06:07:50 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsSpan ToSpan ( )
{
return new EcsSpan ( WorldID , _dense , 1 , _count ) ;
}
public int [ ] ToArray ( )
{
int [ ] result = new int [ _count ] ;
Array . Copy ( _dense , 1 , result , 0 , _count ) ;
return result ;
}
2024-08-31 14:51:13 +08:00
public int ToArray ( ref int [ ] dynamicBuffer )
{
if ( dynamicBuffer . Length < _count )
{
Array . Resize ( ref dynamicBuffer , ArrayUtility . NormalizeSizeToPowerOfTwo ( _count ) ) ;
}
int i = 0 ;
foreach ( var e in this )
{
dynamicBuffer [ i + + ] = e ;
}
return i ;
}
public void ToCollection ( ICollection < int > collection )
{
foreach ( var e in this )
{
collection . Add ( e ) ;
}
}
2023-04-15 00:23:46 +08:00
#endregion
2023-04-01 21:16:08 +08:00
2023-04-08 23:01:10 +08:00
#region Set operations
2023-12-22 18:11:41 +08:00
#region UnionWith
2023-04-08 23:01:10 +08:00
/// <summary>as Union sets</summary>
2023-04-15 00:23:46 +08:00
public void UnionWith ( EcsGroup group )
2023-04-09 02:52:39 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-09-03 17:30:26 +08:00
if ( _source ! = group . _source ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2023-04-08 23:01:10 +08:00
#endif
2024-09-03 17:30:26 +08:00
foreach ( var entityID in group ) { UnionWithStep ( entityID ) ; }
2023-04-08 21:29:18 +08:00
}
2024-03-02 04:57:52 +08:00
/// <summary>as Union sets</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-07 17:18:35 +08:00
public void UnionWith ( EcsReadonlyGroup group ) { UnionWith ( group . GetSource_Internal ( ) ) ; }
2024-03-02 04:57:52 +08:00
/// <summary>as Union sets</summary>
2023-12-24 15:48:24 +08:00
public void UnionWith ( EcsSpan span )
2023-12-22 18:11:41 +08:00
{
2023-12-24 15:48:24 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2023-12-24 15:48:24 +08:00
#endif
2024-09-03 17:30:26 +08:00
foreach ( var entityID in span ) { UnionWithStep ( entityID ) ; }
}
public void UnionWith ( IEnumerable < int > other )
{
foreach ( var entityID in other ) { UnionWithStep ( entityID ) ; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnionWithStep ( int entityID )
{
if ( Has ( entityID ) = = false )
2023-12-22 18:11:41 +08:00
{
2024-09-03 17:30:26 +08:00
Add_Internal ( entityID ) ;
2023-12-22 18:11:41 +08:00
}
}
#endregion
2023-04-15 00:23:46 +08:00
2023-12-22 18:11:41 +08:00
#region ExceptWith
2023-04-08 23:01:10 +08:00
/// <summary>as Except sets</summary>
2023-04-15 00:23:46 +08:00
public void ExceptWith ( EcsGroup group )
2023-04-08 21:29:18 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-09-03 17:30:26 +08:00
if ( _source ! = group . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-09 02:52:39 +08:00
#endif
2024-09-03 17:30:26 +08:00
if ( group . Count > Count ) //мини оптимизация, итеррируемся по короткому списку
2023-12-22 18:11:41 +08:00
{
2023-12-24 18:05:30 +08:00
for ( int i = _count ; i > 0 ; i - - ) //итерация в обратном порядке исключает ошибки при удалении элементов
{
2024-03-02 04:57:52 +08:00
int entityID = _dense [ i ] ;
if ( group . Has ( entityID ) )
{
Remove_Internal ( entityID ) ;
}
2023-12-24 18:05:30 +08:00
}
2023-12-22 18:11:41 +08:00
}
else
{
2024-09-03 17:30:26 +08:00
foreach ( var entityID in group ) { ExceptWithStep_Internal ( entityID ) ; }
2023-12-22 18:11:41 +08:00
}
}
2024-03-02 04:57:52 +08:00
/// <summary>as Except sets</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExceptWith ( EcsReadonlyGroup group ) { ExceptWith ( group . GetSource_Internal ( ) ) ; }
/// <summary>as Except sets</summary>
2023-12-24 15:48:24 +08:00
public void ExceptWith ( EcsSpan span )
2023-12-22 18:11:41 +08:00
{
2023-12-24 15:48:24 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-12-24 15:48:24 +08:00
#endif
2024-09-03 17:30:26 +08:00
foreach ( var entityID in span ) { ExceptWithStep_Internal ( entityID ) ; }
}
public void ExceptWith ( IEnumerable < int > other )
{
foreach ( var entityID in other ) { ExceptWithStep_Internal ( entityID ) ; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ExceptWithStep_Internal ( int entityID )
{
if ( Has ( entityID ) )
2023-12-22 18:11:41 +08:00
{
2024-09-03 17:30:26 +08:00
Remove_Internal ( entityID ) ;
2023-12-22 18:11:41 +08:00
}
2023-04-09 02:52:39 +08:00
}
2023-12-22 18:11:41 +08:00
#endregion
2023-04-15 00:23:46 +08:00
2023-12-22 18:11:41 +08:00
#region IntersectWith
2023-04-08 23:01:10 +08:00
/// <summary>as Intersect sets</summary>
2023-06-05 00:23:18 +08:00
public void IntersectWith ( EcsGroup group )
2023-04-09 02:52:39 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-09-03 17:30:26 +08:00
if ( _source ! = group . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-08 21:29:18 +08:00
#endif
2023-12-24 18:05:30 +08:00
for ( int i = _count ; i > 0 ; i - - ) //итерация в обратном порядке исключает ошибки при удалении элементов
{
2024-03-02 04:57:52 +08:00
int entityID = _dense [ i ] ;
if ( group . Has ( entityID ) = = false )
{
Remove_Internal ( entityID ) ;
}
2023-12-24 18:05:30 +08:00
}
2023-04-08 21:29:18 +08:00
}
2024-03-02 04:57:52 +08:00
/// <summary>as Intersect sets</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IntersectWith ( EcsReadonlyGroup group ) { IntersectWith ( group . GetSource_Internal ( ) ) ; }
2024-09-03 17:30:26 +08:00
/// <summary>as Intersect sets</summary>
2024-10-31 16:27:53 +08:00
public void IntersectWith ( EcsSpan span )
2023-04-09 02:52:39 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-08 21:29:18 +08:00
#endif
2024-09-03 17:30:26 +08:00
foreach ( var entityID in span )
2024-03-02 04:57:52 +08:00
{
if ( Has ( entityID ) )
{
2024-09-03 17:30:26 +08:00
Mark_Internal ( entityID ) ;
2024-03-02 04:57:52 +08:00
}
2024-09-03 17:30:26 +08:00
}
ClearUnmarked_Internal ( ) ;
}
public void IntersectWith ( IEnumerable < int > other )
{
if ( other is ISet < int > set )
{
for ( int i = _count ; i > 0 ; i - - ) //итерация в обратном порядке исключает ошибки при удалении элементов
2024-03-02 04:57:52 +08:00
{
2024-09-03 17:30:26 +08:00
int entityID = _dense [ i ] ;
if ( set . Contains ( entityID ) = = false )
{
Remove_Internal ( entityID ) ;
}
2024-03-02 04:57:52 +08:00
}
}
2024-09-04 12:11:35 +08:00
else
2024-09-03 17:30:26 +08:00
{
foreach ( var entityID in other )
{
if ( Has ( entityID ) )
{
Mark_Internal ( entityID ) ;
}
}
ClearUnmarked_Internal ( ) ;
}
}
#endregion
#region SymmetricExceptWith
/// <summary>as Symmetric Except sets</summary>
public void SymmetricExceptWith ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( _source ! = group . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
#endif
foreach ( var entityID in group ) { SymmetricExceptWithStep_Internal ( entityID ) ; }
2023-04-08 21:29:18 +08:00
}
2024-03-02 04:57:52 +08:00
/// <summary>as Symmetric Except sets</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SymmetricExceptWith ( EcsReadonlyGroup group ) { SymmetricExceptWith ( group . GetSource_Internal ( ) ) ; }
2024-09-03 17:30:26 +08:00
/// <summary>as Symmetric Except sets</summary>
public void SymmetricExceptWith ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2024-09-03 17:30:26 +08:00
#endif
foreach ( var entityID in span ) { SymmetricExceptWithStep_Internal ( entityID ) ; }
}
public void SymmetricExceptWith ( IEnumerable < int > other )
{
foreach ( var entityID in other ) { SymmetricExceptWithStep_Internal ( entityID ) ; }
}
private void SymmetricExceptWithStep_Internal ( int entityID )
{
if ( Has ( entityID ) )
{
Remove_Internal ( entityID ) ;
}
else
{
Add_Internal ( entityID ) ;
}
}
2023-12-22 18:11:41 +08:00
#endregion
2023-12-20 19:15:48 +08:00
2023-12-22 18:11:41 +08:00
#region Inverse
2023-06-01 20:14:34 +08:00
public void Inverse ( )
{
2024-03-02 21:45:09 +08:00
if ( _count = = 0 )
2024-03-02 21:14:30 +08:00
{
foreach ( var entityID in _source . Entities )
{
Add_Internal ( entityID ) ;
}
return ;
}
2024-03-02 04:57:52 +08:00
foreach ( var entityID in _source . Entities )
{
if ( Has ( entityID ) )
{
Remove_Internal ( entityID ) ;
}
2023-06-01 20:14:34 +08:00
else
2024-03-02 04:57:52 +08:00
{
Add_Internal ( entityID ) ;
}
}
2023-06-01 20:14:34 +08:00
}
2023-12-22 18:11:41 +08:00
#endregion
2023-12-20 19:15:48 +08:00
2023-12-22 18:11:41 +08:00
#region SetEquals
2023-12-20 19:15:48 +08:00
public bool SetEquals ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:57:52 +08:00
if ( _source ! = group . World ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-12-20 19:15:48 +08:00
#endif
2024-09-03 17:30:26 +08:00
if ( group . Count ! = Count ) { return false ; }
2024-03-02 04:57:52 +08:00
foreach ( var entityID in group )
{
if ( Has ( entityID ) = = false )
{
2023-12-20 19:15:48 +08:00
return false ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-20 19:15:48 +08:00
return true ;
}
2024-03-02 04:57:52 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool SetEquals ( EcsReadonlyGroup group ) { return SetEquals ( group . GetSource_Internal ( ) ) ; }
2023-12-24 16:04:24 +08:00
public bool SetEquals ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-12-24 16:04:24 +08:00
#endif
2024-09-03 17:30:26 +08:00
if ( span . Count ! = Count ) { return false ; }
foreach ( var entityID in span )
2024-03-02 04:57:52 +08:00
{
2024-09-03 17:30:26 +08:00
if ( Has ( entityID ) = = false )
{
return false ;
}
2024-03-02 04:57:52 +08:00
}
2024-09-03 17:30:26 +08:00
return true ;
}
public bool SetEquals ( IEnumerable < int > other )
{
if ( other is ICollection collection & & collection . Count ! = Count ) { return false ; }
foreach ( var entityID in other )
2024-03-02 04:57:52 +08:00
{
if ( Has ( entityID ) = = false )
{
2023-12-24 16:04:24 +08:00
return false ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-24 16:04:24 +08:00
return true ;
}
2023-12-22 18:11:41 +08:00
#endregion
2023-12-20 19:15:48 +08:00
2023-12-22 18:11:41 +08:00
#region Overlaps
2023-12-20 19:15:48 +08:00
public bool Overlaps ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( _source ! = group . World ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
#endif
2024-01-07 19:32:16 +08:00
if ( group . Count > Count )
2023-12-24 16:04:24 +08:00
{
2024-03-02 04:57:52 +08:00
foreach ( var entityID in this )
{
if ( group . Has ( entityID ) )
{
2023-12-24 16:04:24 +08:00
return true ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-24 16:04:24 +08:00
}
else
{
2024-03-02 04:57:52 +08:00
foreach ( var entityID in group )
{
if ( Has ( entityID ) )
{
2023-12-24 16:04:24 +08:00
return true ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-24 16:04:24 +08:00
}
return false ;
}
2024-03-02 04:57:52 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Overlaps ( EcsReadonlyGroup group ) { return Overlaps ( group . GetSource_Internal ( ) ) ; }
2023-12-24 16:04:24 +08:00
public bool Overlaps ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2023-12-24 16:04:24 +08:00
#endif
2024-03-02 04:57:52 +08:00
foreach ( var entityID in span )
{
if ( Has ( entityID ) )
{
2023-12-20 19:15:48 +08:00
return true ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-20 19:15:48 +08:00
return false ;
}
2024-09-03 17:30:26 +08:00
public bool Overlaps ( IEnumerable < int > other )
{
foreach ( var entityID in other )
{
if ( Has ( entityID ) )
{
return true ;
}
}
return false ;
}
2023-12-22 18:11:41 +08:00
#endregion
2023-12-20 19:15:48 +08:00
2024-09-03 17:30:26 +08:00
#region IsSubsetOf / IsProperSubsetOf
2023-12-20 19:15:48 +08:00
public bool IsSubsetOf ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-09-03 17:30:26 +08:00
if ( _source ! = group . _source ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2023-12-20 19:15:48 +08:00
#endif
2024-09-03 17:30:26 +08:00
if ( Count = = 0 ) { return true ; }
if ( group . Count < Count ) { return false ; }
return IsSubsetOf_Internal ( group ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSubsetOf ( EcsReadonlyGroup group ) { return IsSubsetOf ( group . GetSource_Internal ( ) ) ; }
public bool IsSubsetOf ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2024-09-03 17:30:26 +08:00
#endif
if ( Count = = 0 ) { return true ; }
if ( span . Count < Count ) { return false ; }
return IsSubsetOf_Internal ( span ) ;
}
public bool IsSubsetOf ( IEnumerable < int > other )
{
if ( Count = = 0 ) { return true ; }
if ( other is ICollection collection & & collection . Count < Count ) { return false ; }
return IsSubsetOf_Internal ( other ) ;
}
// ================================================================================
public bool IsProperSubsetOf ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( _source ! = group . _source ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
#endif
if ( Count = = 0 ) { return true ; }
if ( group . Count < = Count ) { return false ; }
return IsSubsetOf_Internal ( group ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSubsetOf ( EcsReadonlyGroup group ) { return IsProperSubsetOf ( group . GetSource_Internal ( ) ) ; }
public bool IsProperSubsetOf ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2024-09-03 17:30:26 +08:00
#endif
if ( Count = = 0 ) { return true ; }
if ( span . Count < = Count ) { return false ; }
return IsSubsetOf_Internal ( span ) ;
}
public bool IsProperSubsetOf ( IEnumerable < int > other )
{
if ( Count = = 0 ) { return true ; }
if ( other is ICollection collection & & collection . Count < = Count ) { return false ; }
return IsSubsetOf_Internal ( other ) ;
}
// ================================================================================
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsSubsetOf_Internal ( EcsGroup group )
{
2024-03-02 04:57:52 +08:00
foreach ( var entityID in this )
{
if ( group . Has ( entityID ) = = false )
{
2023-12-20 19:15:48 +08:00
return false ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-20 19:15:48 +08:00
return true ;
}
2024-03-02 04:57:52 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-03 17:30:26 +08:00
private bool IsSubsetOf_Internal ( EcsSpan span )
{
int uniqueCount = 0 ;
foreach ( var entityID in span )
{
if ( Has ( entityID ) = = false )
{
uniqueCount + + ;
}
}
return uniqueCount = = Count ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsSubsetOf_Internal ( IEnumerable < int > other )
{
int uniqueCount = 0 ;
foreach ( var entityID in other )
{
if ( Has ( entityID ) = = false )
{
uniqueCount + + ;
}
}
return uniqueCount = = Count ;
}
2023-12-22 18:11:41 +08:00
#endregion
2023-12-20 19:15:48 +08:00
2024-09-03 17:30:26 +08:00
#region IsSupersetOf / IsProperSupersetOf
2023-12-20 19:15:48 +08:00
public bool IsSupersetOf ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-09-03 17:30:26 +08:00
if ( _source ! = group . _source ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2023-12-20 19:15:48 +08:00
#endif
2024-09-03 17:30:26 +08:00
if ( group . Count > Count ) { return false ; }
return IsSupersetOf_Internal ( group ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSupersetOf ( EcsReadonlyGroup group ) { return IsSupersetOf ( group . GetSource_Internal ( ) ) ; }
public bool IsSupersetOf ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2024-09-03 17:30:26 +08:00
#endif
if ( span . Count > Count ) { return false ; }
return IsSupersetOf_Internal ( span ) ;
}
public bool IsSupersetOf ( IEnumerable < int > other )
{
if ( other is ICollection collection & & collection . Count > Count ) { return false ; }
return IsSupersetOf_Internal ( other ) ;
}
// ================================================================================
public bool IsProperSupersetOf ( EcsGroup group )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( _source ! = group . _source ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
#endif
if ( group . Count > = Count ) { return false ; }
return IsSupersetOf_Internal ( group ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsProperSupersetOf ( EcsReadonlyGroup group ) { return IsProperSupersetOf ( group . GetSource_Internal ( ) ) ; }
public bool IsProperSupersetOf ( EcsSpan span )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( _source . ID ! = span . WorldID ) Throw . Group_ArgumentDifferentWorldsException ( ) ;
2024-09-03 17:30:26 +08:00
#endif
if ( span . Count > = Count ) { return false ; }
return IsSupersetOf_Internal ( span ) ;
}
public bool IsProperSupersetOf ( IEnumerable < int > other )
{
if ( other is ICollection collection & & collection . Count > = Count ) { return false ; }
return IsSupersetOf_Internal ( other ) ;
}
// ================================================================================
private bool IsSupersetOf_Internal ( EcsGroup group )
{
foreach ( var entityID in group )
2024-03-02 04:57:52 +08:00
{
2024-09-03 17:30:26 +08:00
if ( Has ( entityID ) = = false )
{
return false ;
}
2024-03-02 04:57:52 +08:00
}
2024-09-03 17:30:26 +08:00
return true ;
}
private bool IsSupersetOf_Internal ( EcsSpan span )
{
foreach ( var entityID in span )
{
if ( Has ( entityID ) = = false )
{
return false ;
}
}
return true ;
}
private bool IsSupersetOf_Internal ( IEnumerable < int > other )
{
foreach ( var entityID in other )
2024-03-02 04:57:52 +08:00
{
if ( Has ( entityID ) = = false )
{
2023-12-20 19:15:48 +08:00
return false ;
2024-03-02 04:57:52 +08:00
}
}
2023-12-20 19:15:48 +08:00
return true ;
}
2023-02-14 03:26:34 +08:00
#endregion
2023-12-22 18:11:41 +08:00
#endregion
2023-04-10 22:22:17 +08:00
#region Static Set operations
2024-09-03 17:30:26 +08:00
#region Union
2023-06-01 19:13:04 +08:00
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Union ( EcsGroup a , EcsGroup b )
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:57:52 +08:00
if ( a . _source ! = b . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-06-01 19:13:04 +08:00
#endif
2023-06-01 20:14:34 +08:00
EcsGroup result = a . _source . GetFreeGroup ( ) ;
2024-03-02 04:57:52 +08:00
foreach ( var entityID in a )
{
result . Add_Internal ( entityID ) ;
}
foreach ( var entityID in b )
{
result . Add ( entityID ) ;
}
2023-06-01 19:13:04 +08:00
return result ;
}
2024-09-03 17:30:26 +08:00
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
2024-03-02 04:57:52 +08:00
public static EcsGroup Union ( EcsReadonlyGroup a , EcsReadonlyGroup b )
{
return Union ( a . GetSource_Internal ( ) , b . GetSource_Internal ( ) ) ;
}
2024-09-03 17:30:26 +08:00
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Union ( EcsSpan a , EcsSpan b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( a . WorldID ! = b . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
#endif
EcsGroup result = a . World . GetFreeGroup ( ) ;
foreach ( var entityID in a )
{
result . Add_Internal ( entityID ) ;
}
foreach ( var entityID in b )
{
result . Add ( entityID ) ;
}
return result ;
}
#endregion
2024-03-02 04:57:52 +08:00
2024-09-03 17:30:26 +08:00
#region Except
2023-04-10 22:22:17 +08:00
/// <summary>as Except sets</summary>
2023-04-15 00:23:46 +08:00
/// <returns>new group from pool</returns>
public static EcsGroup Except ( EcsGroup a , EcsGroup b )
2023-04-10 22:22:17 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:57:52 +08:00
if ( a . _source ! = b . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-10 22:22:17 +08:00
#endif
2023-06-01 20:14:34 +08:00
EcsGroup result = a . _source . GetFreeGroup ( ) ;
2024-03-02 04:57:52 +08:00
foreach ( var entityID in a )
{
if ( b . Has ( entityID ) = = false )
{
result . Add_Internal ( entityID ) ;
}
}
2023-04-15 00:23:46 +08:00
return result ;
2023-04-10 22:22:17 +08:00
}
2024-09-03 17:30:26 +08:00
/// <summary>as Except sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Except ( EcsSpan a , EcsGroup b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( a . WorldID ! = b . _source . ID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2024-09-03 17:30:26 +08:00
#endif
EcsGroup result = b . _source . GetFreeGroup ( ) ;
foreach ( var entityID in a )
{
if ( b . Has ( entityID ) = = false )
{
result . Add_Internal ( entityID ) ;
}
}
return result ;
}
/// <summary>as Except sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Except ( EcsSpan a , EcsSpan b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( a . WorldID ! = b . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
#endif
EcsGroup result = a . World . GetFreeGroup ( ) ;
result . CopyFrom ( a ) ;
result . ExceptWith ( b ) ;
return result ;
}
/// <summary>as Except sets</summary>
/// <returns>new group from pool</returns>
2024-03-02 04:57:52 +08:00
public static EcsGroup Except ( EcsReadonlyGroup a , EcsReadonlyGroup b )
{
return Except ( a . GetSource_Internal ( ) , b . GetSource_Internal ( ) ) ;
}
2024-09-03 17:30:26 +08:00
#endregion
2024-03-02 04:57:52 +08:00
2024-09-03 17:30:26 +08:00
#region Intersect
2023-04-10 22:22:17 +08:00
/// <summary>as Intersect sets</summary>
2023-04-15 00:23:46 +08:00
/// <returns>new group from pool</returns>
2023-06-05 00:23:18 +08:00
public static EcsGroup Intersect ( EcsGroup a , EcsGroup b )
2023-04-10 22:22:17 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:57:52 +08:00
if ( a . _source ! = b . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-10 22:22:17 +08:00
#endif
2023-06-01 20:14:34 +08:00
EcsGroup result = a . _source . GetFreeGroup ( ) ;
2024-03-02 04:57:52 +08:00
foreach ( var entityID in a )
{
if ( b . Has ( entityID ) )
{
result . Add_Internal ( entityID ) ;
}
}
2023-04-15 00:23:46 +08:00
return result ;
}
2024-09-03 17:30:26 +08:00
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Intersect ( EcsSpan a , EcsGroup b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-10-31 16:27:53 +08:00
if ( a . WorldID ! = b . _source . ID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2024-09-03 17:30:26 +08:00
#endif
EcsGroup result = b . _source . GetFreeGroup ( ) ;
foreach ( var entityID in a )
{
if ( b . Has ( entityID ) )
{
result . Add_Internal ( entityID ) ;
}
}
return result ;
}
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Intersect ( EcsGroup a , EcsSpan b )
{
//операция симметричная, можно просто переставить параметры
return Intersect ( b , a ) ;
}
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup Intersect ( EcsSpan a , EcsSpan b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( a . WorldID ! = b . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
#endif
EcsGroup result = b . World . GetFreeGroup ( ) ;
result . CopyFrom ( a ) ;
result . IntersectWith ( b ) ;
return result ;
}
/// <summary>as Intersect sets</summary>
/// <returns>new group from pool</returns>
2024-03-02 04:57:52 +08:00
public static EcsGroup Intersect ( EcsReadonlyGroup a , EcsReadonlyGroup b )
{
return Intersect ( a . GetSource_Internal ( ) , b . GetSource_Internal ( ) ) ;
}
2024-09-03 17:30:26 +08:00
#endregion
2023-06-01 19:13:04 +08:00
2024-09-03 17:30:26 +08:00
#region SymmetricExcept
2023-06-01 19:13:04 +08:00
/// <summary>as Symmetric Except sets</summary>
2023-04-15 00:23:46 +08:00
/// <returns>new group from pool</returns>
2023-06-05 00:23:18 +08:00
public static EcsGroup SymmetricExcept ( EcsGroup a , EcsGroup b )
2023-04-15 00:23:46 +08:00
{
2023-06-02 01:20:46 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 04:57:52 +08:00
if ( a . _source ! = b . _source ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
2023-04-15 00:23:46 +08:00
#endif
2023-06-01 20:14:34 +08:00
EcsGroup result = a . _source . GetFreeGroup ( ) ;
2024-03-02 04:57:52 +08:00
foreach ( var entityID in a )
{
2024-09-03 17:30:26 +08:00
if ( b . Has ( entityID ) = = false )
2024-03-02 04:57:52 +08:00
{
result . Add_Internal ( entityID ) ;
}
}
foreach ( var entityID in b )
{
2024-09-03 17:30:26 +08:00
if ( a . Has ( entityID ) = = false )
2024-03-02 04:57:52 +08:00
{
result . Add_Internal ( entityID ) ;
}
}
2023-04-15 00:23:46 +08:00
return result ;
2023-04-10 22:22:17 +08:00
}
2024-09-03 17:30:26 +08:00
/// <summary>as Symmetric Except sets</summary>
/// <returns>new group from pool</returns>
public static EcsGroup SymmetricExcept ( EcsSpan a , EcsSpan b )
{
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
if ( a . WorldID ! = b . WorldID ) { Throw . Group_ArgumentDifferentWorldsException ( ) ; }
#endif
EcsGroup result = a . World . GetFreeGroup ( ) ;
result . CopyFrom ( a ) ;
foreach ( var entityID in b )
{
if ( result . Has ( entityID ) )
{
result . Mark_Internal ( entityID ) ;
}
else
{
result . Add_Internal ( entityID ) ;
}
}
result . ClearMarked_Internal ( ) ;
return result ;
}
2024-03-02 04:57:52 +08:00
public static EcsGroup SymmetricExcept ( EcsReadonlyGroup a , EcsReadonlyGroup b )
{
return SymmetricExcept ( a . GetSource_Internal ( ) , b . GetSource_Internal ( ) ) ;
}
2024-09-03 17:30:26 +08:00
#endregion
2023-06-01 20:14:34 +08:00
2024-09-03 17:30:26 +08:00
#region Inverse
2023-06-01 20:14:34 +08:00
public static EcsGroup Inverse ( EcsGroup a )
{
EcsGroup result = a . _source . GetFreeGroup ( ) ;
2024-08-23 22:31:43 +08:00
foreach ( var entityID in a . _source . Entities )
2024-03-02 04:57:52 +08:00
{
2024-08-23 22:31:43 +08:00
if ( a . Has ( entityID ) = = false )
2024-03-02 04:57:52 +08:00
{
2024-08-23 22:31:43 +08:00
result . Add_Internal ( entityID ) ;
2024-03-02 04:57:52 +08:00
}
}
2023-06-01 20:14:34 +08:00
return result ;
}
2024-03-02 04:57:52 +08:00
public static EcsGroup Inverse ( EcsReadonlyGroup a )
{
return Inverse ( a . GetSource_Internal ( ) ) ;
}
2024-09-03 17:30:26 +08:00
public static EcsGroup Inverse ( EcsSpan a )
{
EcsGroup result = a . World . GetFreeGroup ( ) ;
result . CopyFrom ( a . World . Entities ) ;
result . ExceptWith ( a ) ;
return result ;
}
#endregion
2023-04-10 22:22:17 +08:00
#endregion
2023-06-01 19:13:04 +08:00
#region Enumerator
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 21:02:32 +08:00
public Enumerator GetEnumerator ( ) { return new Enumerator ( this ) ; }
IEnumerator IEnumerable . GetEnumerator ( ) { return GetEnumerator ( ) ; }
IEnumerator < int > IEnumerable < int > . GetEnumerator ( ) { return GetEnumerator ( ) ; }
2023-12-24 15:40:19 +08:00
public struct Enumerator : IEnumerator < int >
2023-02-13 21:11:54 +08:00
{
2023-04-01 21:16:08 +08:00
private readonly int [ ] _dense ;
2023-12-31 13:07:53 +08:00
private uint _index ;
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-02-13 21:11:54 +08:00
public Enumerator ( EcsGroup group )
{
2023-04-01 21:16:08 +08:00
_dense = group . _dense ;
2024-03-02 21:02:32 +08:00
//для оптимизации компилятором
2023-12-31 21:02:53 +08:00
_index = ( uint ) ( group . _count > _dense . Length ? _dense . Length : group . _count ) + 1 ;
2023-02-13 21:11:54 +08:00
}
2023-04-23 15:57:35 +08:00
public int Current
2023-02-13 21:11:54 +08:00
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-07 17:18:35 +08:00
get { return _dense [ _index ] ; }
2023-02-13 21:11:54 +08:00
}
2024-03-02 21:02:32 +08:00
object IEnumerator . Current { get { return Current ; } }
2023-02-13 21:11:54 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-07 17:18:35 +08:00
public bool MoveNext ( ) { return - - _index > 0 ; } // проверка с учтом что отсчет начинается с индекса 1
2023-12-24 15:40:19 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-07 17:18:35 +08:00
void IDisposable . Dispose ( ) { }
void IEnumerator . Reset ( ) { throw new NotSupportedException ( ) ; }
2023-02-13 21:11:54 +08:00
}
#endregion
2023-04-08 23:01:10 +08:00
2024-09-04 12:11:35 +08:00
#region HiBitMarking
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Mark_Internal ( int entityID )
{
_sparse [ entityID ] | = int . MinValue ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsMark_Internal ( int entityID )
{
return ( _sparse [ entityID ] & int . MinValue ) = = int . MinValue ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unmark_Internal ( int entityID )
{
_sparse [ entityID ] & = int . MaxValue ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearUnmarked_Internal ( )
{
for ( int i = _count ; i > 0 ; i - - ) //итерация в обратном порядке исключает ошибки при удалении элементов
{
int entityID = _dense [ i ] ;
if ( IsMark_Internal ( entityID ) )
{
Unmark_Internal ( entityID ) ;
}
else
{
Remove_Internal ( entityID ) ;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearMarked_Internal ( )
{
for ( int i = _count ; i > 0 ; i - - ) //итерация в обратном порядке исключает ошибки при удалении элементов
{
int entityID = _dense [ i ] ;
if ( IsMark_Internal ( entityID ) )
{
Unmark_Internal ( entityID ) ; // Unmark_Internal должен быть до Remove_Internal
Remove_Internal ( entityID ) ;
}
}
}
#endregion
2023-12-24 15:40:19 +08:00
#region Other
2023-04-15 00:23:46 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public int First ( ) { return _dense [ 1 ] ; }
2023-12-23 20:17:28 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public int Last ( ) { return _dense [ _count ] ; }
2023-12-23 20:17:28 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 20:53:23 +08:00
internal void OnWorldResize_Internal ( int newSize )
{
Array . Resize ( ref _sparse , newSize ) ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void OnReleaseDelEntityBuffer_Internal ( ReadOnlySpan < int > buffer )
{
2024-09-04 12:02:18 +08:00
if ( _count < = 0 ) { return ; }
2024-03-02 20:53:23 +08:00
foreach ( var entityID in buffer )
{
if ( Has ( entityID ) )
{
Remove_Internal ( entityID ) ;
}
}
}
2024-03-02 06:07:50 +08:00
public override string ToString ( )
{
return CollectionUtility . EntitiesToString ( _dense . Skip ( 1 ) . Take ( _count ) , "group" ) ;
}
2024-09-04 12:02:18 +08:00
void ICollection < int > . Add ( int item ) { Add ( item ) ; }
bool ICollection < int > . Contains ( int item ) { return Has ( item ) ; }
2024-09-03 17:30:26 +08:00
2023-12-31 13:07:53 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public static implicit operator EcsReadonlyGroup ( EcsGroup a ) { return a . Readonly ; }
2023-12-31 13:07:53 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-03-02 04:20:34 +08:00
public static implicit operator EcsSpan ( EcsGroup a ) { return a . ToSpan ( ) ; }
2023-05-28 06:29:04 +08:00
internal class DebuggerProxy
2023-04-01 20:45:37 +08:00
{
2023-05-28 06:29:04 +08:00
private EcsGroup _group ;
2024-03-02 04:20:34 +08:00
public EcsWorld World { get { return _group . World ; } }
public bool IsReleased { get { return _group . IsReleased ; } }
2024-02-29 22:42:33 +08:00
public EntitySlotInfo [ ] Entities
2023-05-28 06:29:04 +08:00
{
get
{
2024-02-29 22:42:33 +08:00
EntitySlotInfo [ ] result = new EntitySlotInfo [ _group . Count ] ;
2023-05-28 06:29:04 +08:00
int i = 0 ;
foreach ( var e in _group )
2024-02-29 22:42:33 +08:00
{
result [ i + + ] = _group . World . GetEntitySlotInfoDebug ( e ) ;
}
2023-05-28 06:29:04 +08:00
return result ;
}
}
2024-03-02 04:20:34 +08:00
public int Count { get { return _group . Count ; } }
public int CapacityDense { get { return _group . CapacityDense ; } }
public override string ToString ( ) { return _group . ToString ( ) ; }
public DebuggerProxy ( EcsGroup group ) { _group = group ; }
2024-01-29 01:09:17 +08:00
public DebuggerProxy ( EcsReadonlyGroup group ) : this ( group . GetSource_Internal ( ) ) { }
2023-04-01 20:45:37 +08:00
}
2023-05-28 06:29:04 +08:00
#endregion
2023-04-01 20:45:37 +08:00
}
2023-05-28 06:29:04 +08:00
}