Skip to content

Commit d53ef77

Browse files
Add experimental emit locked that doesn't get scheduled in tick scheduler but threadsafe against entity removes
1 parent 259720d commit d53ef77

File tree

8 files changed

+366
-1
lines changed

8 files changed

+366
-1
lines changed

api/AltV.Net.Async/AltAsync.Player.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,30 @@ await AltVAsync.Schedule(() =>
156156
mValueArgs.Dispose();
157157
}
158158

159+
public static bool EmitLocked(this IPlayer player, string eventName, params object[] args)
160+
{
161+
var mValues = MValueLocked.CreateFromObjectsLocked(args);
162+
var mValueArgs = MValue.Create(mValues);
163+
var eventNamePtr = AltNative.StringUtils.StringToHGlobalUtf8(eventName);
164+
var successfully = true;
165+
lock (player)
166+
{
167+
if (player.Exists)
168+
{
169+
Alt.Server.TriggerClientEvent(player, eventNamePtr, ref mValueArgs);
170+
}
171+
else
172+
{
173+
successfully = false;
174+
}
175+
}
176+
177+
Marshal.FreeHGlobal(eventNamePtr);
178+
MValue.Dispose(mValues);
179+
mValueArgs.Dispose();
180+
return successfully;
181+
}
182+
159183
public static Task<ReadOnlyPlayer> CopyAsync(this IPlayer player) =>
160184
AltVAsync.Schedule(player.Copy);
161185
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using AltV.Net.Elements.Entities;
5+
using AltV.Net.Elements.Pools;
6+
7+
namespace AltV.Net.Async
8+
{
9+
public abstract class AsyncBaseObjectPool<TBaseObject> : IBaseObjectPool<TBaseObject>
10+
where TBaseObject : IBaseObject
11+
{
12+
private readonly ConcurrentDictionary<IntPtr, TBaseObject> entities =
13+
new ConcurrentDictionary<IntPtr, TBaseObject>();
14+
15+
private readonly IBaseObjectFactory<TBaseObject> entityFactory;
16+
17+
protected AsyncBaseObjectPool(IBaseObjectFactory<TBaseObject> entityFactory)
18+
{
19+
this.entityFactory = entityFactory;
20+
}
21+
22+
public void Create(IntPtr entityPointer)
23+
{
24+
Add(entityFactory.Create(entityPointer));
25+
}
26+
27+
public void Create(IntPtr entityPointer, out TBaseObject entity)
28+
{
29+
entity = entityFactory.Create(entityPointer);
30+
Add(entity);
31+
}
32+
33+
//TODO: what should happen on failure
34+
public void Add(TBaseObject entity)
35+
{
36+
if (entities.TryAdd(entity.NativePointer, entity))
37+
{
38+
OnAdd(entity);
39+
}
40+
}
41+
42+
public bool Remove(TBaseObject entity)
43+
{
44+
return Remove(entity.NativePointer);
45+
}
46+
47+
public bool Remove(IntPtr entityPointer)
48+
{
49+
if (!entities.TryRemove(entityPointer, out var entity) || !entity.Exists) return false;
50+
lock (entity)
51+
{
52+
BaseObjectPool<TBaseObject>.SetEntityNoLongerExists(entity);
53+
}
54+
55+
OnRemove(entity);
56+
57+
return true;
58+
}
59+
60+
public bool Get(IntPtr entityPointer, out TBaseObject entity)
61+
{
62+
return entities.TryGetValue(entityPointer, out entity) && entity.Exists;
63+
}
64+
65+
public bool GetOrCreate(IntPtr entityPointer, out TBaseObject entity)
66+
{
67+
if (entityPointer == IntPtr.Zero)
68+
{
69+
entity = default;
70+
return false;
71+
}
72+
73+
if (entities.TryGetValue(entityPointer, out entity)) return entity.Exists;
74+
75+
Create(entityPointer, out entity);
76+
77+
return entity.Exists;
78+
}
79+
80+
public ICollection<TBaseObject> GetAllObjects()
81+
{
82+
return entities.Values;
83+
}
84+
85+
public virtual void OnAdd(TBaseObject entity)
86+
{
87+
}
88+
89+
public virtual void OnRemove(TBaseObject entity)
90+
{
91+
}
92+
}
93+
}

api/AltV.Net.Async/AsyncEntityPool.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,13 @@ public bool Remove(TEntity entity)
9797
public bool Remove(IntPtr entityPointer)
9898
{
9999
if (!entities.TryRemove(entityPointer, out var entity) || !entity.Exists) return false;
100-
BaseObjectPool<TEntity>.SetEntityNoLongerExists(entity);
100+
lock (entity)
101+
{
102+
BaseObjectPool<TEntity>.SetEntityNoLongerExists(entity);
103+
}
104+
101105
OnRemove(entity);
106+
102107
return true;
103108
}
104109

api/AltV.Net.Async/AsyncResource.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ public override IEntityPool<IVehicle> GetVehiclePool(IEntityFactory<IVehicle> ve
3737
return new AsyncVehiclePool(vehicleFactory);
3838
}
3939

40+
public override IBaseObjectPool<IBlip> GetBlipPool(IBaseObjectFactory<IBlip> blipFactory)
41+
{
42+
return new AsyncBlipPool(blipFactory);
43+
}
44+
45+
public override IBaseObjectPool<ICheckpoint> GetCheckpointPool(IBaseObjectFactory<ICheckpoint> checkpointFactory)
46+
{
47+
return new AsyncCheckpointPool(checkpointFactory);
48+
}
49+
50+
public override IBaseObjectPool<IVoiceChannel> GetVoiceChannelPool(IBaseObjectFactory<IVoiceChannel> voiceChannelFactory)
51+
{
52+
return new AsyncVoiceChannelPool(voiceChannelFactory);
53+
}
54+
4055
public override Module GetModule(IServer server, CSharpNativeResource cSharpNativeResource,
4156
IBaseBaseObjectPool baseBaseObjectPool,
4257
IBaseEntityPool baseEntityPool,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using AltV.Net.Elements.Entities;
2+
3+
namespace AltV.Net.Async.Elements.Pools
4+
{
5+
public class AsyncBlipPool : AsyncBaseObjectPool<IBlip>
6+
{
7+
public AsyncBlipPool(IBaseObjectFactory<IBlip> entityFactory) : base(entityFactory)
8+
{
9+
}
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using AltV.Net.Elements.Entities;
2+
3+
namespace AltV.Net.Async.Elements.Pools
4+
{
5+
public class AsyncCheckpointPool : AsyncBaseObjectPool<ICheckpoint>
6+
{
7+
public AsyncCheckpointPool(IBaseObjectFactory<ICheckpoint> entityFactory) : base(entityFactory)
8+
{
9+
}
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using AltV.Net.Elements.Entities;
2+
3+
namespace AltV.Net.Async.Elements.Pools
4+
{
5+
public class AsyncVoiceChannelPool : AsyncBaseObjectPool<IVoiceChannel>
6+
{
7+
public AsyncVoiceChannelPool(IBaseObjectFactory<IVoiceChannel> entityFactory) : base(entityFactory)
8+
{
9+
}
10+
}
11+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using AltV.Net.Elements.Args;
4+
using AltV.Net.Elements.Entities;
5+
using AltV.Net.Native;
6+
7+
namespace AltV.Net.Async
8+
{
9+
public static class MValueLocked
10+
{
11+
public static MValue CreateLocked(IPlayer player)
12+
{
13+
lock (player)
14+
{
15+
if (!player.Exists)
16+
{
17+
return MValue.Nil;
18+
}
19+
}
20+
21+
var mValue = MValue.Nil;
22+
AltNative.MValueCreate.MValue_CreatePlayer(player.NativePointer, ref mValue);
23+
return mValue;
24+
}
25+
26+
public static MValue CreateLocked(IVehicle vehicle)
27+
{
28+
lock (vehicle)
29+
{
30+
if (!vehicle.Exists)
31+
{
32+
return MValue.Nil;
33+
}
34+
}
35+
36+
var mValue = MValue.Nil;
37+
AltNative.MValueCreate.MValue_CreateVehicle(vehicle.NativePointer, ref mValue);
38+
return mValue;
39+
}
40+
41+
public static MValue CreateLocked(IBlip blip)
42+
{
43+
lock (blip)
44+
{
45+
if (!blip.Exists)
46+
{
47+
return MValue.Nil;
48+
}
49+
}
50+
51+
var mValue = MValue.Nil;
52+
AltNative.MValueCreate.MValue_CreateBlip(blip.NativePointer, ref mValue);
53+
return mValue;
54+
}
55+
56+
public static MValue CreateLocked(ICheckpoint checkpoint)
57+
{
58+
lock (checkpoint)
59+
{
60+
if (!checkpoint.Exists)
61+
{
62+
return MValue.Nil;
63+
}
64+
}
65+
66+
var mValue = MValue.Nil;
67+
AltNative.MValueCreate.MValue_CreateCheckpoint(checkpoint.NativePointer, ref mValue);
68+
return mValue;
69+
}
70+
71+
public static MValue CreateFromObjectLocked(object obj)
72+
{
73+
if (obj == null)
74+
{
75+
return MValue.Nil;
76+
}
77+
78+
int i;
79+
80+
string[] dictKeys;
81+
MValue[] dictValues;
82+
MValueWriter writer;
83+
84+
switch (obj)
85+
{
86+
case IPlayer player:
87+
return CreateLocked(player);
88+
case IVehicle vehicle:
89+
return CreateLocked(vehicle);
90+
case IBlip blip:
91+
return CreateLocked(blip);
92+
case ICheckpoint checkpoint:
93+
return CreateLocked(checkpoint);
94+
case bool value:
95+
return MValue.Create(value);
96+
case int value:
97+
return MValue.Create(value);
98+
case uint value:
99+
return MValue.Create(value);
100+
case long value:
101+
return MValue.Create(value);
102+
case ulong value:
103+
return MValue.Create(value);
104+
case double value:
105+
return MValue.Create(value);
106+
case float value:
107+
return MValue.Create(value);
108+
case string value:
109+
return MValue.Create(value);
110+
case MValue value:
111+
return value;
112+
case MValue[] value:
113+
return MValue.Create(value);
114+
case Invoker value:
115+
return MValue.Create(value);
116+
case MValue.Function value:
117+
return MValue.Create(value);
118+
case Function function:
119+
return MValue.Create(function.call);
120+
case IDictionary dictionary:
121+
dictKeys = new string[dictionary.Count];
122+
dictValues = new MValue[dictionary.Count];
123+
i = 0;
124+
foreach (var key in dictionary.Keys)
125+
{
126+
if (key is string stringKey)
127+
{
128+
dictKeys[i++] = stringKey;
129+
}
130+
else
131+
{
132+
return MValue.Nil;
133+
}
134+
}
135+
136+
i = 0;
137+
foreach (var value in dictionary.Values)
138+
{
139+
dictValues[i++] = CreateFromObjectLocked(value);
140+
}
141+
142+
return MValue.Create(dictValues, dictKeys);
143+
case ICollection collection:
144+
var length = collection.Count;
145+
var listValues = new MValue[length];
146+
i = 0;
147+
foreach (var value in collection)
148+
{
149+
listValues[i++] = CreateFromObjectLocked(value);
150+
}
151+
152+
return MValue.Create(listValues);
153+
case IDictionary<string, object> dictionary:
154+
dictKeys = new string[dictionary.Count];
155+
dictValues = new MValue[dictionary.Count];
156+
i = 0;
157+
foreach (var key in dictionary.Keys)
158+
{
159+
dictKeys[i++] = key;
160+
}
161+
162+
i = 0;
163+
foreach (var value in dictionary.Values)
164+
{
165+
dictValues[i++] = CreateFromObjectLocked(value);
166+
}
167+
168+
return MValue.Create(dictValues, dictKeys);
169+
case IWritable writable:
170+
writer = new MValueWriter();
171+
writable.OnWrite(writer);
172+
return writer.ToMValue();
173+
case IMValueConvertible convertible:
174+
writer = new MValueWriter();
175+
convertible.GetAdapter().ToMValue(obj, writer);
176+
return writer.ToMValue();
177+
default:
178+
Alt.Log("can't convert type:" + obj.GetType());
179+
return MValue.Nil;
180+
}
181+
}
182+
183+
internal static MValue[] CreateFromObjectsLocked(object[] objects)
184+
{
185+
var length = objects.Length;
186+
var mValues = new MValue[length];
187+
for (var i = 0; i < length; i++)
188+
{
189+
mValues[i] = CreateFromObjectLocked(objects[i]);
190+
}
191+
192+
return mValues;
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)