2
0
Fork 0
Dr. Sascha Woitschetzki 2024-02-06 08:37:51 +07:00
commit 786841b496
45 changed files with 426 additions and 245 deletions

@ -794,6 +794,9 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) {
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
ERR_FAIL_NULL(initialization.deinitialize);
initialization.deinitialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}

@ -36,6 +36,7 @@
#include "core/object/script_language.h"
#include "core/os/condition_variable.h"
#include "core/os/os.h"
#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "core/variant/variant_parser.h"

@ -39,6 +39,9 @@
class ConditionVariable;
template <int Tag>
class SafeBinaryMutex;
class ResourceFormatLoader : public RefCounted {
GDCLASS(ResourceFormatLoader, RefCounted);

@ -188,7 +188,7 @@ public:
template <class T>
static void register_class(bool p_virtual = false) {
GLOBAL_LOCK_FUNCTION;
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@ -203,7 +203,7 @@ public:
template <class T>
static void register_abstract_class() {
GLOBAL_LOCK_FUNCTION;
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@ -216,7 +216,7 @@ public:
template <class T>
static void register_internal_class() {
GLOBAL_LOCK_FUNCTION;
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@ -239,7 +239,7 @@ public:
template <class T>
static void register_custom_instance_class() {
GLOBAL_LOCK_FUNCTION;
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@ -296,7 +296,7 @@ public:
argptrs[i] = &args[i];
}
MethodBind *bind = create_method_bind(p_method);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@ -311,7 +311,7 @@ public:
}
MethodBind *bind = create_static_method_bind(p_method);
bind->set_instance_class(p_class);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@ -325,7 +325,7 @@ public:
argptrs[i] = &args[i];
}
MethodBind *bind = create_method_bind(p_method);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@ -340,7 +340,7 @@ public:
}
MethodBind *bind = create_static_method_bind(p_method);
bind->set_instance_class(p_class);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@ -353,7 +353,7 @@ public:
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_NULL_V(bind, nullptr);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return _bind_vararg_method(bind, p_name, p_default_args, false);
@ -366,7 +366,7 @@ public:
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_NULL_V(bind, nullptr);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return _bind_vararg_method(bind, p_name, p_default_args, true);

@ -72,23 +72,23 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
bool prepad = p_pad_align;
#endif
void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0));
void *mem = malloc(p_bytes + (prepad ? DATA_OFFSET : 0));
ERR_FAIL_NULL_V(mem, nullptr);
alloc_count.increment();
if (prepad) {
uint64_t *s = (uint64_t *)mem;
*s = p_bytes;
uint8_t *s8 = (uint8_t *)mem;
uint64_t *s = (uint64_t *)(s8 + SIZE_OFFSET);
*s = p_bytes;
#ifdef DEBUG_ENABLED
uint64_t new_mem_usage = mem_usage.add(p_bytes);
max_usage.exchange_if_greater(new_mem_usage);
#endif
return s8 + PAD_ALIGN;
return s8 + DATA_OFFSET;
} else {
return mem;
}
@ -108,8 +108,8 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
#endif
if (prepad) {
mem -= PAD_ALIGN;
uint64_t *s = (uint64_t *)mem;
mem -= DATA_OFFSET;
uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
#ifdef DEBUG_ENABLED
if (p_bytes > *s) {
@ -126,14 +126,14 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
} else {
*s = p_bytes;
mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN);
mem = (uint8_t *)realloc(mem, p_bytes + DATA_OFFSET);
ERR_FAIL_NULL_V(mem, nullptr);
s = (uint64_t *)mem;
s = (uint64_t *)(mem + SIZE_OFFSET);
*s = p_bytes;
return mem + PAD_ALIGN;
return mem + DATA_OFFSET;
}
} else {
mem = (uint8_t *)realloc(mem, p_bytes);
@ -158,10 +158,10 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
alloc_count.decrement();
if (prepad) {
mem -= PAD_ALIGN;
mem -= DATA_OFFSET;
#ifdef DEBUG_ENABLED
uint64_t *s = (uint64_t *)mem;
uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
mem_usage.sub(*s);
#endif

@ -38,10 +38,6 @@
#include <new>
#include <type_traits>
#ifndef PAD_ALIGN
#define PAD_ALIGN 16 //must always be greater than this at much
#endif
class Memory {
#ifdef DEBUG_ENABLED
static SafeNumeric<uint64_t> mem_usage;
@ -51,6 +47,17 @@ class Memory {
static SafeNumeric<uint64_t> alloc_count;
public:
// Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t
// ┌─────────────────┬──┬────────────────┬──┬───────────...
// │ uint64_t │░░│ uint64_t │░░│ T[]
// │ alloc size │░░│ element count │░░│ data
// └─────────────────┴──┴────────────────┴──┴───────────...
// Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
static constexpr size_t SIZE_OFFSET = 0;
static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t)));
static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t)));
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
static void free_static(void *p_ptr, bool p_pad_align = false);
@ -105,7 +112,7 @@ void memdelete(T *p_class) {
if (!predelete_handler(p_class)) {
return; // doesn't want to be deleted
}
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
@ -117,7 +124,7 @@ void memdelete_allocator(T *p_class) {
if (!predelete_handler(p_class)) {
return; // doesn't want to be deleted
}
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
@ -133,6 +140,10 @@ void memdelete_allocator(T *p_class) {
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
}
template <typename T>
T *memnew_arr_template(size_t p_elements) {
if (p_elements == 0) {
@ -142,12 +153,14 @@ T *memnew_arr_template(size_t p_elements) {
same strategy used by std::vector, and the Vector class, so it should be safe.*/
size_t len = sizeof(T) * p_elements;
uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true);
uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
T *failptr = nullptr; //get rid of a warning
ERR_FAIL_NULL_V(mem, failptr);
*(mem - 1) = p_elements;
if (!std::is_trivially_constructible<T>::value) {
uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
*(_elem_count_ptr) = p_elements;
if constexpr (!std::is_trivially_constructible_v<T>) {
T *elems = (T *)mem;
/* call operator new */
@ -166,16 +179,18 @@ T *memnew_arr_template(size_t p_elements) {
template <typename T>
size_t memarr_len(const T *p_class) {
uint64_t *ptr = (uint64_t *)p_class;
return *(ptr - 1);
uint8_t *ptr = (uint8_t *)p_class;
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
return *(_elem_count_ptr);
}
template <typename T>
void memdelete_arr(T *p_class) {
uint64_t *ptr = (uint64_t *)p_class;
uint8_t *ptr = (uint8_t *)p_class;
if (!std::is_trivially_destructible<T>::value) {
uint64_t elem_count = *(ptr - 1);
if constexpr (!std::is_trivially_destructible_v<T>) {
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
uint64_t elem_count = *(_elem_count_ptr);
for (uint64_t i = 0; i < elem_count; i++) {
p_class[i].~T();

@ -70,56 +70,6 @@ public:
}
};
// A very special kind of mutex, used in scenarios where these
// requirements hold at the same time:
// - Must be used with a condition variable (only binary mutexes are suitable).
// - Must have recursive semnantics (or simulate, as this one does).
// The implementation keeps the lock count in TS. Therefore, only
// one object of each version of the template can exists; hence the Tag argument.
// Tags must be unique across the Godot codebase.
// Also, don't forget to declare the thread_local variable on each use.
template <int Tag>
class SafeBinaryMutex {
friend class MutexLock<SafeBinaryMutex>;
using StdMutexType = THREADING_NAMESPACE::mutex;
mutable THREADING_NAMESPACE::mutex mutex;
static thread_local uint32_t count;
public:
_ALWAYS_INLINE_ void lock() const {
if (++count == 1) {
mutex.lock();
}
}
_ALWAYS_INLINE_ void unlock() const {
DEV_ASSERT(count);
if (--count == 0) {
mutex.unlock();
}
}
_ALWAYS_INLINE_ bool try_lock() const {
if (count) {
count++;
return true;
} else {
if (mutex.try_lock()) {
count++;
return true;
} else {
return false;
}
}
}
~SafeBinaryMutex() {
DEV_ASSERT(!count);
}
};
template <class MutexT>
class MutexLock {
friend class ConditionVariable;
@ -131,24 +81,6 @@ public:
lock(p_mutex.mutex) {}
};
// This specialization is needed so manual locking and MutexLock can be used
// at the same time on a SafeBinaryMutex.
template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
friend class ConditionVariable;
THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
public:
_ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
lock(p_mutex.mutex) {
SafeBinaryMutex<Tag>::count++;
};
_ALWAYS_INLINE_ ~MutexLock() {
SafeBinaryMutex<Tag>::count--;
};
};
using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care
@ -168,24 +100,12 @@ public:
bool try_lock() const { return true; }
};
template <int Tag>
class SafeBinaryMutex : public MutexImpl {
static thread_local uint32_t count;
};
template <class MutexT>
class MutexLock {
public:
MutexLock(const MutexT &p_mutex) {}
};
template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
public:
MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
~MutexLock() {}
};
using Mutex = MutexImpl;
using BinaryMutex = MutexImpl;

@ -0,0 +1,124 @@
/**************************************************************************/
/* safe_binary_mutex.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SAFE_BINARY_MUTEX_H
#define SAFE_BINARY_MUTEX_H
#include "core/error/error_macros.h"
#include "core/os/mutex.h"
#include "core/typedefs.h"
#ifdef THREADS_ENABLED
// A very special kind of mutex, used in scenarios where these
// requirements hold at the same time:
// - Must be used with a condition variable (only binary mutexes are suitable).
// - Must have recursive semnantics (or simulate, as this one does).
// The implementation keeps the lock count in TS. Therefore, only
// one object of each version of the template can exists; hence the Tag argument.
// Tags must be unique across the Godot codebase.
// Also, don't forget to declare the thread_local variable on each use.
template <int Tag>
class SafeBinaryMutex {
friend class MutexLock<SafeBinaryMutex>;
using StdMutexType = THREADING_NAMESPACE::mutex;
mutable THREADING_NAMESPACE::mutex mutex;
static thread_local uint32_t count;
public:
_ALWAYS_INLINE_ void lock() const {
if (++count == 1) {
mutex.lock();
}
}
_ALWAYS_INLINE_ void unlock() const {
DEV_ASSERT(count);
if (--count == 0) {
mutex.unlock();
}
}
_ALWAYS_INLINE_ bool try_lock() const {
if (count) {
count++;
return true;
} else {
if (mutex.try_lock()) {
count++;
return true;
} else {
return false;
}
}
}
~SafeBinaryMutex() {
DEV_ASSERT(!count);
}
};
// This specialization is needed so manual locking and MutexLock can be used
// at the same time on a SafeBinaryMutex.
template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
friend class ConditionVariable;
THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
public:
_ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
lock(p_mutex.mutex) {
SafeBinaryMutex<Tag>::count++;
};
_ALWAYS_INLINE_ ~MutexLock() {
SafeBinaryMutex<Tag>::count--;
};
};
#else // No threads.
template <int Tag>
class SafeBinaryMutex : public MutexImpl {
static thread_local uint32_t count;
};
template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
public:
MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
~MutexLock() {}
};
#endif // THREADS_ENABLED
#endif // SAFE_BINARY_MUTEX_H

@ -46,7 +46,7 @@ class CharString;
template <class T, class V>
class VMap;
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint64_t)
static_assert(std::is_trivially_destructible_v<std::atomic<uint64_t>>);
// Silence a false positive warning (see GH-52119).
#if defined(__GNUC__) && !defined(__clang__)
@ -89,18 +89,39 @@ private:
return ++x;
}
static constexpr USize ALLOC_PAD = sizeof(USize) * 2; // For size and atomic refcount.
// Alignment: ↓ max_align_t ↓ USize ↓ max_align_t
// ┌────────────────────┬──┬─────────────┬──┬───────────...
// │ SafeNumeric<USize> │░░│ USize │░░│ T[]
// │ ref. count │░░│ data size │░░│ data
// └────────────────────┴──┴─────────────┴──┴───────────...
// Offset: ↑ REF_COUNT_OFFSET ↑ SIZE_OFFSET ↑ DATA_OFFSET
static constexpr size_t REF_COUNT_OFFSET = 0;
static constexpr size_t SIZE_OFFSET = ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize) == 0) ? (REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) : ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) + alignof(USize) - ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize)));
static constexpr size_t DATA_OFFSET = ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t) == 0) ? (SIZE_OFFSET + sizeof(USize)) : ((SIZE_OFFSET + sizeof(USize)) + alignof(max_align_t) - ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t)));
mutable T *_ptr = nullptr;
// internal helpers
static _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount_ptr(uint8_t *p_ptr) {
return (SafeNumeric<USize> *)(p_ptr + REF_COUNT_OFFSET);
}
static _FORCE_INLINE_ USize *_get_size_ptr(uint8_t *p_ptr) {
return (USize *)(p_ptr + SIZE_OFFSET);
}
static _FORCE_INLINE_ T *_get_data_ptr(uint8_t *p_ptr) {
return (T *)(p_ptr + DATA_OFFSET);
}
_FORCE_INLINE_ SafeNumeric<USize> *_get_refcount() const {
if (!_ptr) {
return nullptr;
}
return reinterpret_cast<SafeNumeric<USize> *>(_ptr) - 2;
return (SafeNumeric<USize> *)((uint8_t *)_ptr - DATA_OFFSET + REF_COUNT_OFFSET);
}
_FORCE_INLINE_ USize *_get_size() const {
@ -108,7 +129,7 @@ private:
return nullptr;
}
return reinterpret_cast<USize *>(_ptr) - 1;
return (USize *)((uint8_t *)_ptr - DATA_OFFSET + SIZE_OFFSET);
}
_FORCE_INLINE_ USize _get_alloc_size(USize p_elements) const {
@ -233,7 +254,7 @@ void CowData<T>::_unref(void *p_data) {
}
// clean up
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
USize *count = _get_size();
T *data = (T *)(count + 1);
@ -244,7 +265,7 @@ void CowData<T>::_unref(void *p_data) {
}
// free mem
Memory::free_static(((uint8_t *)p_data) - ALLOC_PAD, false);
Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false);
}
template <class T>
@ -260,26 +281,27 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() {
/* in use by more than me */
USize current_size = *_get_size();
USize *mem_new = (USize *)Memory::alloc_static(_get_alloc_size(current_size) + ALLOC_PAD, false);
mem_new += 2;
uint8_t *mem_new = (uint8_t *)Memory::alloc_static(_get_alloc_size(current_size) + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, 0);
new (mem_new - 2) SafeNumeric<USize>(1); //refcount
*(mem_new - 1) = current_size; //size
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
USize *_size_ptr = _get_size_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
T *_data = (T *)(mem_new);
new (_refc_ptr) SafeNumeric<USize>(1); //refcount
*(_size_ptr) = current_size; //size
// initialize new elements
if (std::is_trivially_copyable<T>::value) {
memcpy(mem_new, _ptr, current_size * sizeof(T));
if constexpr (std::is_trivially_copyable_v<T>) {
memcpy((uint8_t *)_data_ptr, _ptr, current_size * sizeof(T));
} else {
for (USize i = 0; i < current_size; i++) {
memnew_placement(&_data[i], T(_ptr[i]));
memnew_placement(&_data_ptr[i], T(_ptr[i]));
}
}
_unref(_ptr);
_ptr = _data;
_ptr = _data_ptr;
rc = 1;
}
@ -315,27 +337,34 @@ Error CowData<T>::resize(Size p_size) {
if (alloc_size != current_alloc_size) {
if (current_size == 0) {
// alloc from scratch
USize *ptr = (USize *)Memory::alloc_static(alloc_size + ALLOC_PAD, false);
ptr += 2;
ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY);
*(ptr - 1) = 0; //size, currently none
new (ptr - 2) SafeNumeric<USize>(1); //refcount
uint8_t *mem_new = (uint8_t *)Memory::alloc_static(alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
USize *_size_ptr = _get_size_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
_ptr = (T *)ptr;
new (_refc_ptr) SafeNumeric<USize>(1); //refcount
*(_size_ptr) = 0; //size, currently none
_ptr = _data_ptr;
} else {
USize *_ptrnew = (USize *)Memory::realloc_static(((uint8_t *)_ptr) - ALLOC_PAD, alloc_size + ALLOC_PAD, false);
ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
_ptrnew += 2;
new (_ptrnew - 2) SafeNumeric<USize>(rc); //refcount
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
_ptr = (T *)(_ptrnew);
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
_ptr = _data_ptr;
}
}
// construct the newly created elements
if (!std::is_trivially_constructible<T>::value) {
if constexpr (!std::is_trivially_constructible_v<T>) {
for (Size i = *_get_size(); i < p_size; i++) {
memnew_placement(&_ptr[i], T);
}
@ -346,7 +375,7 @@ Error CowData<T>::resize(Size p_size) {
*_get_size() = p_size;
} else if (p_size < current_size) {
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
// deinitialize no longer needed elements
for (USize i = p_size; i < *_get_size(); i++) {
T *t = &_ptr[i];
@ -355,12 +384,15 @@ Error CowData<T>::resize(Size p_size) {
}
if (alloc_size != current_alloc_size) {
USize *_ptrnew = (USize *)Memory::realloc_static(((uint8_t *)_ptr) - ALLOC_PAD, alloc_size + ALLOC_PAD, false);
ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
_ptrnew += 2;
new (_ptrnew - 2) SafeNumeric<USize>(rc); //refcount
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
T *_data_ptr = _get_data_ptr(mem_new);
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
_ptr = (T *)(_ptrnew);
_ptr = _data_ptr;
}
*_get_size() = p_size;

@ -64,7 +64,7 @@ public:
CRASH_COND_MSG(!data, "Out of memory");
}
if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) {
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
memnew_placement(&data[count++], T(p_elem));
} else {
data[count++] = p_elem;
@ -77,7 +77,7 @@ public:
for (U i = p_index; i < count; i++) {
data[i] = data[i + 1];
}
if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
}
@ -90,7 +90,7 @@ public:
if (count > p_index) {
data[p_index] = data[count];
}
if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
}
@ -133,7 +133,7 @@ public:
_FORCE_INLINE_ U size() const { return count; }
void resize(U p_size) {
if (p_size < count) {
if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
for (U i = p_size; i < count; i++) {
data[i].~T();
}
@ -145,7 +145,7 @@ public:
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
}
if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) {
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
for (U i = count; i < p_size; i++) {
memnew_placement(&data[i], T);
}

@ -101,7 +101,7 @@ public:
private:
void _reset(bool p_allow_unfreed) {
if (!p_allow_unfreed || !std::is_trivially_destructible<T>::value) {
if (!p_allow_unfreed || !std::is_trivially_destructible_v<T>) {
ERR_FAIL_COND(allocs_available < pages_allocated * page_size);
}
if (pages_allocated) {

@ -202,7 +202,7 @@ public:
uint32_t page = count >> page_size_shift;
uint32_t offset = count & page_size_mask;
if (!std::is_trivially_constructible<T>::value) {
if constexpr (!std::is_trivially_constructible_v<T>) {
memnew_placement(&page_data[page][offset], T(p_value));
} else {
page_data[page][offset] = p_value;
@ -214,7 +214,7 @@ public:
_FORCE_INLINE_ void pop_back() {
ERR_FAIL_COND(count == 0);
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
uint32_t page = (count - 1) >> page_size_shift;
uint32_t offset = (count - 1) & page_size_mask;
page_data[page][offset].~T();
@ -237,7 +237,7 @@ public:
void clear() {
//destruct if needed
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
for (uint64_t i = 0; i < count; i++) {
uint32_t page = i >> page_size_shift;
uint32_t offset = i & page_size_mask;
@ -318,13 +318,13 @@ public:
uint32_t to_copy = MIN(page_size - new_remainder, remainder);
for (uint32_t i = 0; i < to_copy; i++) {
if (!std::is_trivially_constructible<T>::value) {
if constexpr (!std::is_trivially_constructible_v<T>) {
memnew_placement(&dst_page[i + new_remainder], T(remainder_page[i + remainder - to_copy]));
} else {
dst_page[i + new_remainder] = remainder_page[i + remainder - to_copy];
}
if (!std::is_trivially_destructible<T>::value) {
if constexpr (!std::is_trivially_destructible_v<T>) {
remainder_page[i + remainder - to_copy].~T();
}
}

@ -54,7 +54,7 @@
#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \
static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \
static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \
static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value);
static_assert(std::is_trivially_destructible_v<std::atomic<m_type>>);
#define SAFE_FLAG_TYPE_PUN_GUARANTEES \
static_assert(sizeof(SafeFlag) == sizeof(bool)); \
static_assert(alignof(SafeFlag) == alignof(bool));

@ -51,7 +51,7 @@ template <class T>
struct VariantCaster {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
if constexpr (std::is_base_of<Object, TStripped>::value) {
if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@ -63,7 +63,7 @@ template <class T>
struct VariantCaster<T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
if constexpr (std::is_base_of<Object, TStripped>::value) {
if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@ -75,7 +75,7 @@ template <class T>
struct VariantCaster<const T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
if constexpr (std::is_base_of<Object, TStripped>::value) {
if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@ -226,7 +226,7 @@ template <typename T>
struct VariantObjectClassChecker {
static _FORCE_INLINE_ bool check(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
if constexpr (std::is_base_of<Object, TStripped>::value) {
if constexpr (std::is_base_of_v<Object, TStripped>) {
Object *obj = p_variant;
return Object::cast_to<TStripped>(p_variant) || !obj;
} else {

@ -43,14 +43,10 @@ struct EnableIf<false, T> {
};
template <typename, typename>
struct TypesAreSame {
static bool const value = false;
};
inline constexpr bool types_are_same_v = false;
template <typename A>
struct TypesAreSame<A, A> {
static bool const value = true;
};
template <typename T>
inline constexpr bool types_are_same_v<T, T> = true;
template <typename B, typename D>
struct TypeInherits {
@ -60,7 +56,7 @@ struct TypeInherits {
static char (&test(...))[2];
static bool const value = sizeof(test(get_d())) == sizeof(char) &&
!TypesAreSame<B volatile const, void volatile const>::value;
!types_are_same_v<B volatile const, void volatile const>;
};
namespace GodotTypeInfo {

@ -98,10 +98,18 @@
<return type="void" />
<description>
Calls the method represented by this [Callable] in deferred mode, i.e. at the end of the current frame. Arguments can be passed and should match the method's signature.
[codeblock]
[codeblocks]
[gdscript]
func _ready():
grab_focus.call_deferred()
[/codeblock]
[/gdscript]
[csharp]
public override void _Ready()
{
Callable.From(GrabFocus).CallDeferred();
}
[/csharp]
[/codeblocks]
See also [method Object.call_deferred].
</description>
</method>

@ -8,7 +8,7 @@
Used in scripting by [EditorExportPlugin] to configure platform-specific customization of scenes and resources. See [method EditorExportPlugin._begin_customize_scenes] and [method EditorExportPlugin._begin_customize_resources] for more details.
</description>
<tutorials>
<link title="$DOCS_URL/tutorials/platform/consoles.html">Console support in Godot</link>
<link title="Console support in Godot">$DOCS_URL/tutorials/platform/consoles.html</link>
</tutorials>
<methods>
<method name="get_os_name" qualifiers="const">

@ -9,9 +9,23 @@
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_max_height" qualifiers="const">
<return type="float" />
<description>
Returns the largest height value found in [member map_data]. Recalculates only when [member map_data] changes.
</description>
</method>
<method name="get_min_height" qualifiers="const">
<return type="float" />
<description>
Returns the smallest height value found in [member map_data]. Recalculates only when [member map_data] changes.
</description>
</method>
</methods>
<members>
<member name="map_data" type="PackedFloat32Array" setter="set_map_data" getter="get_map_data" default="PackedFloat32Array(0, 0, 0, 0)">
Height map data, pool array must be of [member map_width] * [member map_depth] size.
Height map data. The array's size must be equal to [member map_width] multiplied by [member map_depth].
</member>
<member name="map_depth" type="int" setter="set_map_depth" getter="get_map_depth" default="2">
Number of vertices in the depth of the height map. Changing this will resize the [member map_data].

@ -23,6 +23,12 @@
Returns an array of nodes that were added as collision exceptions for this body.
</description>
</method>
<method name="get_gravity" qualifiers="const">
<return type="Vector2" />
<description>
Returns the gravity vector computed from all sources that can affect the body, including all gravity overrides from [Area2D] nodes and the global world gravity.
</description>
</method>
<method name="move_and_collide">
<return type="KinematicCollision2D" />
<param index="0" name="motion" type="Vector2" />

@ -31,6 +31,12 @@
Returns an array of nodes that were added as collision exceptions for this body.
</description>
</method>
<method name="get_gravity" qualifiers="const">
<return type="Vector3" />
<description>
Returns the gravity vector computed from all sources that can affect the body, including all gravity overrides from [Area3D] nodes and the global world gravity.
</description>
</method>
<method name="move_and_collide">
<return type="KinematicCollision3D" />
<param index="0" name="motion" type="Vector3" />

@ -649,7 +649,9 @@ void EditorData::remove_scene(int p_idx) {
EditorNode::get_singleton()->emit_signal("scene_closed", edited_scene[p_idx].path);
}
if (undo_redo_manager->has_history(edited_scene[p_idx].history_id)) { // Might not exist if scene failed to load.
undo_redo_manager->discard_history(edited_scene[p_idx].history_id);
}
edited_scene.remove_at(p_idx);
}

@ -375,6 +375,10 @@ bool EditorUndoRedoManager::has_redo() {
return false;
}
bool EditorUndoRedoManager::has_history(int p_idx) const {
return history_map.has(p_idx);
}
void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) {
if (p_idx != INVALID_HISTORY) {
History &history = get_or_create_history(p_idx);

@ -130,6 +130,7 @@ public:
bool is_history_unsaved(int p_idx);
bool has_undo();
bool has_redo();
bool has_history(int p_idx) const;
String get_current_action_name();
int get_current_action_history_id();

@ -5661,7 +5661,7 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
if (texture != nullptr || scene != nullptr) {
bool root_node_selected = EditorNode::get_singleton()->get_editor_selection()->is_selected(EditorNode::get_singleton()->get_edited_scene());
String desc = TTR("Drag and drop to add as child of current scene's root node.") + "\n" + vformat(TTR("Hold %s when dropping to add as child of selected node."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL));
String desc = TTR("Drag and drop to add as child of selected node.") + "\n" + TTR("Hold Alt when dropping to add as child of root node.");
if (!root_node_selected) {
desc += "\n" + TTR("Hold Shift when dropping to add as sibling of selected node.");
}
@ -5672,7 +5672,7 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
preview_node->add_child(sprite);
label->show();
label_desc->show();
desc += "\n" + TTR("Hold Alt when dropping to add as a different node type.");
desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type.");
label_desc->set_text(desc);
} else {
if (scene.is_valid()) {
@ -5965,7 +5965,6 @@ bool CanvasItemEditorViewport::_only_packed_scenes_selected() const {
void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) {
bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT);
bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL);
bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
selected_files.clear();
@ -5981,9 +5980,9 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
Node *root_node = EditorNode::get_singleton()->get_edited_scene();
if (selected_nodes.size() > 0) {
Node *selected_node = selected_nodes[0];
target_node = root_node;
if (is_ctrl) {
target_node = selected_node;
if (is_alt) {
target_node = root_node;
} else if (is_shift && selected_node != root_node) {
target_node = selected_node->get_parent();
}
@ -5997,7 +5996,7 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
drop_pos = p_point;
if (is_alt && !_only_packed_scenes_selected()) {
if (is_alt && is_shift && !_only_packed_scenes_selected()) {
_show_resource_type_selector();
} else {
_perform_drop_data();
@ -6030,6 +6029,10 @@ void CanvasItemEditorViewport::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
disconnect("mouse_exited", callable_mp(this, &CanvasItemEditorViewport::_on_mouse_exit));
} break;
case NOTIFICATION_DRAG_END: {
_remove_preview();
} break;
}
}

@ -1749,8 +1749,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_edit.mode = TRANSFORM_NONE;
_edit.original = spatial_editor->get_gizmo_transform(); // To prevent to break when flipping with scale.
bool node_selected = spatial_editor->get_single_selected_node();
bool can_select_gizmos = node_selected;
bool can_select_gizmos = spatial_editor->get_single_selected_node();
{
int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
@ -1840,6 +1839,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
clicked = ObjectID();
bool node_selected = get_selected_count() > 0;
if (node_selected && ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_or_control_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)) {
begin_transform(TRANSFORM_ROTATE, false);
break;
@ -1985,9 +1986,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else {
const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
// enable region-select if nothing has been selected yet or multi-select (shift key) is active
if (selection_in_progress && movement_threshold_passed) {
if (get_selected_count() == 0 || clicked_wants_append) {
if (selection_in_progress && movement_threshold_passed && clicked.is_valid()) {
if (clicked_wants_append || !editor_selection->is_selected(Object::cast_to<Node>(ObjectDB::get_instance(clicked)))) {
cursor.region_select = true;
cursor.region_begin = _edit.original_mouse_pos;
clicked = ObjectID();
@ -3017,6 +3017,8 @@ void Node3DEditorViewport::_notification(int p_what) {
// Clear preview material when dropped outside applicable object.
if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) {
_remove_preview_material();
} else {
_remove_preview_node();
}
} break;
}
@ -4530,7 +4532,7 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
}
bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT);
bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
selected_files.clear();
Dictionary d = p_data;
@ -4540,15 +4542,15 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
List<Node *> selected_nodes = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list();
Node *root_node = EditorNode::get_singleton()->get_edited_scene();
if (selected_nodes.size() == 1) {
if (selected_nodes.size() > 0) {
Node *selected_node = selected_nodes[0];
target_node = root_node;
if (is_ctrl) {
target_node = selected_node;
if (is_alt) {
target_node = root_node;
} else if (is_shift && selected_node != root_node) {
target_node = selected_node->get_parent();
}
} else if (selected_nodes.size() == 0) {
} else {
if (root_node) {
target_node = root_node;
} else {
@ -4556,11 +4558,6 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
target_node = get_tree()->get_edited_scene_root();
}
} else {
accept->set_text(TTR("Cannot drag and drop into multiple selected nodes."));
accept->popup_centered();
_remove_preview_node();
return;
}
drop_pos = p_point;

@ -226,8 +226,8 @@ void TextureRegionEditor::_texture_overlay_draw() {
hscroll->set_value((hscroll->get_min() + hscroll->get_max() - hscroll->get_page()) / 2);
vscroll->set_value((vscroll->get_min() + vscroll->get_max() - vscroll->get_page()) / 2);
// This ensures that the view is updated correctly.
callable_mp(this, &TextureRegionEditor::_pan_callback).bind(Vector2(1, 0)).call_deferred();
callable_mp(this, &TextureRegionEditor::_scroll_changed).bind(0.0).call_deferred();
callable_mp(this, &TextureRegionEditor::_pan_callback).call_deferred(Vector2(1, 0), Ref<InputEvent>());
callable_mp(this, &TextureRegionEditor::_scroll_changed).call_deferred(0.0);
request_center = false;
}

@ -1824,7 +1824,6 @@ bool SceneTreeDock::_check_node_path_recursive(Node *p_root_node, Variant &r_var
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(resource, propertyname, updated_variant);
undo_redo->add_undo_property(resource, propertyname, old_variant);
resource->set(propertyname, updated_variant);
}
}
break;
@ -1968,7 +1967,6 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(p_base, propertyname, updated_variant);
undo_redo->add_undo_property(p_base, propertyname, old_variant);
p_base->set(propertyname, updated_variant);
}
}
@ -2849,7 +2847,6 @@ void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(p_base, propertyname, updated_variant);
undo_redo->add_undo_property(p_base, propertyname, old_variant);
p_base->set(propertyname, updated_variant);
if (!warn_message.is_empty()) {
String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/.");
WARN_PRINT(warn_message + vformat("Removing the node from variable \"%s\" on node \"%s\".", propertyname, node_path));

@ -6,14 +6,11 @@ extends _BASE_
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
velocity.y += gravity * delta
velocity += get_gravity() * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():

@ -6,14 +6,11 @@ extends _BASE_
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
velocity += get_gravity() * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():

@ -672,6 +672,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
bool has_decimal = false;
bool has_exponent = false;
bool has_error = false;
bool need_digits = false;
bool (*digit_check_func)(char32_t) = is_digit;
// Sign before hexadecimal or binary.
@ -686,11 +687,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
// Hexadecimal.
base = 16;
digit_check_func = is_hex_digit;
need_digits = true;
_advance();
} else if (_peek() == 'b') {
// Binary.
base = 2;
digit_check_func = is_binary_digit;
need_digits = true;
_advance();
}
}
@ -717,6 +720,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
}
previous_was_underscore = true;
} else {
need_digits = false;
previous_was_underscore = false;
}
_advance();
@ -820,6 +824,16 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
}
}
if (need_digits) {
// No digits in hex or bin literal.
Token error = make_error(vformat(R"(Expected %s digit after "0%c".)", (base == 16 ? "hexadecimal" : "binary"), (base == 16 ? 'x' : 'b')));
error.start_column = column;
error.leftmost_column = column;
error.end_column = column + 1;
error.rightmost_column = column + 1;
return error;
}
// Detect extra decimal point.
if (!has_error && has_decimal && _peek() == '.' && _peek(1) != '.') {
Token error = make_error("Cannot use a decimal point twice in a number.");

@ -5982,8 +5982,13 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, const GL
p_scene_parent = bone_attachment;
}
if (skeleton->get_parent() == nullptr) {
if (p_scene_root) {
p_scene_parent->add_child(skeleton, true);
skeleton->set_owner(p_scene_root);
} else {
p_scene_parent = skeleton;
p_scene_root = skeleton;
}
}
}
@ -6582,7 +6587,7 @@ float GLTFDocument::get_max_component(const Color &p_color) {
return MAX(MAX(r, g), b);
}
void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state) {
void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root) {
for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) {
Ref<GLTFNode> node = p_state->nodes[node_i];
@ -6607,7 +6612,7 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state) {
mi->get_parent()->remove_child(mi);
skeleton->add_child(mi, true);
mi->set_owner(skeleton->get_owner());
mi->set_owner(p_scene_root);
mi->set_skin(p_state->skins.write[skin_i]->godot_skin);
mi->set_skeleton_path(mi->get_path_to(skeleton));
@ -7461,7 +7466,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
Error err = OK;
Node *root = _generate_scene_node_tree(p_state);
ERR_FAIL_NULL_V(root, nullptr);
_process_mesh_instances(p_state);
_process_mesh_instances(p_state, root);
if (p_state->get_create_animations() && p_state->animations.size()) {
AnimationPlayer *ap = memnew(AnimationPlayer);
root->add_child(ap, true);

@ -326,7 +326,7 @@ public:
Error _parse_gltf_state(Ref<GLTFState> p_state, const String &p_search_path);
Error _parse_asset_header(Ref<GLTFState> p_state);
Error _parse_gltf_extensions(Ref<GLTFState> p_state);
void _process_mesh_instances(Ref<GLTFState> p_state);
void _process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root);
Node *_generate_scene_node_tree(Ref<GLTFState> p_state);
void _generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);

@ -8,20 +8,21 @@ public partial class _CLASS_ : _BASE_
public const float Speed = 300.0f;
public const float JumpVelocity = -400.0f;
// Get the gravity from the project settings to be synced with RigidBody nodes.
public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
public override void _PhysicsProcess(double delta)
{
Vector2 velocity = Velocity;
// Add the gravity.
if (!IsOnFloor())
velocity.Y += gravity * (float)delta;
{
velocity += GetGravity() * (float)delta;
}
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
{
velocity.Y = JumpVelocity;
}
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.

@ -8,20 +8,21 @@ public partial class _CLASS_ : _BASE_
public const float Speed = 5.0f;
public const float JumpVelocity = 4.5f;
// Get the gravity from the project settings to be synced with RigidBody nodes.
public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();
public override void _PhysicsProcess(double delta)
{
Vector3 velocity = Velocity;
// Add the gravity.
if (!IsOnFloor())
velocity.Y -= gravity * (float)delta;
{
velocity += GetGravity() * (float)delta;
}
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
{
velocity.Y = JumpVelocity;
}
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.

@ -67,13 +67,14 @@ def get_mvk_sdk_path():
if not os.path.exists(dirname):
return ""
ver_min = ver_parse("1.3.231.0")
ver_num = ver_parse("0.0.0.0")
files = os.listdir(dirname)
lib_name_out = dirname
for file in files:
if os.path.isdir(os.path.join(dirname, file)):
ver_comp = ver_parse(file)
if ver_comp > ver_num:
if ver_comp > ver_num and ver_comp >= ver_min:
# Try new SDK location.
lib_name = os.path.join(
os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/"

@ -206,6 +206,11 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"])
# Allow use to take control of swapping WebGL buffers.
env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"])
# Breaking change since emscripten 3.1.51
# https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#3151---121323
if cc_semver >= (3, 1, 51):
# Enables the use of *glGetProcAddress()
env.Append(LINKFLAGS=["-s", "GL_ENABLE_GET_PROC_ADDRESS=1"])
if env["javascript_eval"]:
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
@ -229,6 +234,9 @@ def configure(env: "Environment"):
# Workaround https://github.com/emscripten-core/emscripten/issues/19781.
if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46):
env.Append(LINKFLAGS=["-Wl,-u,scalbnf"])
# Workaround https://github.com/emscripten-core/emscripten/issues/16836.
if cc_semver >= (3, 1, 47):
env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"])
if env["dlink_enabled"]:
if env["proxy_to_pthread"]:

@ -35,6 +35,7 @@
void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_gravity"), &PhysicsBody2D::get_gravity);
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
@ -145,6 +146,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion
return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
}
Vector2 PhysicsBody2D::get_gravity() const {
PhysicsDirectBodyState2D *state = PhysicsServer2D::get_singleton()->body_get_direct_state(get_rid());
ERR_FAIL_NULL_V(state, Vector2());
return state->get_total_gravity();
}
TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() {
List<RID> exceptions;
PhysicsServer2D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);

@ -52,6 +52,7 @@ protected:
public:
bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08, bool p_recovery_as_collision = false);
Vector2 get_gravity() const;
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody

@ -35,6 +35,7 @@
void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
ClassDB::bind_method(D_METHOD("get_gravity"), &PhysicsBody3D::get_gravity);
ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock);
ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock);
@ -187,6 +188,12 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion
return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
}
Vector3 PhysicsBody3D::get_gravity() const {
PhysicsDirectBodyState3D *state = PhysicsServer3D::get_singleton()->body_get_direct_state(get_rid());
ERR_FAIL_NULL_V(state, Vector3());
return state->get_total_gravity();
}
void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) {
if (p_lock) {
locked_axis |= p_axis;

@ -55,6 +55,7 @@ protected:
public:
bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1);
Vector3 get_gravity() const;
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;

@ -7333,7 +7333,7 @@ void TextEdit::_update_scrollbars() {
int visible_rows = get_visible_line_count();
int total_rows = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_total_visible_line_count();
if (scroll_past_end_of_file_enabled) {
if (scroll_past_end_of_file_enabled && !fit_content_height) {
total_rows += visible_rows - 1;
}

@ -2587,12 +2587,8 @@ int Tree::_count_selected_items(TreeItem *p_from) const {
}
}
if (p_from->get_first_child()) {
count += _count_selected_items(p_from->get_first_child());
}
if (p_from->get_next()) {
count += _count_selected_items(p_from->get_next());
for (TreeItem *c = p_from->get_first_child(); c; c = c->get_next()) {
count += _count_selected_items(c);
}
return count;

@ -179,6 +179,14 @@ Vector<real_t> HeightMapShape3D::get_map_data() const {
return map_data;
}
real_t HeightMapShape3D::get_min_height() const {
return min_height;
}
real_t HeightMapShape3D::get_max_height() const {
return max_height;
}
void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width);
ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width);
@ -186,6 +194,8 @@ void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape3D::get_map_depth);
ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape3D::set_map_data);
ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape3D::get_map_data);
ClassDB::bind_method(D_METHOD("get_min_height"), &HeightMapShape3D::get_min_height);
ClassDB::bind_method(D_METHOD("get_max_height"), &HeightMapShape3D::get_max_height);
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");

@ -54,6 +54,9 @@ public:
void set_map_data(Vector<real_t> p_new);
Vector<real_t> get_map_data() const;
real_t get_min_height() const;
real_t get_max_height() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;

@ -243,10 +243,10 @@ inline bool is_convertible_array(Variant::Type type) {
}
template <class, class = void>
struct is_vector_type : std::false_type {};
inline constexpr bool is_vector_type_v = false;
template <class T>
struct is_vector_type<T, std::void_t<decltype(T::AXIS_COUNT)>> : std::true_type {};
inline constexpr bool is_vector_type_v<T, std::void_t<decltype(T::AXIS_COUNT)>> = true;
template <typename T, typename P>
void convert_item_std140(const T &p_item, P *p_write, bool p_compact = false) {
@ -274,7 +274,7 @@ Vector<P> convert_array_std140(const Variant &p_variant, [[maybe_unused]] bool p
const Variant &item = array.get(i);
P *offset = write + (i * elements);
if constexpr (is_vector_type<T>::value) {
if constexpr (is_vector_type_v<T>) {
const T &vec = convert_to_vector<T>(item, p_linear_color);
convert_item_std140<T, P>(vec, offset, true);
} else {