1 /** 2 The most important data type in Godot. 3 4 Copyright: 5 Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 Copyright (c) 2022-2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.variant; 15 16 import godot.abi; 17 import godot; 18 import godot.object; 19 import godot.api.traits; 20 import godot.api.reference; 21 import godot.script; 22 import godot.api.types; 23 24 import std.meta, std.traits; 25 import std.conv : text; 26 import std.range; 27 28 // for tests 29 import godot.node; 30 import godot.resource; 31 32 // FIXME ABI type should probably have its own `version`... 33 version (X86_64) { 34 version (DigitalMars) { 35 version (linux) version = GodotSystemV; 36 version (OSX) version = GodotSystemV; 37 version (Posix) version = GodotSystemV; 38 } 39 } 40 41 /// User-defined Variant conversions. 42 /// For structs and classes, constructors and member functions can also be used. 43 unittest { 44 struct A { 45 } 46 47 static assert(!Variant.compatible!A); 48 49 struct B { 50 } 51 52 B to(T : B)(Variant v) { 53 return B(); 54 } 55 56 int to(T : Variant)(B b) { 57 return 1; 58 } 59 60 static assert(Variant.compatible!B); 61 62 struct C { 63 this(Variant v) { 64 } 65 66 Variant to(T : Variant)() { 67 return Variant(1); 68 } 69 } 70 71 static assert(Variant.compatible!C); 72 73 B b; 74 C c; 75 76 Variant vb = b; 77 Variant vc = c; 78 79 b = vb.as!B; 80 c = vc.as!C; 81 } 82 83 /// 84 enum VariantType { 85 nil, 86 87 // atomic types 88 bool_, 89 int_, 90 float_, 91 string, 92 93 // math types 94 95 vector2, // 5 96 vector2i, 97 rect2, 98 rect2i, 99 vector3, 100 vector3i, // 10 101 transform2d, 102 vector4, 103 vector4i, 104 plane, 105 quaternion, 106 aabb, 107 basis, // 15 108 transform3d, 109 projection, 110 111 // misc types 112 color, 113 string_name, 114 node_path, 115 rid, 116 object, // 20 117 callable, 118 signal, 119 dictionary, 120 array, 121 122 // arrays 123 packed_byte_array, // 25 124 packed_int32_array, 125 packed_int64_array, 126 packed_float32_array, 127 packed_float64_array, 128 packed_string_array, // 30 129 packed_vector2_array, 130 packed_vector3_array, 131 packed_color_array, 132 } 133 134 /** 135 Godot's tagged union type. 136 137 Primitives, Godot core types, and `GodotObject`-derived classes can be stored in 138 a Variant. Other user-defined D types can be made compatible with Variant by 139 defining `to!CustomType(Variant)` and `to!Variant(CustomType)` functions. 140 141 Properties and method arguments/returns are passed between Godot and D through 142 Variant, so these must use Variant-compatible types. 143 */ 144 struct Variant { 145 package(godot) godot_variant _godot_variant; 146 147 // having it inside variant creates annoying recursive alias issue 148 alias Type = VariantType; 149 150 /// GDNative type that gets passed to the C functions 151 // NOTE: godot 4 now uses default int as int32 and double precision by default 152 // TODO: verify this 153 alias InternalType = AliasSeq!( 154 typeof(null), 155 156 godot_bool, 157 int, 158 double, 159 godot_string, 160 161 godot_vector2, // 5 162 godot_vector2i, 163 godot_rect2, 164 godot_rect2i, 165 godot_vector3, 166 godot_vector3i, // 10 167 godot_transform2d, 168 godot_vector4, 169 godot_vector4i, 170 godot_plane, 171 godot_quaternion, 172 godot_aabb, 173 godot_basis, // 15 174 godot_transform3d, 175 godot_projection, 176 177 godot_color, 178 godot_string, //godot_string_name 179 godot_node_path, 180 godot_rid, // 20 181 godot_object, 182 godot_callable, 183 godot_signal, 184 godot_dictionary, 185 godot_array, // 25 186 187 godot_packed_byte_array, 188 godot_packed_int32_array, 189 godot_packed_int64_array, 190 godot_packed_float32_array, 191 godot_packed_float64_array, // 30 192 godot_packed_string_array, 193 godot_packed_vector2_array, 194 godot_packed_vector3_array, 195 godot_packed_color_array, 196 ); 197 198 /// D type that this Variant implementation uses 199 alias DType = AliasSeq!( 200 typeof(null), 201 202 bool, 203 long, 204 double, 205 String, 206 207 Vector2, // 5 208 Vector2i, 209 Rect2, 210 Rect2i, 211 Vector3, 212 Vector3i, // 10 213 Transform2D, 214 Vector4, 215 Vector4i, 216 Plane, 217 Quaternion, 218 AABB, 219 Basis, // 15 220 Transform3D, 221 Projection,// misc types 222 Color, 223 StringName, 224 NodePath, 225 RID, // 20 226 GodotObject, 227 GodotCallable, 228 GodotSignal, 229 Dictionary, 230 Array, // 25 231 232 // arrays 233 PackedByteArray, 234 PackedInt32Array, 235 PackedInt64Array, 236 PackedFloat32Array, 237 PackedFloat64Array, // 30 238 PackedStringArray, 239 PackedVector2Array, 240 PackedVector3Array, 241 PackedColorArray, 242 ); 243 244 /// 245 enum Operator { 246 //comparation 247 equal, 248 notEqual, 249 less, 250 lessEqual, 251 greater, 252 greaterEqual, 253 254 //mathematic 255 add, 256 substract, 257 multiply, 258 divide, 259 negate, 260 positive, 261 modulus, 262 power, 263 //stringConcat, 264 265 //bitwise 266 shiftLeft, 267 shiftRight, 268 bitAnd, 269 bitOr, 270 bitXor, 271 bitNegate, 272 273 //logic 274 and, 275 or, 276 xor, 277 not, 278 279 //containment 280 in_ 281 } 282 283 private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest); 284 285 private static GodotObject objectToGodot(T)(T o) { 286 return o.getGodotObject; 287 } 288 289 // Conversions for non-core types provided by Variant. 290 // Lower priority than user-defined `to` functions, to allow overriding 291 // default blanket implementations. 292 private alias internalAs(T) = (Variant v) => v.as!T; 293 private enum bool hasInternalAs(T) = __traits(compiles, internalAs!T); 294 private alias internalFrom(T) = (T t) => Variant.from(t); 295 private enum bool hasInternalFrom(T) = __traits(compiles, internalFrom!T); 296 297 /// 298 T as(T)() const if (isStaticArray!T && compatibleFromGodot!(ElementType!T)) { 299 return as!Array.as!T; 300 } 301 302 /// 303 T as(T)() const if (is(T : TypedArray!U, U...) && !is(U == Array)) { 304 return T(as!Array); 305 } 306 307 T as(T : void*)() { 308 return cast(T)&_godot_variant; 309 } 310 311 /// 312 T as(T)() const 313 if ((isGodotClass!T && !is(T == GodotObject)) || is(T : Ref!U, U)) { 314 GodotObject o = cast()(as!GodotObject); 315 return o.as!T; 316 } 317 318 /// 319 static Array from(T)(T t) 320 if ((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T)) { 321 return Array.from(t); 322 } 323 324 /// 325 GodotType as(T : GodotType)() const { 326 if (type == Type.object) { 327 Ref!Script s = as!Script; 328 if (s) 329 return GodotType(s); 330 else 331 return GodotType.init; 332 } else if (type == Type..string) 333 return GodotType(BuiltInClass(as!String)); 334 else if (type == Type.int_) 335 return GodotType(cast(Variant.Type)(as!int)); 336 else 337 return GodotType.init; 338 } 339 340 /// 341 static Variant from(T : GodotType)(T t) { 342 import sumtype : match; 343 344 Variant ret; 345 t.match!( 346 (Variant.Type t) { ret = cast(int) t; }, 347 (BuiltInClass c) { ret = c.name; }, 348 (Ref!Script s) { ret = s; } 349 ); 350 return ret; 351 } 352 353 // TODO: fix me 354 /* 355 static assert(hasInternalAs!Node, internalAs!Node); 356 static assert(hasInternalAs!(Ref!Resource), internalAs!(Ref!Resource)); 357 static assert(!hasInternalAs!Object); // `directlyCompatible` types not handled by internalAs 358 static assert(hasInternalAs!(int[4]), internalAs!(int[4])); 359 static assert(hasInternalFrom!(int[4]), internalFrom!(int[4])); 360 static assert(!hasInternalAs!(int[])); 361 static assert(hasInternalFrom!(int[]), internalFrom!(int[])); 362 static assert(hasInternalAs!GodotType, internalAs!GodotType); 363 static assert(hasInternalFrom!GodotType, internalFrom!GodotType); 364 static assert(compatible!GodotType); 365 */ 366 367 private template getToVariantFunction(T) { 368 mixin("import " ~ moduleName!T ~ ";"); 369 alias getToVariantFunction = (T t) { Variant v = t.to!Variant; return v; }; 370 } 371 372 enum bool hasToVariantFunction(T) = __traits(compiles, getToVariantFunction!T); 373 374 private template getVariantConstructor(T) { 375 alias getVariantConstructor = (Variant v) => T(v); 376 } 377 378 enum bool hasVariantConstructor(T) = __traits(compiles, getVariantConstructor!T); 379 380 template getFromVariantFunction(T) { 381 mixin("import " ~ moduleName!T ~ ";"); 382 alias getFromVariantFunction = (Variant v) { T ret = v.to!T; return ret; }; 383 } 384 385 enum bool hasFromVariantFunction(T) = __traits(compiles, getFromVariantFunction!T); 386 387 /// function to convert T to an equivalent Godot type 388 template conversionToGodot(T) { 389 static if (isGodotClass!T) 390 alias conversionToGodot = objectToGodot!T; 391 else static if (is(T : GodotStringLiteral!s, string s)) 392 alias conversionToGodot = (T t) => t.str(); 393 else static if (is(T : Ref!U, U)) 394 alias conversionToGodot = objectToGodot!U; 395 else static if (isIntegral!T) 396 alias conversionToGodot = (T t) => cast(long) t; 397 else static if (isFloatingPoint!T) 398 alias conversionToGodot = (T t) => cast(double) t; 399 else static if (implicit!(T, const(char)[]) || implicit!(T, const(char)*)) 400 alias conversionToGodot = (T t) => String(t); 401 else static if (hasToVariantFunction!T) { 402 alias conversionToGodot = getToVariantFunction!T; 403 } else static if (hasInternalFrom!T) 404 alias conversionToGodot = internalFrom!T; 405 else 406 alias conversionToGodot = void; // none 407 } 408 409 enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T); 410 alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T)); 411 412 /// function to convert a Godot-compatible type to T 413 template conversionFromGodot(T) { 414 static if (isIntegral!T) 415 alias conversionFromGodot = (long v) => cast(T) v; 416 else static if (isFloatingPoint!T) 417 alias conversionFromGodot = (double v) => cast(T) v; 418 else static if (hasVariantConstructor!T) { 419 alias conversionFromGodot = getVariantConstructor!T; 420 } else static if (hasFromVariantFunction!T) { 421 alias conversionFromGodot = getFromVariantFunction!T; 422 } else 423 alias conversionFromGodot = void; 424 } 425 426 enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T); 427 alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]); 428 429 enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1; 430 template compatibleToGodot(T) { 431 static if (directlyCompatible!T) 432 enum bool compatibleToGodot = true; 433 else 434 enum bool compatibleToGodot = convertsToGodot!T; 435 } 436 437 template compatibleFromGodot(T) { 438 static if (directlyCompatible!T) 439 enum bool compatibleFromGodot = true; 440 else static if (hasInternalAs!T) 441 enum bool compatibleFromGodot = true; 442 else 443 enum bool compatibleFromGodot = convertsFromGodot!T; 444 } 445 446 enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R); 447 448 /// All target Variant.Types that T could implicitly convert to, as indices 449 private template implicitTargetIndices(T) { 450 private enum bool _implicit(size_t di) = implicit!(T, DType[di]); 451 alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length))); 452 } 453 454 /++ 455 Get the Variant.Type of a compatible D type. Incompatible types return nil. 456 +/ 457 public template variantTypeOf(T) { 458 import std.traits, godot; 459 460 static if (directlyCompatible!T) { 461 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)]; 462 } else static if (convertsToGodot!T) { 463 static if (is(conversionToGodotType!T : Variant)) 464 enum Type variantTypeOf = Type.nil; 465 else 466 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!( 467 conversionToGodotType!T, DType)]; 468 } else 469 enum Type variantTypeOf = Type.nil; // so the template always returns a Type 470 } 471 472 /// 473 R as(R)() const 474 if (!is(R == Variant) && !is(R == typeof(null)) && (convertsFromGodot!R || directlyCompatible!R)) { 475 static if (directlyCompatible!R) 476 enum VarType = variantTypeOf!R; 477 else static if (is(conversionFromGodotType!R : Variant)) 478 enum VarType = Type.nil; 479 else 480 enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)]; 481 482 // HACK workaround for DMD issue #5570 483 version (GodotSystemV) 484 enum sV = true; 485 else 486 enum sV = false; 487 static if (VarType == Type.vector3 && sV) { 488 godot_vector3 ret = void; 489 void* _func = cast(void*) _godot_api.variant_as_vector3; 490 void* _this = cast(void*)&this; 491 492 asm @nogc nothrow { 493 mov RDI, _this; 494 call _func; 495 496 mov ret[0], RAX; 497 mov ret[8], EDX; 498 } 499 return *cast(Vector3*)&ret; 500 } else static if (VarType == Type.nil) { 501 return conversionFromGodot!R(this); 502 } else static if (is(Unqual!R == String)) { 503 static if (is(Unqual!R == NodePath)) 504 godot_node_path str; 505 else 506 godot_string str; 507 _godot_api.variant_stringify(&_godot_variant, cast(void*)&str); 508 return R(str); 509 } else { 510 DType[VarType] ret = void; 511 //*cast(InternalType[VarType]*)&ret = mixin("_godot_api.variant_as_"~FunctionAs!VarType~"(&_godot_variant)"); 512 513 // this gives wrong result, try the other way around 514 //auto fn = _godot_api.get_variant_from_type_constructor(VarType); 515 //fn(cast(GDNativeVariantPtr) &_godot_variant, &ret); 516 517 // special case such as calling function with null optional parameter 518 if (_godot_variant._opaque.ptr is null) { 519 return R.init; 520 } 521 522 auto fn = _godot_api.get_variant_to_type_constructor(VarType); 523 fn(cast(void*)&ret, cast(void*)&_godot_variant); 524 525 static if (directlyCompatible!R) 526 return ret; 527 else { 528 return conversionFromGodot!R(ret); 529 } 530 } 531 } 532 533 this(R)(auto ref R input) if (!is(R : Variant) && !is(R : typeof(null))) { 534 static assert(compatibleToGodot!R, R.stringof ~ " isn't compatible with Variant."); 535 enum VarType = variantTypeOf!R; 536 537 static if (VarType == Type.nil) { 538 this = conversionToGodot!R(input); 539 } else { 540 //mixin("auto Fn = _godot_api.variant_new_"~FunctionNew!VarType~";"); 541 auto Fn = _godot_api.get_variant_from_type_constructor(VarType); 542 alias PassType = Parameters!Fn[1]; // second param is the value 543 544 alias IT = InternalType[VarType]; 545 546 // handle explicit conversions 547 static if (directlyCompatible!R) 548 alias inputConv = input; 549 else 550 auto inputConv = conversionToGodot!R(input); 551 552 static if (is(IT == Unqual!PassType)) 553 Fn(&_godot_variant, cast(IT) inputConv); // value 554 else 555 Fn(&_godot_variant, cast(IT*)&inputConv); // pointer 556 } 557 } 558 559 pragma(inline, true) 560 void opAssign(T)(in auto ref T input) 561 if (!is(T : Variant) && !is(T : typeof(null)) && !is(Unqual!T : void*)) { 562 import std.conv : emplace; 563 564 _godot_api.variant_destroy(&_godot_variant); 565 static if (is(T : TypedArray!Args, Args...)) { 566 // hacky way, for some reasons 'alias this' was ignored 567 emplace!(Variant)(&this, input._array); 568 } else { 569 emplace!(Variant)(&this, input); 570 } 571 } 572 573 // internal use only, but can be useful for users who knows what they are doing 574 // used in few cases only, audioeffect.process() as a buffer is one example 575 pragma(inline, true) 576 package(godot) void opAssign(const void* input) { 577 // can it be messed up by alignment? 578 _godot_variant = *cast(godot_variant*) input; 579 } 580 581 static assert(allSatisfy!(compatible, DType)); 582 static assert(!compatible!Object); // D Object 583 584 static assert(directlyCompatible!GodotObject); 585 static assert(directlyCompatible!(const(GodotObject))); 586 static assert(!directlyCompatible!Node); 587 // TODO: fix me 588 //static assert(compatibleFromGodot!Node); 589 static assert(compatibleToGodot!Node); 590 // TODO: fix me 591 //static assert(compatibleFromGodot!(const(Node))); 592 static assert(compatibleToGodot!(const(Node))); 593 static assert(!directlyCompatible!(Ref!Resource)); 594 // TODO: fix me 595 //static assert(compatibleFromGodot!(Ref!Resource)); 596 static assert(compatibleToGodot!(Ref!Resource)); 597 // TODO: fix me 598 //static assert(compatibleFromGodot!(const(Ref!Resource))); 599 static assert(compatibleToGodot!(const(Ref!Resource))); 600 601 private template FunctionAs(Type type) { 602 private enum string name_ = text(type); 603 private enum string FunctionAs = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_; 604 } 605 606 private template FunctionNew(Type type) { 607 private enum string name_ = text(type); 608 private enum string FunctionNew = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_; 609 } 610 611 //@nogc nothrow: 612 this(this) { 613 godot_variant other = _godot_variant; // source Variant still owns this 614 _godot_api.variant_new_copy(&_godot_variant, &other); 615 } 616 617 static Variant nil() { 618 Variant v = void; 619 _godot_api.variant_new_nil(&v._godot_variant); 620 return v; 621 } 622 623 this(in ref Variant other) { 624 _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant); 625 } 626 627 this(T : typeof(null))(in T nil) { 628 _godot_api.variant_new_nil(&_godot_variant); 629 } 630 631 ~this() { 632 // TODO: need to check this, causes broken values after several Variant to variant assignments 633 _godot_api.variant_destroy(&_godot_variant); 634 } 635 636 Type type() const { 637 return cast(Type) _godot_api.variant_get_type(&_godot_variant); 638 } 639 640 inout(T) as(T : Variant)() inout { 641 return this; 642 } 643 644 pragma(inline, true) 645 void opAssign(T : typeof(null))(in T nil) { 646 _godot_api.variant_destroy(&_godot_variant); 647 _godot_api.variant_new_nil(&_godot_variant); 648 } 649 650 pragma(inline, true) 651 void opAssign(T : Variant)(in T other) { 652 _godot_api.variant_destroy(&_godot_variant); 653 _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant); 654 } 655 656 bool opEquals(in ref Variant other) const { 657 Variant ret; 658 bool valid; 659 evaluate(GDNATIVE_VARIANT_OP_EQUAL, this, other, ret, valid); 660 return ret.as!bool; 661 //return cast(bool)_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant); 662 } 663 664 private void evaluate(int op, ref const Variant a, ref const Variant b, ref Variant ret, ref bool isValid) const { 665 GDNativeBool res; 666 _godot_api.variant_evaluate(op, &a._godot_variant, &b._godot_variant, &ret._godot_variant, &res); 667 isValid = !!res; 668 } 669 670 int opCmp(in ref Variant other) const { 671 Variant res; 672 bool valid; 673 evaluate(GDNATIVE_VARIANT_OP_EQUAL, this, other, res, valid); 674 if (res.as!bool) 675 return 0; 676 evaluate(GDNATIVE_VARIANT_OP_LESS, this, other, res, valid); 677 return res.as!bool ? -1 : 1; 678 //if(_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant)) 679 // return 0; 680 //return _godot_api.variant_operator_less(&_godot_variant, &other._godot_variant)? 681 // -1 : 1; 682 } 683 684 bool booleanize() const { 685 return cast(bool) _godot_api.variant_booleanize(&_godot_variant); 686 } 687 688 auto toString() const { 689 String str = as!String; 690 return str.data; 691 } 692 693 /// Is this Variant of the specified `type` or of a subclass of `type`? 694 bool isType(GodotType type) const { 695 import sumtype : match; 696 697 return type.match!( 698 (Ref!Script script) { 699 GodotObject o = this.as!GodotObject; 700 if (o == null) 701 return false; 702 return script.instanceHas(o); 703 }, 704 (BuiltInClass object) { 705 GodotObject o = this.as!GodotObject; 706 if (o == null) 707 return false; 708 return o.isClass(object.name); 709 }, 710 (Type vt) => this.type == vt 711 ); 712 } 713 714 /++ 715 The exact GodotType of the value stored in this Variant. 716 717 To check if a Variant is a specific GodotType, use `isType` instead to 718 account for inheritance. 719 +/ 720 GodotType exactType() const { 721 if (GodotObject o = this.as!GodotObject) { 722 if (Ref!Script s = o.getScript().as!Script) 723 return GodotType(s); 724 else 725 return GodotType(BuiltInClass(o.getClass())); 726 } else 727 return GodotType(this.type); 728 } 729 }