Ejemplo n.º 1
0
def pybind_instance_call_method(handle, p_method, p_args, p_argcount, r_error):
    instance = ffi.from_handle(handle)
    # TODO: improve this by using a dict lookup using string_name
    method = lib.godot_string_name_get_name(p_method)
    methname = godot_string_to_pyobj(ffi.addressof(method))
    lib.godot_string_destroy(ffi.addressof(method))
    try:
        meth = getattr(instance, methname)
    except AttributeError:
        r_error.error = lib.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD
        # TODO: Keep this object cached instead of recreating everytime
        return pyobj_to_variant(None, for_ffi_return=True)[0]

    # print('[GD->PY] Calling %s on %s ==> %s' % (methname, instance, meth))
    pyargs = [variant_to_pyobj(p_args[i]) for i in range(p_argcount)]
    try:
        pyret = meth(*pyargs)
        ret = pyobj_to_variant(pyret, for_ffi_return=True)
        r_error.error = lib.GODOT_CALL_ERROR_CALL_OK
        # print('[GD->PY] result: %s (%s)' % (pyret, ret[0]))
        return ret[0]

    except NotImplementedError:
        # print('[GD->PY] not implemented !')
        r_error.error = lib.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD
    except TypeError:
        traceback.print_exc()
        # TODO: handle errors here
        r_error.error = lib.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT
        r_error.argument = 1
        r_error.expected = lib.GODOT_VARIANT_TYPE_NIL
    # Something bad occured, return a default None variant
    # TODO: Keep this object cached instead of recreating it everytime
    return pyobj_to_variant(None, for_ffi_return=True)[0]
Ejemplo n.º 2
0
    def __getitem__(self, idx):
        if isinstance(idx, slice):
            return Array(list(self)[idx])

        size = len(self)
        idx = size + idx if idx < 0 else idx
        if abs(idx) >= size:
            raise IndexError("list index out of range")

        gdvar = lib.godot_array_get(self._gd_ptr, idx)
        ret = variant_to_pyobj(ffi.addressof(gdvar))
        lib.godot_variant_destroy(ffi.addressof(gdvar))
        return ret
Ejemplo n.º 3
0
 def bind(self, *args):
     # print('[PY->GD] Varargs call %s.%s (%s) on %s with %s' % (classname, methname, meth, self, args))
     vaargs = [
         convert_arg(meth_arg["type"], meth_arg["name"], arg, to_variant=True)
         for arg, meth_arg in zip(args, meth["args"])
     ]
     vaargs += [pyobj_to_variant(arg) for arg in args[fixargs_count:]]
     vavaargs = ffi.new("godot_variant*[]", vaargs) if vaargs else ffi.NULL
     # TODO: use `godot_variant_call_error` to raise exceptions
     varret = lib.godot_method_bind_call(
         methbind, self._gd_ptr, vavaargs, len(args), ffi.NULL
     )
     ret = variant_to_pyobj(ffi.addressof(varret))
     lib.godot_variant_destroy(ffi.addressof(varret))
     # print('[PY->GD] returned:', ret)
     return ret
Ejemplo n.º 4
0
def pybind_get_signal_list(handle):
    # Lazily generate the list of exported properties' names
    cls_or_instance = ffi.from_handle(handle)
    cls = cls_or_instance if isinstance(cls_or_instance,
                                        type) else type(cls_or_instance)
    # Need to store the cached list with a per-class name to avoid shadowing
    # from a parent class
    field = '_%s__signal_raw_list' % cls.__name__
    raw_list = getattr(cls, field, None)
    if not raw_list:
        # Build the list of signals, ready to be access by godot
        raw_list = ffi.new('godot_string[]', len(cls.__signals) + 1)
        for i, name in enumerate(cls.__signals.keys()):
            lib.godot_string_new_unicode_data(ffi.addressof(raw_list[i]), name,
                                              -1)
        # Last entry is an empty string
        lib.godot_string_new(ffi.addressof(raw_list[len(cls.__signals)]))
        setattr(cls, field, raw_list)
    return raw_list
Ejemplo n.º 5
0
def pybind_get_meth_list(handle):
    # Lazily generate the list of methods' names
    cls_or_instance = ffi.from_handle(handle)
    cls = cls_or_instance if isinstance(cls_or_instance,
                                        type) else type(cls_or_instance)
    # Need to store the cached list with a per-class name to avoid shadowing
    # from a parent class
    field = '_%s__meth_raw_list' % cls.__name__
    raw_list = getattr(cls, field, None)
    if not raw_list:
        meths = [
            k for k in dir(cls)
            if not k.startswith('__') and callable(getattr(cls, k))
        ]
        raw_list = ffi.new('godot_string[]', len(meths) + 1)
        for i, name in enumerate(meths):
            lib.godot_string_new_unicode_data(ffi.addressof(raw_list[i]), name,
                                              -1)
        # Last entry is an empty string
        lib.godot_string_new(ffi.addressof(raw_list[len(meths)]))
        setattr(cls, field, raw_list)
    return raw_list
Ejemplo n.º 6
0
 def build_from_gdobj(cls, gdobj, steal=False):
     # Avoid calling cls.__init__ by first instanciating a placeholder, then
     # overloading it __class__ to turn it into an instance of the right class
     ret = BuiltinInitPlaceholder()
     if steal:
         assert ffi.typeof(gdobj).kind == 'pointer'
         ret._gd_ptr = gdobj
     else:
         if ffi.typeof(gdobj).kind == 'pointer':
             ret._gd_ptr = cls._copy_gdobj(gdobj)
         else:
             ret._gd_ptr = cls._copy_gdobj(ffi.addressof(gdobj))
     ret.__class__ = cls
     return ret
Ejemplo n.º 7
0
 def get_class_consts(cls, classname):
     consts = []
     ret = godot_pool_string_array_alloc()
     lib.godot_pool_string_array_new(ret)
     gd_classname = godot_string_from_pyobj(classname)
     gd_true = godot_bool_alloc(True)
     args = ffi.new("void*[2]", [gd_classname, gd_true])
     # 2nd arg should be false, which what we get by not initializing it
     lib.godot_method_bind_ptrcall(cls._meth_get_integer_constant_list,
                                   cls._instance, args, ret)
     for i in range(lib.godot_pool_string_array_size(ret)):
         godot_str = lib.godot_pool_string_array_get(ret, i)
         raw_str = lib.godot_string_wide_str(ffi.addressof(godot_str))
         consts.append(ffi.string(raw_str))
     return consts
Ejemplo n.º 8
0
def pybind_profiling_get_frame_data(handle, info, info_max):
    print('get_frame_data')
    # Sort function to make sure we can display the most consuming ones
    sorted_and_limited = sorted(
        profiler.per_meth_profiling.items(),
        key=lambda x: -x[1].last_frame_self_time)[:info_max]
    for i, item in enumerate(sorted_and_limited):
        signature, profile = item
        # TODO: should be able to use lib.godot_string_new_with_wide_string directly
        lib.godot_string_name_new(ffi.addressof(info[i].signature),
                                  godot_string_from_pyobj(signature))
        info[i].call_count = profile.last_frame_call_count
        info[i].total_time = int(profile.last_frame_total_time * 1e6)
        info[i].self_time = int(profile.last_frame_self_time * 1e6)
    return len(sorted_and_limited)
Ejemplo n.º 9
0
 def get_class_properties(cls, classname):
     properties = []
     ret = godot_array_alloc()
     lib.godot_array_new(ret)
     gd_classname = godot_string_from_pyobj(classname)
     gd_true = godot_bool_alloc(True)
     args = ffi.new("void*[2]", [gd_classname, gd_true])
     # 2nd arg should be false, which what we get by not initializing it
     lib.godot_method_bind_ptrcall(cls._meth_get_property_list,
                                   cls._instance, args, ret)
     for i in range(lib.godot_array_size(ret)):
         var = lib.godot_array_get(ret, i)
         gddict = lib.godot_variant_as_dictionary(ffi.addressof(var))
         propdict = Dictionary.build_from_gdobj(gddict)
         properties.append(propdict)
     return properties
Ejemplo n.º 10
0
    def get_class_list(cls):
        ret = godot_pool_string_array_alloc()
        lib.godot_method_bind_ptrcall(cls._meth_get_class_list, cls._instance,
                                      ffi.NULL, ret)

        # Convert Godot return into Python civilized stuff
        unordered = []
        for i in range(lib.godot_pool_string_array_size(ret)):
            godot_str = lib.godot_pool_string_array_get(ret, i)
            raw_str = lib.godot_string_wide_str(ffi.addressof(godot_str))
            unordered.append(ffi.string(raw_str))

        # Order class to have a parent defined before their children
        classes = []
        while len(unordered) != len(classes):
            for classname in unordered:
                parentname = cls.get_parent_class(classname)
                if not parentname or parentname in classes:
                    if classname not in classes:
                        classes.append(classname)

        return classes
Ejemplo n.º 11
0
 def back(self):
     ret = lib.godot_array_back(self._gd_ptr)
     return variant_to_pyobj(ffi.addressof(ret))
Ejemplo n.º 12
0
 def __getitem__(self, key):
     var = pyobj_to_variant(key)
     retvar = lib.godot_dictionary_get(self._gd_ptr, var)
     return variant_to_pyobj(ffi.addressof(retvar))
Ejemplo n.º 13
0
 def get_subname(self, idx):
     self._check_param_type("idx", idx, int)
     subname = lib.godot_node_path_get_subname(self._gd_ptr, idx)
     return godot_string_to_pyobj(ffi.addressof(subname))
Ejemplo n.º 14
0
 def pop_front(self):
     ret = lib.godot_array_pop_front(self._gd_ptr)
     return variant_to_pyobj(ffi.addressof(ret))
Ejemplo n.º 15
0
 def path(self):
     gd_repr = lib.godot_node_path_as_string(self._gd_ptr)
     return ffi.string(lib.godot_string_wide_str(ffi.addressof(gd_repr)))
Ejemplo n.º 16
0
 def get_concatenated_subnames(self):
     concatenated = lib.godot_node_path_get_concatenated_subnames(
         self._gd_ptr)
     return godot_string_to_pyobj(ffi.addressof(concatenated))
Ejemplo n.º 17
0
 def __getitem__(self, key):
     var = pyobj_to_variant(key)
     gdvar = lib.godot_dictionary_get(self._gd_ptr, var)
     ret = variant_to_pyobj(ffi.addressof(gdvar))
     lib.godot_variant_destroy(ffi.addressof(gdvar))
     return ret
Ejemplo n.º 18
0
def _string_gd_to_py(self, value):
    return godot_string_to_pyobj(ffi.addressof(value))
Ejemplo n.º 19
0
 def __repr__(self):
     gd_repr = lib.godot_transform2d_as_string(self._gd_ptr)
     raw_str = lib.godot_string_wide_str(ffi.addressof(gd_repr))
     return "<%s(%s)>" % (type(self).__name__, ffi.string(raw_str))
Ejemplo n.º 20
0
 def to_json(self):
     raw = lib.godot_dictionary_to_json(self._gd_ptr)
     return godot_string_to_pyobj(ffi.addressof(raw))
Ejemplo n.º 21
0
 def to_html(self, with_alpha=True):
     gdstr = lib.godot_color_to_html(self._gd_ptr, with_alpha)
     return ffi.string(lib.godot_string_wide_str(ffi.addressof(gdstr)))
Ejemplo n.º 22
0
def variant_to_pyobj(p_gdvar):
    """
    Convert Godot variant to regular Python object
    :param p_gdvar: Godot variant as ``<cdata 'struct godot_variant *'>`` (note the pointer)
    """
    gdtype = lib.godot_variant_get_type(p_gdvar)
    if gdtype == lib.GODOT_VARIANT_TYPE_NIL:
        return None
    elif gdtype == lib.GODOT_VARIANT_TYPE_BOOL:
        return bool(lib.godot_variant_as_bool(p_gdvar))
    elif gdtype == lib.GODOT_VARIANT_TYPE_INT:
        return int(lib.godot_variant_as_int(p_gdvar))
    elif gdtype == lib.GODOT_VARIANT_TYPE_REAL:
        return float(lib.godot_variant_as_real(p_gdvar))
    elif gdtype == lib.GODOT_VARIANT_TYPE_STRING:
        raw = lib.godot_variant_as_string(p_gdvar)
        return godot_string_to_pyobj(ffi.addressof(raw))
    elif gdtype == lib.GODOT_VARIANT_TYPE_VECTOR2:
        raw = lib.godot_variant_as_vector2(p_gdvar)
        return Vector2.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_RECT2:
        raw = lib.godot_variant_as_rec2(p_gdvar)
        return Rect2.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_VECTOR3:
        raw = lib.godot_variant_as_vector3(p_gdvar)
        return Vector3.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_TRANSFORM2D:
        raw = lib.godot_variant_as_transform2d(p_gdvar)
        return Transform2D.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_PLANE:
        raw = lib.godot_variant_as_plane(p_gdvar)
        return Plane.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_QUAT:
        raw = lib.godot_variant_as_quat(p_gdvar)
        return Quat.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_AABB:
        raw = lib.godot_variant_as_aabb(p_gdvar)
        return AABB.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_BASIS:
        raw = lib.godot_variant_as_basis(p_gdvar)
        return Basis.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_TRANSFORM:
        raw = lib.godot_variant_as_transform(p_gdvar)
        return Transform.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_COLOR:
        raw = lib.godot_variant_as_color(p_gdvar)
        return Color.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_NODE_PATH:
        raw = lib.godot_variant_as_node_path(p_gdvar)
        return NodePath.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_RID:
        raw = lib.godot_variant_as_rid(p_gdvar)
        return RID.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_OBJECT:
        p_raw = lib.godot_variant_as_object(p_gdvar)
        # TODO: optimize this
        tmpobj = godot_bindings_module.Object(p_raw)
        return getattr(godot_bindings_module, tmpobj.get_class())(p_raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_DICTIONARY:
        raw = lib.godot_variant_as_dictionary(p_gdvar)
        return Dictionary.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_ARRAY:
        raw = lib.godot_variant_as_array(p_gdvar)
        return Array.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY:
        raw = lib.godot_variant_as_pool_byte_array(p_gdvar)
        return PoolByteArray.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_INT_ARRAY:
        raw = lib.godot_variant_as_pool_int_array(p_gdvar)
        return PoolIntArray.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_REAL_ARRAY:
        raw = lib.godot_variant_as_pool_real_array(p_gdvar)
        return PoolRealArray.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_STRING_ARRAY:
        raw = lib.godot_variant_as_pool_string_array(p_gdvar)
        return PoolStringArray.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_VECTOR2_ARRAY:
        raw = lib.godot_variant_as_pool_vector2_array(p_gdvar)
        return PoolVector2Array.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_VECTOR3_ARRAY:
        raw = lib.godot_variant_as_pool_vector3_array(p_gdvar)
        return PoolVector3Array.build_from_gdobj(raw)
    elif gdtype == lib.GODOT_VARIANT_TYPE_POOL_COLOR_ARRAY:
        raw = lib.godot_variant_as_pool_color_array(p_gdvar)
        return PoolColorArray.build_from_gdobj(raw)
    else:
        raise TypeError(
            "Unknown Variant type `%s` (this should never happen !)" % gdtype)
Ejemplo n.º 23
0
def _build_script_manifest(cls):
    from godot.bindings import Dictionary, Array

    def _build_signal_info(signal):
        methinfo = Dictionary()
        methinfo['name'] = signal.name
        # Dummy data, only name is important here
        methinfo['args'] = Array()
        methinfo['default_args'] = Array()
        methinfo['return'] = None
        methinfo['flags'] = lib.METHOD_FLAG_FROM_SCRIPT
        return methinfo

    def _build_method_info(meth, methname):
        spec = inspect.getfullargspec(meth)
        methinfo = Dictionary()
        methinfo['name'] = methname
        # TODO: Handle classmethod/staticmethod
        methinfo['args'] = Array(spec.args)
        methinfo['default_args'] = Array()  # TODO
        # TODO: use annotation to determine return type ?
        methinfo['return'] = None
        methinfo['flags'] = lib.METHOD_FLAG_FROM_SCRIPT
        methinfo['rpc_mode'] = getattr(meth, '__rpc',
                                       lib.GODOT_METHOD_RPC_MODE_DISABLED)
        return methinfo

    def _build_property_info(prop):
        propinfo = Dictionary()
        propinfo['name'] = prop.name
        propinfo['type'] = py_to_gd_type(prop.type)
        propinfo['hint'] = prop.hint
        propinfo['hint_string'] = prop.hint_string
        propinfo['usage'] = prop.usage
        propinfo['default_value'] = prop.default
        propinfo['rset_mode'] = prop.rpc
        return propinfo

    manifest = ffi.new('godot_pluginscript_script_manifest*')
    manifest.data = connect_handle(cls)
    gdname = godot_string_from_pyobj(cls.__name__)
    lib.godot_string_name_new(ffi.addressof(manifest.name), gdname)
    if cls.__bases__ and issubclass(cls.__bases__[0], BaseObject):
        gdbase = godot_string_from_pyobj(cls.__bases__[0].__name__)
        lib.godot_string_name_new(ffi.addressof(manifest.base), gdbase)
    manifest.is_tool = cls.__tool
    lib.godot_dictionary_new(ffi.addressof(manifest.member_lines))
    lib.godot_array_new(ffi.addressof(manifest.methods))
    methods = Array()
    # TODO: include inherited in exposed methods ? Expose Godot base class' ones ?
    # for methname in vars(cls):
    for methname in dir(cls):
        meth = getattr(cls, methname)
        if not inspect.isfunction(meth) or meth.__name__.startswith('__'):
            continue
        methinfo = _build_method_info(meth, methname)
        methods.append(methinfo)

    signals = Array()
    for signal in cls.__signals.values():
        signalinfo = _build_signal_info(signal)
        signals.append(signalinfo)

    properties = Array()
    for prop in cls.__exported.values():
        property_info = _build_property_info(prop)
        properties.append(property_info)

    lib.godot_array_new_copy(ffi.addressof(manifest.methods), methods._gd_ptr)
    lib.godot_array_new_copy(ffi.addressof(manifest.signals), signals._gd_ptr)
    lib.godot_array_new_copy(ffi.addressof(manifest.properties),
                             properties._gd_ptr)
    return manifest
Ejemplo n.º 24
0
 def pop_front(self):
     gdvar = lib.godot_array_pop_front(self._gd_ptr)
     ret = variant_to_pyobj(ffi.addressof(gdvar))
     lib.godot_variant_destroy(ffi.addressof(gdvar))
     return ret
Ejemplo n.º 25
0
def _build_script_manifest(cls):
    def _build_signal_info(signal):
        methinfo = Dictionary()
        methinfo["name"] = signal.name
        # Dummy data, only name is important here
        methinfo["args"] = Array()
        methinfo["default_args"] = Array()
        methinfo["return"] = None
        methinfo["flags"] = lib.METHOD_FLAG_FROM_SCRIPT
        return methinfo

    def _build_method_info(meth, methname):
        spec = inspect.getfullargspec(meth)
        methinfo = Dictionary()
        methinfo["name"] = methname
        # TODO: Handle classmethod/staticmethod
        methinfo["args"] = Array(spec.args)
        methinfo["default_args"] = Array()  # TODO
        # TODO: use annotation to determine return type ?
        methinfo["return"] = None
        methinfo["flags"] = lib.METHOD_FLAG_FROM_SCRIPT
        methinfo["rpc_mode"] = getattr(meth, "__rpc",
                                       lib.GODOT_METHOD_RPC_MODE_DISABLED)
        return methinfo

    def _build_property_info(prop):
        propinfo = Dictionary()
        propinfo["name"] = prop.name
        propinfo["type"] = py_to_gd_type(prop.type)
        propinfo["hint"] = prop.hint
        propinfo["hint_string"] = prop.hint_string
        propinfo["usage"] = prop.usage
        propinfo["default_value"] = prop.default
        propinfo["rset_mode"] = prop.rpc
        return propinfo

    manifest = ffi.new("godot_pluginscript_script_manifest*")
    manifest.data = connect_handle(cls)
    # TODO: should be able to use lib.godot_string_new_with_wide_string directly
    gdname = godot_string_from_pyobj(cls.__name__)
    lib.godot_string_name_new(ffi.addressof(manifest.name), gdname)
    if cls.__bases__:
        # Only one Godot parent class (checked at class definition time)
        godot_parent_class = next(
            (b for b in cls.__bases__ if issubclass(b, BaseObject)))
        if godot_parent_class.__dict__.get("__is_godot_native_class"):
            path = godot_parent_class.__name__
        else:
            # Pluginscript wants us to return the parent as a path
            path = "res://%s.py" % "/".join(
                cls.__bases__[0].__module__.split("."))
        gdbase = godot_string_from_pyobj(path)
        lib.godot_string_name_new(ffi.addressof(manifest.base), gdbase)
    manifest.is_tool = cls.__tool
    lib.godot_dictionary_new(ffi.addressof(manifest.member_lines))
    lib.godot_array_new(ffi.addressof(manifest.methods))
    methods = Array()
    # TODO: include inherited in exposed methods ? Expose Godot base class' ones ?
    # for methname in vars(cls):
    for methname in dir(cls):
        meth = getattr(cls, methname)
        if not inspect.isfunction(meth) or meth.__name__.startswith("__"):
            continue

        methinfo = _build_method_info(meth, methname)
        methods.append(methinfo)

    signals = Array()
    for signal in cls.__signals.values():
        signalinfo = _build_signal_info(signal)
        signals.append(signalinfo)

    properties = Array()
    for prop in cls.__exported.values():
        property_info = _build_property_info(prop)
        properties.append(property_info)

    lib.godot_array_new_copy(ffi.addressof(manifest.methods), methods._gd_ptr)
    lib.godot_array_new_copy(ffi.addressof(manifest.signals), signals._gd_ptr)
    lib.godot_array_new_copy(ffi.addressof(manifest.properties),
                             properties._gd_ptr)
    return manifest