forked from altmp/coreclr-module
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AsyncEntityPool.cs
192 lines (163 loc) · 6.34 KB
/
AsyncEntityPool.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using AltV.Net.Async.Elements.Entities;
using AltV.Net.Elements.Entities;
using AltV.Net.Elements.Pools;
using AltV.Net.Shared;
namespace AltV.Net.Async
{
//TODO: write GetOrCreateAsync for BaseEntityPool
/*public partial class AltAsync
{
/// <summary>
/// Create entity async in entity pool, don't provide a IntPtr.Zero
/// </summary>
/// <param name="entityPool"></param>
/// <param name="entityPointer"></param>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
public static async Task<TEntity> CreateAsync<TEntity>(this IEntityPool<TEntity> entityPool,
IntPtr entityPointer) where TEntity : IEntity
{
var id = await Do(() => Entity.GetId(entityPointer));
entityPool.Create(entityPointer, id, out var entity);
return entity;
}
/// <summary>
/// Get or create entity async in entity pool, don't provide a IntPtr.Zero
/// </summary>
/// <param name="entityPool"></param>
/// <param name="entityPointer"></param>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
public static async Task<TEntity> GetOrCreateAsync<TEntity>(this IEntityPool<TEntity> entityPool,
IntPtr entityPointer) where TEntity : IEntity
{
if (entityPool.Get(entityPointer, out var entity)) return entity;
entity = await entityPool.CreateAsync(entityPointer);
return entity;
}
}*/
public abstract class AsyncEntityPool<TEntity> : IEntityPool<TEntity> where TEntity : class, IEntity
{
private readonly ConcurrentDictionary<IntPtr, TEntity> entities = new();
private readonly IEntityFactory<TEntity> entityFactory;
private readonly Dictionary<IntPtr, WeakReference<TEntity>> cache = new();
private readonly bool forceAsync;
public AsyncEntityPool(IEntityFactory<TEntity> entityFactory, bool forceAsync)
{
this.entityFactory = entityFactory;
this.forceAsync = forceAsync;
}
public abstract uint GetId(IntPtr entityPointer);
public TEntity Create(ICore core, IntPtr entityPointer, uint id)
{
if (entityPointer == IntPtr.Zero) return default;
if (entities.TryGetValue(entityPointer, out var entity)) return entity;
entity = entityFactory.Create(core, entityPointer, id);
Add(entity);
return entity;
}
public TEntity Create(ICore core, IntPtr entityPointer)
{
return Create(core, entityPointer, GetId(entityPointer));
}
public void Add(TEntity entity)
{
entities[entity.NativePointer] = entity;
if (forceAsync && entity is not AsyncEntity)
throw new Exception("Tried to add sync entity to async pool. Probably you used \"new Vehicle\" syntax (should be \"new AsyncVehicle\"), or didn't adapt your custom entity class to new Async API.");
OnAdd(entity);
}
public bool Remove(TEntity entity)
{
return Remove(entity.NativePointer);
}
//TODO: what should happen on failure
public bool Remove(IntPtr entityPointer)
{
if (!entities.TryRemove(entityPointer, out var entity) || entity is not IInternalBaseObject internalEntity || !entity.Exists) return false;
lock (entity)
{
if (AltShared.CacheEntities)
{
unsafe
{
var ptr = entity.Core.Library.Shared.BaseObject_TryCache(entity.BaseObjectNativePointer);
if (ptr != IntPtr.Zero)
{
internalEntity.SetCached(ptr);
cache[entity.NativePointer] = new WeakReference<TEntity>(entity);
}
}
}
entity.OnDestroy();
BaseObjectPool<TEntity>.SetEntityNoLongerExists(entity);
}
OnRemove(entity);
return true;
}
public TEntity Get(IntPtr entityPointer)
{
if (entities.TryGetValue(entityPointer, out var entity)) return entity;
lock (cache) {
if (cache.TryGetValue(entityPointer, out var cachedEntity))
{
if (cachedEntity.TryGetTarget(out entity))
{
return entity;
}
cache.Remove(entityPointer);
}
}
return default;
}
public TEntity GetOrCreate(ICore core, IntPtr entityPointer)
{
if (entityPointer == IntPtr.Zero)
{
return default;
}
if (Get(entityPointer) is { } entity) return entity;
return Create(core, entityPointer);
}
public TEntity GetOrCreate(ICore core, IntPtr entityPointer, uint id)
{
if (entityPointer == IntPtr.Zero)
{
return default;
}
if (Get(entityPointer) is { } entity) return entity;
return Create(core, entityPointer, id);
}
public IReadOnlyCollection<TEntity> GetAllEntities()
{
return (IReadOnlyCollection<TEntity>) entities.Values;
}
public KeyValuePair<IntPtr, TEntity>[] GetEntitiesArray()
{
return entities.ToArray();
}
public abstract void ForEach(IBaseObjectCallback<TEntity> baseObjectCallback);
public abstract Task ForEach(IAsyncBaseObjectCallback<TEntity> asyncBaseObjectCallback);
public virtual void OnAdd(TEntity entity)
{
}
public virtual void OnRemove(TEntity entity)
{
}
public void Dispose()
{
foreach (var entity in entities.Values)
{
if (!(entity is IInternalBaseObject internalEntity)) continue;
internalEntity.ClearData();
entity.OnDestroy();
OnRemove(entity);
}
entities.Clear();
}
}
}