forked from godotengine/godot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGodotObject.base.cs
354 lines (300 loc) · 13.1 KB
/
GodotObject.base.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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using Godot.Bridge;
using Godot.NativeInterop;
#nullable enable
namespace Godot
{
public partial class GodotObject : IDisposable
{
private bool _disposed;
private static readonly Type _cachedType = typeof(GodotObject);
private static readonly Dictionary<Type, StringName?> _nativeNames = new Dictionary<Type, StringName?>();
internal IntPtr NativePtr;
private bool _memoryOwn;
private WeakReference<GodotObject>? _weakReferenceToSelf;
/// <summary>
/// Constructs a new <see cref="GodotObject"/>.
/// </summary>
public GodotObject() : this(false)
{
unsafe
{
ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false);
}
}
internal GodotObject(IntPtr nativePtr) : this(false)
{
// NativePtr must be non-zero before calling ConstructAndInitialize to avoid invoking the constructor NativeCtor.
// We don't want to invoke the constructor, because we already have a constructed instance in nativePtr.
NativePtr = nativePtr;
unsafe
{
ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false);
}
}
internal unsafe void ConstructAndInitialize(
delegate* unmanaged<godot_bool, IntPtr> nativeCtor,
StringName nativeName,
Type cachedType,
bool refCounted
)
{
if (NativePtr == IntPtr.Zero)
{
Debug.Assert(nativeCtor != null);
// Need postinitialization.
NativePtr = nativeCtor(godot_bool.True);
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
nativeName, refCounted, GetType(), cachedType);
}
else
{
InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
GetType(), cachedType);
}
_weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
}
internal GodotObject(bool memoryOwn)
{
_memoryOwn = memoryOwn;
}
/// <summary>
/// The pointer to the native instance of this <see cref="GodotObject"/>.
/// </summary>
public IntPtr NativeInstance => NativePtr;
internal static IntPtr GetPtr(GodotObject? instance)
{
if (instance == null)
return IntPtr.Zero;
// We check if NativePtr is null because this may be called by the debugger.
// If the debugger puts a breakpoint in one of the base constructors, before
// NativePtr is assigned, that would result in UB or crashes when calling
// native functions that receive the pointer, which can happen because the
// debugger calls ToString() and tries to get the value of properties.
if (instance._disposed || instance.NativePtr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.NativePtr;
}
~GodotObject()
{
Dispose(false);
}
/// <summary>
/// Disposes of this <see cref="GodotObject"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes implementation of this <see cref="GodotObject"/>.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
_disposed = true;
if (NativePtr != IntPtr.Zero)
{
IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
if (gcHandleToFree != IntPtr.Zero)
{
object? target = GCHandle.FromIntPtr(gcHandleToFree).Target;
// The GC handle may have been replaced in another thread. Release it only if
// it's associated to this managed instance, or if the target is no longer alive.
if (target != this && target != null)
gcHandleToFree = IntPtr.Zero;
}
if (_memoryOwn)
{
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
(!disposing).ToGodotBool());
}
else
{
NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
}
NativePtr = IntPtr.Zero;
}
if (_weakReferenceToSelf != null)
{
DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
}
}
/// <summary>
/// Converts this <see cref="GodotObject"/> to a string.
/// </summary>
/// <returns>A string representation of this object.</returns>
public override string ToString()
{
NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
using (str)
return Marshaling.ConvertStringToManaged(str);
}
/// <summary>
/// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance
/// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
/// </summary>
/// <param name="source">
/// The instance the awaiter will be listening to.
/// </param>
/// <param name="signal">
/// The signal the awaiter will be waiting for.
/// </param>
/// <example>
/// This sample prints a message once every frame up to 100 times.
/// <code>
/// public override void _Ready()
/// {
/// for (int i = 0; i < 100; i++)
/// {
/// await ToSignal(GetTree(), "process_frame");
/// GD.Print($"Frame {i}");
/// }
/// }
/// </code>
/// </example>
/// <returns>
/// A <see cref="SignalAwaiter"/> that completes when
/// <paramref name="source"/> emits the <paramref name="signal"/>.
/// </returns>
public SignalAwaiter ToSignal(GodotObject source, StringName signal)
{
return new SignalAwaiter(source, signal, this);
}
internal static bool IsNativeClass(Type t)
{
if (ReferenceEquals(t.Assembly, typeof(GodotObject).Assembly))
{
return true;
}
if (ReflectionUtils.IsEditorHintCached)
{
return t.Assembly.GetName().Name == "GodotSharpEditor";
}
return false;
}
internal static Type InternalGetClassNativeBase(Type t)
{
while (!IsNativeClass(t))
{
Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type.");
t = t.BaseType;
}
return t;
}
internal static StringName? InternalGetClassNativeBaseName(Type t)
{
if (_nativeNames.TryGetValue(t, out var name))
{
return name;
}
var baseType = InternalGetClassNativeBase(t);
if (_nativeNames.TryGetValue(baseType, out name))
{
return name;
}
var field = baseType.GetField("NativeName",
BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
name = field?.GetValue(null) as StringName;
_nativeNames[baseType] = name;
return name;
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
/// <summary>
/// Set the value of a property contained in this class.
/// This method is used by Godot to assign property values.
/// Do not call or override this method.
/// </summary>
/// <param name="name">Name of the property to set.</param>
/// <param name="value">Value to set the property to if it was found.</param>
/// <returns><see langword="true"/> if a property with the given name was found.</returns>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
{
return false;
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
/// <summary>
/// Get the value of a property contained in this class.
/// This method is used by Godot to retrieve property values.
/// Do not call or override this method.
/// </summary>
/// <param name="name">Name of the property to get.</param>
/// <param name="value">Value of the property if it was found.</param>
/// <returns><see langword="true"/> if a property with the given name was found.</returns>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
{
value = default;
return false;
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
/// <summary>
/// Raises the signal with the given name, using the given arguments.
/// This method is used by Godot to raise signals from the engine side.\n"
/// Do not call or override this method.
/// </summary>
/// <param name="signal">Name of the signal to raise.</param>
/// <param name="args">Arguments to use with the raised signal.</param>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
NativeVariantPtrArgs args)
{
}
internal static IntPtr ClassDB_get_method(StringName type, StringName method)
{
var typeSelf = (godot_string_name)type.NativeValue;
var methodSelf = (godot_string_name)method.NativeValue;
IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
if (methodBind == IntPtr.Zero)
throw new NativeMethodBindNotFoundException(type + "." + method);
return methodBind;
}
internal static IntPtr ClassDB_get_method_with_compatibility(StringName type, StringName method, ulong hash)
{
var typeSelf = (godot_string_name)type.NativeValue;
var methodSelf = (godot_string_name)method.NativeValue;
IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method_with_compatibility(typeSelf, methodSelf, hash);
if (methodBind == IntPtr.Zero)
throw new NativeMethodBindNotFoundException(type + "." + method);
return methodBind;
}
internal static unsafe delegate* unmanaged<godot_bool, IntPtr> ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var typeSelf = (godot_string_name)type.NativeValue;
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
if (nativeConstructor == null)
throw new NativeConstructorNotFoundException(type);
return nativeConstructor;
}
/// <summary>
/// Saves this instance's state to be restored when reloading assemblies.
/// Do not call or override this method.
/// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
/// </summary>
/// <param name="info">Object used to save the data.</param>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
{
}
// TODO: Should this be a constructor overload?
/// <summary>
/// Restores this instance's state after reloading assemblies.
/// Do not call or override this method.
/// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
/// </summary>
/// <param name="info">Object that contains the previously saved data.</param>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
{
}
}
}