diff options
Diffstat (limited to 'core')
66 files changed, 704 insertions, 466 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 45776c03e4..b5f1015ff5 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1235,8 +1235,8 @@ ProjectSettings::ProjectSettings() { // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); - GLOBAL_DEF("physics/2d/run_on_thread", false); - GLOBAL_DEF("physics/3d/run_on_thread", false); + GLOBAL_DEF("physics/2d/run_on_separate_thread", false); + GLOBAL_DEF("physics/3d/run_on_separate_thread", false); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9a3234d4a2..8d03f35617 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -623,7 +623,6 @@ void OS::_bind_methods() { // Those default values need to be specified for the docs generator, // to avoid using values from the documentation writer's own OS instance. - ADD_PROPERTY_DEFAULT("exit_code", 0); ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false); ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index a45430465f..7c7d38ab0a 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -68,8 +68,8 @@ void RemoteDebuggerPeerTCP::close() { running = false; thread.wait_to_finish(); tcp_client->disconnect_from_host(); - out_buf.resize(0); - in_buf.resize(0); + out_buf.clear(); + in_buf.clear(); } RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) { @@ -190,7 +190,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) { - const uint64_t min_tick = 100; + // Update in time for 144hz monitors + const uint64_t min_tick = 6900; RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud; while (peer->running && peer->is_peer_connected()) { uint64_t ticks_usec = OS::get_singleton()->get_ticks_usec(); @@ -225,7 +226,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) { String debug_host = p_uri.replace("tcp://", ""); uint16_t debug_port = 6007; - if (debug_host.find(":") != -1) { + if (debug_host.contains(":")) { int sep_pos = debug_host.rfind(":"); debug_port = debug_host.substr(sep_pos + 1).to_int(); debug_host = debug_host.substr(0, sep_pos); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 36723e5568..4dd93249ef 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -104,7 +104,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int // Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way. error_stack_info.append_array(p_stack_info); EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type); - error_stack_info.resize(0); + error_stack_info.clear(); } Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { diff --git a/core/input/input.cpp b/core/input/input.cpp index fa2f00bf8d..d36d0f4da0 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -90,6 +90,7 @@ Input::MouseMode Input::get_mouse_mode() const { } void Input::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed); ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); @@ -189,35 +190,53 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) { float delta_t = tdiff / 1000000.0; last_tick = tick; + if (delta_t > max_ref_frame) { + // First movement in a long time, reset and start again. + velocity = Vector2(); + accum = p_delta_p; + accum_t = 0; + return; + } + accum += p_delta_p; accum_t += delta_t; - if (accum_t > max_ref_frame * 10) { - accum_t = max_ref_frame * 10; + if (accum_t < min_ref_frame) { + // Not enough time has passed to calculate speed precisely. + return; } - while (accum_t >= min_ref_frame) { - float slice_t = min_ref_frame / accum_t; - Vector2 slice = accum * slice_t; - accum = accum - slice; - accum_t -= min_ref_frame; - - velocity = (slice / min_ref_frame).lerp(velocity, min_ref_frame / max_ref_frame); - } + velocity = accum / accum_t; + accum = Vector2(); + accum_t = 0; } void Input::VelocityTrack::reset() { last_tick = OS::get_singleton()->get_ticks_usec(); velocity = Vector2(); + accum = Vector2(); accum_t = 0; } Input::VelocityTrack::VelocityTrack() { min_ref_frame = 0.1; - max_ref_frame = 0.3; + max_ref_frame = 3.0; reset(); } +bool Input::is_anything_pressed() const { + _THREAD_SAFE_METHOD_ + + for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { + if (E->get().pressed) { + return true; + } + } + return !keys_pressed.is_empty() || + !joy_buttons_pressed.is_empty() || + mouse_button_mask > MouseButton::NONE; +} + bool Input::is_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ return keys_pressed.has(p_keycode); @@ -504,18 +523,20 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos = mm->get_global_position(); - if (mouse_pos != pos) { - set_mouse_position(pos); + Point2 position = mm->get_global_position(); + if (mouse_pos != position) { + set_mouse_position(position); } + Vector2 relative = mm->get_relative(); + mouse_velocity_track.update(relative); if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) { Ref<InputEventScreenDrag> drag_event; drag_event.instantiate(); - drag_event->set_position(mm->get_position()); - drag_event->set_relative(mm->get_relative()); - drag_event->set_velocity(mm->get_velocity()); + drag_event->set_position(position); + drag_event->set_relative(relative); + drag_event->set_velocity(get_last_mouse_velocity()); event_dispatch_function(drag_event); } @@ -696,7 +717,6 @@ void Input::set_gyroscope(const Vector3 &p_gyroscope) { } void Input::set_mouse_position(const Point2 &p_posf) { - mouse_velocity_track.update(p_posf - mouse_pos); mouse_pos = p_posf; } @@ -704,7 +724,8 @@ Point2 Input::get_mouse_position() const { return mouse_pos; } -Point2 Input::get_last_mouse_velocity() const { +Point2 Input::get_last_mouse_velocity() { + mouse_velocity_track.update(Vector2()); return mouse_velocity_track.velocity; } diff --git a/core/input/input.h b/core/input/input.h index e5ef31ab4f..ab2cd377f4 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -242,6 +242,7 @@ public: static Input *get_singleton(); + bool is_anything_pressed() const; bool is_key_pressed(Key p_keycode) const; bool is_physical_key_pressed(Key p_keycode) const; bool is_mouse_button_pressed(MouseButton p_button) const; @@ -269,7 +270,7 @@ public: Vector3 get_gyroscope() const; Point2 get_mouse_position() const; - Vector2 get_last_mouse_velocity() const; + Vector2 get_last_mouse_velocity(); MouseButton get_mouse_button_mask() const; void warp_mouse_position(const Vector2 &p_to); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index c608076a21..ab0f36132f 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -86,7 +86,7 @@ Ref<InputEvent> InputEvent::xformed_by(const Transform2D &p_xform, const Vector2 return Ref<InputEvent>((InputEvent *)this); } -bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { return false; } @@ -412,35 +412,32 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) { return ie; } -bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventKey> key = p_event; if (key.is_null()) { return false; } - bool match = false; - if (get_keycode() == Key::NONE) { - Key code = get_physical_keycode_with_modifiers(); - Key event_code = key->get_physical_keycode_with_modifiers(); - - match = get_physical_keycode() == key->get_physical_keycode() && (!key->is_pressed() || (code & event_code) == code); + bool match; + if (keycode != Key::NONE) { + match = keycode == key->keycode; } else { - Key code = get_keycode_with_modifiers(); - Key event_code = key->get_keycode_with_modifiers(); - - match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code); + match = get_physical_keycode() == key->get_physical_keycode(); + } + if (p_exact_match) { + match &= get_modifiers_mask() == key->get_modifiers_mask(); } if (match) { bool pressed = key->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } return match; @@ -585,24 +582,27 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co return mb; } -bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventMouseButton> mb = p_event; if (mb.is_null()) { return false; } - bool match = mb->button_index == button_index; + bool match = button_index == mb->button_index; + if (p_exact_match) { + match &= get_modifiers_mask() == mb->get_modifiers_mask(); + } if (match) { bool pressed = mb->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } @@ -887,36 +887,40 @@ bool InputEventJoypadMotion::is_pressed() const { return Math::abs(axis_value) >= 0.5f; } -bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventJoypadMotion> jm = p_event; if (jm.is_null()) { return false; } - bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event. + // Matches even if not in the same direction, but returns a "not pressed" event. + bool match = axis == jm->axis; + if (p_exact_match) { + match &= (axis_value < 0) == (jm->axis_value < 0); + } if (match) { float jm_abs_axis_value = Math::abs(jm->get_axis_value()); bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0); bool pressed = same_direction && jm_abs_axis_value >= p_deadzone; - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } - if (p_strength != nullptr) { + if (r_strength != nullptr) { if (pressed) { if (p_deadzone == 1.0f) { - *p_strength = 1.0f; + *r_strength = 1.0f; } else { - *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f); + *r_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f); } } else { - *p_strength = 0.0f; + *r_strength = 0.0f; } } - if (p_raw_strength != nullptr) { + if (r_raw_strength != nullptr) { if (same_direction) { // NOT pressed, because we want to ignore the deadzone. - *p_raw_strength = jm_abs_axis_value; + *r_raw_strength = jm_abs_axis_value; } else { - *p_raw_strength = 0.0f; + *r_raw_strength = 0.0f; } } } @@ -994,7 +998,7 @@ float InputEventJoypadButton::get_pressure() const { return pressure; } -bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventJoypadButton> jb = p_event; if (jb.is_null()) { return false; @@ -1003,15 +1007,15 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool * bool match = button_index == jb->button_index; if (match) { bool pressed = jb->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } @@ -1217,8 +1221,9 @@ String InputEventScreenDrag::to_string() { bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) { Ref<InputEventScreenDrag> drag = p_event; - if (drag.is_null()) + if (drag.is_null()) { return false; + } if (get_index() != drag->get_index()) { return false; @@ -1288,7 +1293,7 @@ bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; } -bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventAction> act = p_event; if (act.is_null()) { return false; @@ -1297,15 +1302,15 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres bool match = action == act->action; if (match) { bool pressed = act->pressed; - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } return match; diff --git a/core/input/input_event.h b/core/input/input_event.h index 29450dfc52..114db46623 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -79,7 +79,7 @@ public: virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const; virtual bool is_action_type() const; @@ -192,7 +192,7 @@ public: Key get_keycode_with_modifiers() const; Key get_physical_keycode_with_modifiers() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -255,7 +255,7 @@ public: virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -315,7 +315,7 @@ public: virtual bool is_pressed() const override; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -344,7 +344,7 @@ public: void set_pressure(float p_pressure); float get_pressure() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -437,7 +437,7 @@ public: virtual bool is_action(const StringName &p_action) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 753ac72ab6..41083b4c47 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -126,15 +126,13 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { int device = E->get()->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (p_exact_match && E->get()->is_match(p_event, true)) { - return E; - } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { return E; } } @@ -217,40 +215,28 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { - bool pressed = input_event_action->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + const bool pressed = input_event_action->is_pressed(); + if (r_pressed != nullptr) { + *r_pressed = pressed; + } + const float strength = pressed ? input_event_action->get_strength() : 0.0f; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_strength != nullptr) { - *p_strength = pressed ? input_event_action->get_strength() : 0.0f; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } return input_event_action->get_action() == p_action; } - bool pressed; - float strength; - float raw_strength; - List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); - if (event != nullptr) { - if (p_pressed != nullptr) { - *p_pressed = pressed; - } - if (p_strength != nullptr) { - *p_strength = strength; - } - if (p_raw_strength != nullptr) { - *p_raw_strength = raw_strength; - } - return true; - } else { - return false; - } + List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, r_pressed, r_strength, r_raw_strength); + return event != nullptr; } const OrderedHashMap<StringName, InputMap::Action> &InputMap::get_action_map() const { diff --git a/core/input/input_map.h b/core/input/input_map.h index e896d1f679..79b4d1038f 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -58,7 +58,7 @@ private: OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache; OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; Array _action_get_events(const StringName &p_action); Array _get_actions(); @@ -83,7 +83,7 @@ public: const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; const OrderedHashMap<StringName, Action> &get_action_map() const; void load_from_project_settings(); diff --git a/core/io/compression.cpp b/core/io/compression.cpp index d1f915f064..ae5ccf8354 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -212,7 +212,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s strm.avail_in = p_src_size; // Ensure the destination buffer is empty - p_dst_vect->resize(0); + p_dst_vect->clear(); // decompress until deflate stream ends or end of file do { @@ -244,7 +244,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s WARN_PRINT(strm.msg); } (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return ret; } } while (strm.avail_out > 0 && strm.avail_in > 0); @@ -254,7 +254,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s // Enforce max output size if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return Z_BUF_ERROR; } } while (ret != Z_STREAM_END); diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 3da9288ffd..86d8dea3d9 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -145,17 +145,21 @@ Error DirAccess::make_dir_recursive(String p_dir) { full_dir = full_dir.replace("\\", "/"); - //int slices = full_dir.get_slice_count("/"); - String base; if (full_dir.begins_with("res://")) { base = "res://"; } else if (full_dir.begins_with("user://")) { base = "user://"; + } else if (full_dir.is_network_share_path()) { + int pos = full_dir.find("/", 2); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + pos = full_dir.find("/", pos + 1); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + base = full_dir.substr(0, pos + 1); } else if (full_dir.begins_with("/")) { base = "/"; - } else if (full_dir.find(":/") != -1) { + } else if (full_dir.contains(":/")) { base = full_dir.substr(0, full_dir.find(":/") + 2); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index bb92648484..86836454c5 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -538,7 +538,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_ for (int i = 0; i < size; ++i) { String value = p_values[i]; - if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) { + if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) { value = "\"" + value.replace("\"", "\"\"") + "\""; } if (i < size - 1) { diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 3882627103..7dbea96c3d 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -70,7 +70,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 String p = p_path.replace_first("res://", ""); PackedDir *cd = root; - if (p.find("/") != -1) { //in a subdir + if (p.contains("/")) { //in a subdir Vector<String> ds = p.get_base_dir().split("/"); diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 4d0747c591..52b1120b2a 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -96,6 +96,17 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { return query.substr(1); } +Error HTTPClient::verify_headers(const Vector<String> &p_headers) { + for (int i = 0; i < p_headers.size(); i++) { + String sanitized = p_headers[i].strip_edges(); + ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty."); + ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER, + "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]); + } + + return OK; +} + Dictionary HTTPClient::_get_response_headers_as_dictionary() { List<String> rh; get_response_headers(&rh); diff --git a/core/io/http_client.h b/core/io/http_client.h index 8be6e6524c..de6045f647 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ public: static HTTPClient *create(); String query_string_from_dict(const Dictionary &p_dict); + Error verify_headers(const Vector<String> &p_headers); virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0; virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0; @@ -180,7 +181,7 @@ public: virtual bool is_response_chunked() const = 0; virtual int get_response_code() const = 0; virtual Error get_response_headers(List<String> *r_response) = 0; - virtual int get_response_body_length() const = 0; + virtual int64_t get_response_body_length() const = 0; virtual PackedByteArray read_response_body_chunk() = 0; // Can't get body as partial text because of most encodings UTF8, gzip, etc. diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 4c27cd1b10..f920799677 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -71,7 +71,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss connection = tcp_connection; if (ssl && https_proxy_port != -1) { - proxy_client.instantiate(); // Needs proxy negotiation + proxy_client.instantiate(); // Needs proxy negotiation. server_host = https_proxy_host; server_port = https_proxy_port; } else if (!ssl && http_proxy_port != -1) { @@ -83,7 +83,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss } if (server_host.is_valid_ip_address()) { - // Host contains valid IP + // Host contains valid IP. Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port); if (err) { status = STATUS_CANT_CONNECT; @@ -92,7 +92,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss status = STATUS_CONNECTING; } else { - // Host contains hostname and needs to be resolved to IP + // Host contains hostname and needs to be resolved to IP. resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host); status = STATUS_RESOLVING; } @@ -124,7 +124,7 @@ Ref<StreamPeer> HTTPClientTCP::get_connection() const { static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) { switch (p_method) { case HTTPClientTCP::METHOD_CONNECT: { - // Authority in host:port format, as in RFC7231 + // Authority in host:port format, as in RFC7231. int pos = p_url.find_char(':'); return 0 < pos && pos < p_url.length() - 1; } @@ -135,7 +135,7 @@ static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_u [[fallthrough]]; } default: - // Absolute path or absolute URL + // Absolute path or absolute URL. return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://"); } } @@ -146,6 +146,11 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); + Error err = verify_headers(p_headers); + if (err) { + return err; + } + String uri = p_url; if (!ssl && http_proxy_port != -1) { uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); @@ -173,7 +178,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } if (add_host) { if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { - // Don't append the standard ports + // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; @@ -192,21 +197,12 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< request += "\r\n"; CharString cs = request.utf8(); - Vector<uint8_t> data; - data.resize(cs.length() + p_body_size); - memcpy(data.ptrw(), cs.get_data(), cs.length()); + request_buffer->clear(); + request_buffer->put_data((const uint8_t *)cs.get_data(), cs.length()); if (p_body_size > 0) { - memcpy(data.ptrw() + cs.length(), p_body, p_body_size); - } - - // TODO Implement non-blocking requests. - Error err = connection->put_data(data.ptr(), data.size()); - - if (err) { - close(); - status = STATUS_CONNECTION_ERROR; - return err; + request_buffer->put_data(p_body, p_body_size); } + request_buffer->seek(0); status = STATUS_REQUESTING; head_request = p_method == METHOD_HEAD; @@ -257,6 +253,7 @@ void HTTPClientTCP::close() { ip_candidates.clear(); response_headers.clear(); response_str.clear(); + request_buffer->clear(); body_size = -1; body_left = 0; chunk_left = 0; @@ -274,7 +271,7 @@ Error HTTPClientTCP::poll() { IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); switch (rstatus) { case IP::RESOLVER_STATUS_WAITING: - return OK; // Still resolving + return OK; // Still resolving. case IP::RESOLVER_STATUS_DONE: { ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving); @@ -356,7 +353,7 @@ Error HTTPClientTCP::poll() { } else if (ssl) { Ref<StreamPeerSSL> ssl; if (!handshaking) { - // Connect the StreamPeerSSL and start handshaking + // Connect the StreamPeerSSL and start handshaking. ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl->set_blocking_handshake_enabled(false); Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); @@ -368,7 +365,7 @@ Error HTTPClientTCP::poll() { connection = ssl; handshaking = true; } else { - // We are already handshaking, which means we can use your already active SSL connection + // We are already handshaking, which means we can use your already active SSL connection. ssl = static_cast<Ref<StreamPeerSSL>>(connection); if (ssl.is_null()) { close(); @@ -376,22 +373,22 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } - ssl->poll(); // Try to finish the handshake + ssl->poll(); // Try to finish the handshake. } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successful + // Handshake has been successful. handshaking = false; ip_candidates.clear(); status = STATUS_CONNECTED; return OK; } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { - // Handshake has failed + // Handshake has failed. close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - // ... we will need to poll more for handshake to finish + // ... we will need to poll more for handshake to finish. } else { ip_candidates.clear(); status = STATUS_CONNECTED; @@ -416,7 +413,7 @@ Error HTTPClientTCP::poll() { } break; case STATUS_BODY: case STATUS_CONNECTED: { - // Check if we are still connected + // Check if we are still connected. if (ssl) { Ref<StreamPeerSSL> tmp = connection; tmp->poll(); @@ -428,10 +425,34 @@ Error HTTPClientTCP::poll() { status = STATUS_CONNECTION_ERROR; return ERR_CONNECTION_ERROR; } - // Connection established, requests can now be made + // Connection established, requests can now be made. return OK; } break; case STATUS_REQUESTING: { + if (request_buffer->get_available_bytes()) { + int avail = request_buffer->get_available_bytes(); + int pos = request_buffer->get_position(); + const Vector<uint8_t> data = request_buffer->get_data_array(); + int wrote = 0; + Error err; + if (blocking) { + err = connection->put_data(data.ptr() + pos, avail); + wrote += avail; + } else { + err = connection->put_partial_data(data.ptr() + pos, avail, wrote); + } + if (err != OK) { + close(); + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + pos += wrote; + request_buffer->seek(pos); + if (avail - wrote > 0) { + return OK; + } + request_buffer->clear(); + } while (true) { uint8_t byte; int rec = 0; @@ -534,7 +555,7 @@ Error HTTPClientTCP::poll() { return OK; } -int HTTPClientTCP::get_response_body_length() const { +int64_t HTTPClientTCP::get_response_body_length() const { return body_size; } @@ -547,7 +568,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { if (chunked) { while (true) { if (chunk_trailer_part) { - // We need to consume the trailer part too or keep-alive will break + // We need to consume the trailer part too or keep-alive will break. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -560,18 +581,18 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { int cs = chunk.size(); if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) { if (cs == 2) { - // Finally over + // Finally over. chunk_trailer_part = false; status = STATUS_CONNECTED; chunk.clear(); break; } else { - // We do not process nor return the trailer data + // We do not process nor return the trailer data. chunk.clear(); } } } else if (chunk_left == 0) { - // Reading length + // Reading length. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -593,7 +614,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { for (int i = 0; i < chunk.size() - 2; i++) { char c = chunk[i]; int v = 0; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a' + 10; @@ -658,7 +679,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { uint8_t *w = ret.ptrw(); err = _get_http_data(w + _offset, to_read, rec); } - if (rec <= 0) { // Ended up reading less + if (rec <= 0) { // Ended up reading less. ret.resize(_offset); break; } else { @@ -679,7 +700,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected + status = STATUS_DISCONNECTED; // Server disconnected. } else { status = STATUS_CONNECTION_ERROR; } @@ -759,6 +780,7 @@ void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { HTTPClientTCP::HTTPClientTCP() { tcp_connection.instantiate(); + request_buffer.instantiate(); } HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func; diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 886ad0ef48..c10e0b1eca 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -38,13 +38,13 @@ private: Status status = STATUS_DISCONNECTED; IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; Array ip_candidates; - int conn_port = -1; // Server to make requests to + int conn_port = -1; // Server to make requests to. String conn_host; - int server_port = -1; // Server to connect to (might be a proxy server) + int server_port = -1; // Server to connect to (might be a proxy server). String server_host; - int http_proxy_port = -1; // Proxy server for http requests + int http_proxy_port = -1; // Proxy server for http requests. String http_proxy_host; - int https_proxy_port = -1; // Proxy server for https requests + int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; bool ssl = false; bool ssl_verify_host = false; @@ -62,9 +62,10 @@ private: int64_t body_left = 0; bool read_until_eof = false; + Ref<StreamPeerBuffer> request_buffer; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; - Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server + Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server. int response_num = 0; Vector<String> response_headers; @@ -87,7 +88,7 @@ public: bool is_response_chunked() const override; int get_response_code() const override; Error get_response_headers(List<String> *r_response) override; - int get_response_body_length() const override; + int64_t get_response_body_length() const override; PackedByteArray read_response_body_chunk() override; void set_blocking_mode(bool p_enable) override; bool is_blocking_mode_enabled() const override; diff --git a/core/io/image.cpp b/core/io/image.cpp index 7956d0bad7..4f72599faf 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -1434,12 +1434,11 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & } // Set mipmap size. - // It might be necessary to put this after the minimum mipmap size check because of the possible occurrence of "1 >> 1". if (r_mm_width) { - *r_mm_width = bw >> 1; + *r_mm_width = w; } if (r_mm_height) { - *r_mm_height = bh >> 1; + *r_mm_height = h; } // Reach target mipmap. @@ -2057,7 +2056,7 @@ void Image::create(const char **p_xpm) { for (int i = 0; i < 6; i++) { char v = line_ptr[i]; - if (v >= '0' && v <= '9') { + if (is_digit(v)) { v -= '0'; } else if (v >= 'A' && v <= 'F') { v = (v - 'A') + 10; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 4970afc1d3..8e0d47e762 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -98,6 +98,11 @@ struct _IP_ResolverPrivate { if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) { continue; } + // We might be overriding another result, but we don't care as long as the result is valid. + if (response.size()) { + String key = get_cache_key(hostname, type); + cache[key] = response; + } queue[i].response = response; queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); } @@ -120,30 +125,8 @@ struct _IP_ResolverPrivate { }; IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - List<IPAddress> res; - String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - - resolver->mutex.lock(); - if (resolver->cache.has(key)) { - res = resolver->cache[key]; - } else { - // This should be run unlocked so the resolver thread can keep - // resolving other requests. - resolver->mutex.unlock(); - _resolve_hostname(res, p_hostname, p_type); - resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; - } - resolver->mutex.unlock(); - - for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - return res[i]; - } - } - return IPAddress(); + const Array addresses = resolve_hostname_addresses(p_hostname, p_type); + return addresses.size() ? addresses[0].operator IPAddress() : IPAddress(); } Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { @@ -159,17 +142,16 @@ Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { resolver->mutex.unlock(); _resolve_hostname(res, p_hostname, p_type); resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; + // We might be overriding another result, but we don't care as long as the result is valid. + if (res.size()) { + resolver->cache[key] = res; + } } resolver->mutex.unlock(); Array result; for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - result.push_back(String(res[i])); - } + result.push_back(String(res[i])); } return result; } diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 38f99a08a4..d183c60798 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { int n = 0; char32_t c = p_string[i]; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = 10 + (c - 'a'); @@ -113,7 +113,7 @@ void IPAddress::_parse_ipv6(const String &p_string) { } else if (c == '.') { part_ipv4 = true; - } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + } else if (is_hex_digit(c)) { if (!part_found) { parts[parts_idx++] = i; part_found = true; diff --git a/core/io/json.cpp b/core/io/json.cpp index 7b642f6a59..4b745dff44 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -229,12 +229,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -265,12 +265,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -326,7 +326,7 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to break; } - if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { + if (p_str[index] == '-' || is_digit(p_str[index])) { //a number const char32_t *rptr; double number = String::to_float(&p_str[index], &rptr); @@ -335,10 +335,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_token.value = number; return OK; - } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + } else if (is_ascii_char(p_str[index])) { String id; - while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + while (is_ascii_char(p_str[index])) { id += p_str[index]; index++; } diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 6a0668f027..555d4f6df4 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -52,9 +52,8 @@ ObjectID EncodedObjectAsID::get_object_id() const { return id; } -#define _S(a) ((int32_t)a) -#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) -#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err) #define ENCODE_MASK 0xFF #define ENCODE_FLAG_64 1 << 16 diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index e90d1695e5..0af236f766 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -39,7 +39,7 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); - encode_buffer.resize(0); + encode_buffer.clear(); } int PacketPeer::get_encode_buffer_max_size() const { diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 221a680130..272ace3438 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -62,7 +62,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & int v = 0; if (i * 2 < _key.length()) { char32_t ct = _key[i * 2]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; @@ -72,7 +72,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & if (i * 2 + 1 < _key.length()) { char32_t ct = _key[i * 2 + 1]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; diff --git a/core/io/resource.h b/core/io/resource.h index a0800c57cb..b1e1c15541 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -37,6 +37,8 @@ #include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" +class Node; + #define RES_BASE_EXTENSION(m_ext) \ public: \ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \ @@ -103,7 +105,7 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false); String get_path() const; - _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.find("::") != -1 || path_cache.begins_with("local://"); } + _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } static String generate_scene_unique_id(); void set_scene_unique_id(const String &p_id); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 200d5fafde..8588bab0be 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -335,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { String exttype = get_unicode_string(); String path = get_unicode_string(); - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); } @@ -626,7 +626,7 @@ Error ResourceLoaderBinary::load() { path = remaps[path]; } - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path)); } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 7d42a9c8d2..21bf566b1b 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -820,7 +820,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } String l = res_remaps[i].substr(split + 1).strip_edges(); int score = TranslationServer::get_singleton()->compare_locales(locale, l); - if (score >= best_score) { + if (score > 0 && score >= best_score) { new_path = res_remaps[i].left(split); best_score = score; if (score == 10) { diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 1a16d5b47a..776756e64e 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -71,9 +71,9 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const { for (uint32_t i = 6; i < l; i++) { uid *= base; uint32_t c = p_text[i]; - if (c >= 'a' && c <= 'z') { + if (is_ascii_lower_case(c)) { uid += c - 'a'; - } else if (c >= '0' && c <= '9') { + } else if (is_digit(c)) { uid += c - '0' + char_count; } else { return INVALID_ID; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 28ebe811c9..c65968ef03 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -98,7 +98,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) { Error err = get_partial_data(&w[0], p_bytes, received); if (err != OK) { - data.resize(0); + data.clear(); } else if (received != data.size()) { data.resize(received); } @@ -563,7 +563,7 @@ Vector<uint8_t> StreamPeerBuffer::get_data_array() const { } void StreamPeerBuffer::clear() { - data.resize(0); + data.clear(); pointer = 0; } diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 2f6742bd7a..8d3e58cad1 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -179,7 +179,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { } if (l.is_empty() || l.begins_with("#")) { - if (l.find("fuzzy") != -1) { + if (l.contains("fuzzy")) { skip_next = true; } line++; diff --git a/core/math/aabb.h b/core/math/aabb.h index 2eaaafa27d..3d19410ddf 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -41,7 +41,7 @@ */ class Variant; -class AABB { +class _NO_DISCARD_ AABB { public: Vector3 position; Vector3 size; diff --git a/core/math/basis.h b/core/math/basis.h index 709f2cb3cf..802da82089 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -34,7 +34,7 @@ #include "core/math/quaternion.h" #include "core/math/vector3.h" -class Basis { +class _NO_DISCARD_ Basis { private: void _set_diagonal(const Vector3 &p_diag); diff --git a/core/math/color.h b/core/math/color.h index 6c09f7941c..72a4a5f8be 100644 --- a/core/math/color.h +++ b/core/math/color.h @@ -34,7 +34,7 @@ #include "core/math/math_funcs.h" #include "core/string/ustring.h" -struct Color { +struct _NO_DISCARD_ Color { union { struct { float r; diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 912ffb8b16..bd292f4c2a 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2129,7 +2129,7 @@ bool ConvexHullInternal::shift_face(Face *p_face, real_t p_amount, LocalVector<V printf("Needed %d iterations to remove part\n", n); #endif - p_stack.resize(0); + p_stack.clear(); p_face->origin = shifted_origin; return true; @@ -2167,9 +2167,9 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea return shift; } - vertices.resize(0); - edges.resize(0); - faces.resize(0); + vertices.clear(); + edges.clear(); + faces.clear(); LocalVector<ConvexHullInternal::Vertex *> old_vertices; get_vertex_copy(hull.vertex_list, old_vertices); diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 4f8e79038f..b447d09887 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -37,10 +37,6 @@ #include "core/os/os.h" #include "core/variant/variant_parser.h" -static bool _is_number(char32_t c) { - return (c >= '0' && c <= '9'); -} - Error Expression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) @@ -88,7 +84,7 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_INPUT; int index = 0; do { - if (!_is_number(expression[str_ofs])) { + if (!is_digit(expression[str_ofs])) { _set_error("Expected number after '$'"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; @@ -97,7 +93,7 @@ Error Expression::_get_token(Token &r_token) { index += expression[str_ofs] - '0'; str_ofs++; - } while (_is_number(expression[str_ofs])); + } while (is_digit(expression[str_ofs])); r_token.value = index; return OK; @@ -197,6 +193,7 @@ Error Expression::_get_token(Token &r_token) { case '\'': case '"': { String str; + char32_t prev = 0; while (true) { char32_t ch = GET_CHAR(); @@ -234,9 +231,11 @@ Error Expression::_get_token(Token &r_token) { case 'r': res = 13; break; + case 'U': case 'u': { - // hex number - for (int j = 0; j < 4; j++) { + // Hexadecimal sequence. + int hex_len = (next == 'U') ? 6 : 4; + for (int j = 0; j < hex_len; j++) { char32_t c = GET_CHAR(); if (c == 0) { @@ -244,13 +243,13 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { _set_error("Malformed hex constant in string"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } char32_t v; - if (_is_number(c)) { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -273,12 +272,46 @@ Error Expression::_get_token(Token &r_token) { } break; } + // Parse UTF-16 pair. + if ((res & 0xfffffc00) == 0xd800) { + if (prev == 0) { + prev = res; + continue; + } else { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } else if ((res & 0xfffffc00) == 0xdc00) { + if (prev == 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else { + res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev = 0; + } + } + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += res; - } else { + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += ch; } } + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } r_token.type = TK_CONSTANT; r_token.value = str; @@ -291,27 +324,38 @@ Error Expression::_get_token(Token &r_token) { } char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; - if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { + if (is_digit(cchar) || (cchar == '.' && is_digit(next_char))) { //a number String num; #define READING_SIGN 0 #define READING_INT 1 -#define READING_DEC 2 -#define READING_EXP 3 -#define READING_DONE 4 +#define READING_HEX 2 +#define READING_BIN 3 +#define READING_DEC 4 +#define READING_EXP 5 +#define READING_DONE 6 int reading = READING_INT; char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; + bool bin_beg = false; + bool hex_beg = false; bool is_float = false; + bool is_first_char = true; while (true) { switch (reading) { case READING_INT: { - if (_is_number(c)) { - //pass + if (is_digit(c)) { + if (is_first_char && c == '0') { + if (next_char == 'b') { + reading = READING_BIN; + } else if (next_char == 'x') { + reading = READING_HEX; + } + } } else if (c == '.') { reading = READING_DEC; is_float = true; @@ -322,8 +366,24 @@ Error Expression::_get_token(Token &r_token) { } } break; + case READING_BIN: { + if (bin_beg && !is_binary_digit(c)) { + reading = READING_DONE; + } else if (c == 'b') { + bin_beg = true; + } + + } break; + case READING_HEX: { + if (hex_beg && !is_hex_digit(c)) { + reading = READING_DONE; + } else if (c == 'x') { + hex_beg = true; + } + + } break; case READING_DEC: { - if (_is_number(c)) { + if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; @@ -333,7 +393,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_EXP: { - if (_is_number(c)) { + if (is_digit(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { @@ -353,6 +413,7 @@ Error Expression::_get_token(Token &r_token) { } num += String::chr(c); c = GET_CHAR(); + is_first_char = false; } str_ofs--; @@ -361,16 +422,20 @@ Error Expression::_get_token(Token &r_token) { if (is_float) { r_token.value = num.to_float(); + } else if (bin_beg) { + r_token.value = num.bin_to_int(); + } else if (hex_beg) { + r_token.value = num.hex_to_int(); } else { r_token.value = num.to_int(); } return OK; - } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + } else if (is_ascii_char(cchar) || is_underscore(cchar)) { String id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { + while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { id += String::chr(cchar); cchar = GET_CHAR(); first = false; diff --git a/core/math/face3.cpp b/core/math/face3.cpp index ba10b50465..d588f34e5d 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -260,8 +260,8 @@ void Face3::project_range(const Vector3 &p_normal, const Transform3D &p_transfor } void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const { -#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98 -#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05 + constexpr double face_support_threshold = 0.98; + constexpr double edge_support_threshold = 0.05; if (p_max <= 0) { return; @@ -270,7 +270,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 n = p_transform.basis.xform_inv(p_normal); /** TEST FACE AS SUPPORT **/ - if (get_plane().normal.dot(n) > _FACE_IS_VALID_SUPPORT_THRESHOLD) { + if (get_plane().normal.dot(n) > face_support_threshold) { *p_count = MIN(3, p_max); for (int i = 0; i < *p_count; i++) { @@ -304,7 +304,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, // check if edge is valid as a support real_t dot = (vertex[i] - vertex[(i + 1) % 3]).normalized().dot(n); dot = ABS(dot); - if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD) { + if (dot < edge_support_threshold) { *p_count = MIN(2, p_max); for (int j = 0; j < *p_count; j++) { diff --git a/core/math/face3.h b/core/math/face3.h index 5a34858ccb..3dbbca09e0 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -36,7 +36,7 @@ #include "core/math/transform_3d.h" #include "core/math/vector3.h" -class Face3 { +class _NO_DISCARD_ Face3 { public: enum Side { SIDE_OVER, diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 98a2c27d93..a9ff46410e 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -281,16 +281,16 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int div_y = len_y > 1 ? 2 : 1; int div_z = len_z > 1 ? 2 : 1; -#define _SPLIT(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \ - if (m_div == 1) { \ - m_new_v = m_v; \ - m_new_len_v = 1; \ - } else if (m_i == 0) { \ - m_new_v = m_v; \ - m_new_len_v = m_len_v / 2; \ - } else { \ - m_new_v = m_v + m_len_v / 2; \ - m_new_len_v = m_len_v - m_len_v / 2; \ +#define SPLIT_DIV(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \ + if (m_div == 1) { \ + m_new_v = m_v; \ + m_new_len_v = 1; \ + } else if (m_i == 0) { \ + m_new_v = m_v; \ + m_new_len_v = m_len_v / 2; \ + } else { \ + m_new_v = m_v + m_len_v / 2; \ + m_new_len_v = m_len_v - m_len_v / 2; \ } int new_x; @@ -301,18 +301,20 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int new_len_z; for (int i = 0; i < div_x; i++) { - _SPLIT(i, div_x, x, len_x, new_x, new_len_x); + SPLIT_DIV(i, div_x, x, len_x, new_x, new_len_x); for (int j = 0; j < div_y; j++) { - _SPLIT(j, div_y, y, len_y, new_y, new_len_y); + SPLIT_DIV(j, div_y, y, len_y, new_y, new_len_y); for (int k = 0; k < div_z; k++) { - _SPLIT(k, div_z, z, len_z, new_z, new_len_z); + SPLIT_DIV(k, div_z, z, len_z, new_z, new_len_z); _plot_face(p_cell_status, new_x, new_y, new_z, new_len_x, new_len_y, new_len_z, voxelsize, p_face); } } } + +#undef SPLIT_DIV } static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) { @@ -491,11 +493,10 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i } Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { -#define _MIN_SIZE 1.0 -#define _MAX_LENGTH 20 - int face_count = p_array.size(); const Face3 *faces = p_array.ptr(); + constexpr double min_size = 1.0; + constexpr int max_length = 20; AABB global_aabb; @@ -512,22 +513,22 @@ Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) // Determine amount of cells in grid axis. int div_x, div_y, div_z; - if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) { - div_x = (int)(global_aabb.size.x / _MIN_SIZE) + 1; + if (global_aabb.size.x / min_size < max_length) { + div_x = (int)(global_aabb.size.x / min_size) + 1; } else { - div_x = _MAX_LENGTH; + div_x = max_length; } - if (global_aabb.size.y / _MIN_SIZE < _MAX_LENGTH) { - div_y = (int)(global_aabb.size.y / _MIN_SIZE) + 1; + if (global_aabb.size.y / min_size < max_length) { + div_y = (int)(global_aabb.size.y / min_size) + 1; } else { - div_y = _MAX_LENGTH; + div_y = max_length; } - if (global_aabb.size.z / _MIN_SIZE < _MAX_LENGTH) { - div_z = (int)(global_aabb.size.z / _MIN_SIZE) + 1; + if (global_aabb.size.z / min_size < max_length) { + div_z = (int)(global_aabb.size.z / min_size) + 1; } else { - div_z = _MAX_LENGTH; + div_z = max_length; } Vector3 voxelsize = global_aabb.size; diff --git a/core/math/plane.h b/core/math/plane.h index bac946502b..8cb6f62b3b 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -35,7 +35,7 @@ class Variant; -class Plane { +class _NO_DISCARD_ Plane { public: Vector3 normal; real_t d = 0; diff --git a/core/math/quaternion.h b/core/math/quaternion.h index cf3762e090..2575d7d229 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -36,7 +36,7 @@ #include "core/math/vector3.h" #include "core/string/ustring.h" -class Quaternion { +class _NO_DISCARD_ Quaternion { public: union { struct { diff --git a/core/math/rect2.h b/core/math/rect2.h index f34550bef1..b14c69302c 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -35,7 +35,7 @@ struct Transform2D; -struct Rect2 { +struct _NO_DISCARD_ Rect2 { Point2 position; Size2 size; @@ -363,7 +363,7 @@ struct Rect2 { } }; -struct Rect2i { +struct _NO_DISCARD_ Rect2i { Point2i position; Size2i size; @@ -382,16 +382,16 @@ struct Rect2i { ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); } #endif - if (position.x > (p_rect.position.x + p_rect.size.width)) { + if (position.x >= (p_rect.position.x + p_rect.size.width)) { return false; } - if ((position.x + size.width) < p_rect.position.x) { + if ((position.x + size.width) <= p_rect.position.x) { return false; } - if (position.y > (p_rect.position.y + p_rect.size.height)) { + if (position.y >= (p_rect.position.y + p_rect.size.height)) { return false; } - if ((position.y + size.height) < p_rect.position.y) { + if ((position.y + size.height) <= p_rect.position.y) { return false; } @@ -405,8 +405,8 @@ struct Rect2i { } #endif return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); } _FORCE_INLINE_ bool has_no_area() const { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 752a885eba..6c2d51bd9b 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -33,7 +33,7 @@ #include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring -struct Transform2D { +struct _NO_DISCARD_ Transform2D { // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": // M = (elements[0][0] elements[1][0]) // (elements[0][1] elements[1][1]) diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index c0ef2ecfc1..c16c278e74 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -35,7 +35,7 @@ #include "core/math/basis.h" #include "core/math/plane.h" -class Transform3D { +class _NO_DISCARD_ Transform3D { public: Basis basis; Vector3 origin; diff --git a/core/math/vector2.h b/core/math/vector2.h index a340036ac7..af40b9e68d 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -36,7 +36,7 @@ struct Vector2i; -struct Vector2 { +struct _NO_DISCARD_ Vector2 { static const int AXIS_COUNT = 2; enum Axis { @@ -284,7 +284,7 @@ typedef Vector2 Point2; /* INTEGER STUFF */ -struct Vector2i { +struct _NO_DISCARD_ Vector2i { enum Axis { AXIS_X, AXIS_Y, diff --git a/core/math/vector3.h b/core/math/vector3.h index d7a72b05a8..b62edef40f 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -37,7 +37,7 @@ #include "core/string/ustring.h" class Basis; -struct Vector3 { +struct _NO_DISCARD_ Vector3 { static const int AXIS_COUNT = 3; enum Axis { diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 1416c98057..1564ee9173 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -35,7 +35,7 @@ #include "core/string/ustring.h" #include "core/typedefs.h" -struct Vector3i { +struct _NO_DISCARD_ Vector3i { enum Axis { AXIS_X, AXIS_Y, diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 03774bc36a..72a98ca20c 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -732,7 +732,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName String enum_name = p_enum; if (!enum_name.is_empty()) { - if (enum_name.find(".") != -1) { + if (enum_name.contains(".")) { enum_name = enum_name.get_slicec('.', 1); } diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index e961745d96..5de1b49026 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -123,9 +123,9 @@ def generate_version(argcount, const=False, returns=False): callargtext += "," callargtext += " m_ret& r_ret" s = s.replace("$CALLSIBEGIN", "Variant ret = ") - s = s.replace("$CALLSIRET", "r_ret = ret;") + s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);") s = s.replace("$CALLPTRRETPASS", "&ret") - s = s.replace("$CALLPTRRET", "r_ret = ret;") + s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;") else: s = s.replace("$CALLSIBEGIN", "") s = s.replace("$CALLSIRET", "") diff --git a/core/object/object.h b/core/object/object.h index 63130a1aef..1a0a81581d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -700,8 +700,9 @@ public: static String get_category_static() { return String(); } virtual String get_class() const { - if (_extension) + if (_extension) { return _extension->class_name.operator String(); + } return "Object"; } virtual String get_save_class() const { return get_class(); } //class stored when saving diff --git a/core/string/char_utils.h b/core/string/char_utils.h new file mode 100644 index 0000000000..0afd058f01 --- /dev/null +++ b/core/string/char_utils.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* char_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 CHAR_UTILS_H +#define CHAR_UTILS_H + +#include "core/typedefs.h" + +static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +static _FORCE_INLINE_ bool is_digit(char32_t c) { + return (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { + return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { + return (c == '0' || c == '1'); +} + +static _FORCE_INLINE_ bool is_ascii_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static _FORCE_INLINE_ bool is_symbol(char32_t c) { + return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); +} + +static _FORCE_INLINE_ bool is_control(char32_t p_char) { + return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f); +} + +static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) { + return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085); +} + +static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { + return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); +} + +static _FORCE_INLINE_ bool is_punct(char32_t p_char) { + return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f); +} + +static _FORCE_INLINE_ bool is_underscore(char32_t p_char) { + return (p_char == '_'); +} + +#endif // CHAR_UTILS_H diff --git a/core/string/locales.h b/core/string/locales.h index 9ef6dee5ac..32d6608ec2 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -42,6 +42,7 @@ static const char *locale_renames[][2] = { { "in", "id" }, // Indonesian { "iw", "he" }, // Hebrew { "no", "nb" }, // Norwegian Bokmål + { "C", "en" }, // Locale is not set, fallback to English. { nullptr, nullptr } }; diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 5d7e583de1..7cc41df9ef 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -213,14 +213,6 @@ static _character_accent_pair _character_to_accented[] = { { 'z', U"ź" }, }; -static _FORCE_INLINE_ bool is_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); -} - -static _FORCE_INLINE_ bool is_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); -} - Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; Map<String, String> TranslationServer::language_map; @@ -309,15 +301,15 @@ String TranslationServer::standardize_locale(const String &p_locale) const { Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); lang = locale_elements[0]; if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { script = locale_elements[1]; } - if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { country = locale_elements[1]; } } if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { country = locale_elements[2]; } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) { variant = locale_elements[2].to_lower(); @@ -434,15 +426,15 @@ String TranslationServer::get_locale_name(const String &p_locale) const { Vector<String> locale_elements = locale.split("_"); lang = locale_elements[0]; if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { script = locale_elements[1]; } - if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { country = locale_elements[1]; } } if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { country = locale_elements[2]; } } @@ -544,7 +536,7 @@ Ref<Translation> TranslationServer::get_translation_object(const String &p_local String l = t->get_locale(); int score = compare_locales(p_locale, l); - if (score >= best_score) { + if (score > 0 && score >= best_score) { res = t; best_score = score; if (score == 10) { @@ -566,8 +558,6 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin return p_message; } - ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); - StringName res = _get_message_from_translations(p_message, p_context, locale, false); if (!res && fallback.length() >= 2) { @@ -589,8 +579,6 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons return p_message_plural; } - ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); - StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); if (!res && fallback.length() >= 2) { @@ -617,13 +605,17 @@ StringName TranslationServer::_get_message_from_translations(const StringName &p String l = t->get_locale(); int score = compare_locales(p_locale, l); - if (score >= best_score) { + if (score > 0 && score >= best_score) { StringName r; if (!plural) { - res = t->get_message(p_message, p_context); + r = t->get_message(p_message, p_context); } else { - res = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + } + if (!r) { + continue; } + res = r; best_score = score; if (score == 10) { break; // Exact match, skip the rest. @@ -911,7 +903,7 @@ String TranslationServer::add_padding(String &p_message, int p_length) const { } const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { - if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) { + if (!is_ascii_char(p_character)) { return nullptr; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 658dbc98cf..c4edc8c086 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -54,34 +54,14 @@ static const int MAX_DECIMALS = 32; -static _FORCE_INLINE_ bool is_digit(char32_t c) { - return (c >= '0' && c <= '9'); -} - -static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { - return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); -} - -static _FORCE_INLINE_ bool is_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); -} - -static _FORCE_INLINE_ bool is_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); -} - static _FORCE_INLINE_ char32_t lower_case(char32_t c) { - return (is_upper_case(c) ? (c + ('a' - 'A')) : c); + return (is_ascii_upper_case(c) ? (c + ('a' - 'A')) : c); } const char CharString::_null = 0; const char16_t Char16String::_null = 0; const char32_t String::_null = 0; -bool is_symbol(char32_t c) { - return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); -} - bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { const String &s = p_s; int beg = CLAMP(p_col, 0, s.length()); @@ -974,21 +954,21 @@ String String::camelcase_to_underscore(bool lowercase) const { int start_index = 0; for (int i = 1; i < this->size(); i++) { - bool is_upper = is_upper_case(cstr[i]); + bool is_upper = is_ascii_upper_case(cstr[i]); bool is_number = is_digit(cstr[i]); bool are_next_2_lower = false; bool is_next_lower = false; bool is_next_number = false; - bool was_precedent_upper = is_upper_case(cstr[i - 1]); + bool was_precedent_upper = is_ascii_upper_case(cstr[i - 1]); bool was_precedent_number = is_digit(cstr[i - 1]); if (i + 2 < this->size()) { - are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]); + are_next_2_lower = is_ascii_lower_case(cstr[i + 1]) && is_ascii_lower_case(cstr[i + 2]); } if (i + 1 < this->size()) { - is_next_lower = is_lower_case(cstr[i + 1]); + is_next_lower = is_ascii_lower_case(cstr[i + 1]); is_next_number = is_digit(cstr[i + 1]); } @@ -1632,7 +1612,7 @@ String String::utf8(const char *p_utf8, int p_len) { } bool String::parse_utf8(const char *p_utf8, int p_len) { -#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); +#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); if (!p_utf8) { return true; @@ -1673,12 +1653,12 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } else if ((c & 0xf8) == 0xf0) { skip = 3; } else { - _UNICERROR("invalid skip at " + num_int64(cstr_size)); + UNICERROR("invalid skip at " + num_int64(cstr_size)); return true; //invalid utf8 } if (skip == 1 && (c & 0x1e) == 0) { - _UNICERROR("overlong rejected at " + num_int64(cstr_size)); + UNICERROR("overlong rejected at " + num_int64(cstr_size)); return true; //reject overlong } @@ -1693,7 +1673,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } if (skip) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } } @@ -1720,17 +1700,17 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } else if ((*p_utf8 & 0xf8) == 0xf0) { len = 4; } else { - _UNICERROR("invalid len"); + UNICERROR("invalid len"); return true; //invalid UTF8 } if (len > cstr_size) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } if (len == 2 && (*p_utf8 & 0x1E) == 0) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //reject overlong } @@ -1745,18 +1725,18 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { for (int i = 1; i < len; i++) { if ((p_utf8[i] & 0xc0) != 0x80) { - _UNICERROR("invalid utf8"); + UNICERROR("invalid utf8"); return true; //invalid utf8 } if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7f) >> (7 - len)) == 0) { - _UNICERROR("invalid utf8 overlong"); + UNICERROR("invalid utf8 overlong"); return true; //no overlong } unichar = (unichar << 6) | (p_utf8[i] & 0x3f); } } if (unichar >= 0xd800 && unichar <= 0xdfff) { - _UNICERROR("invalid code point"); + UNICERROR("invalid code point"); return CharString(); } @@ -1766,7 +1746,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } return false; -#undef _UNICERROR +#undef UNICERROR } CharString String::utf8() const { @@ -1840,7 +1820,7 @@ String String::utf16(const char16_t *p_utf16, int p_len) { } bool String::parse_utf16(const char16_t *p_utf16, int p_len) { -#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); +#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); if (!p_utf16) { return true; @@ -1880,7 +1860,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { if ((c & 0xfffffc00) == 0xd800) { skip = 1; // lead surrogate } else if ((c & 0xfffffc00) == 0xdc00) { - _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); return true; // invalid UTF16 } else { skip = 0; @@ -1890,7 +1870,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate --skip; } else { - _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); return true; // invalid UTF16 } } @@ -1900,7 +1880,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } if (skip) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; // not enough space } } @@ -1925,7 +1905,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } if (len > cstr_size) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } @@ -1943,7 +1923,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } return false; -#undef _UNICERROR +#undef UNICERROR } Char16String String::utf16() const { @@ -2212,7 +2192,7 @@ bool String::is_numeric() const { return false; } dot = true; - } else if (c < '0' || c > '9') { + } else if (!is_digit(c)) { return false; } } @@ -3080,7 +3060,7 @@ bool String::is_subsequence_of(const String &p_string) const { return _base_is_subsequence_of(p_string, false); } -bool String::is_subsequence_ofi(const String &p_string) const { +bool String::is_subsequence_ofn(const String &p_string) const { return _base_is_subsequence_of(p_string, true); } @@ -3558,6 +3538,10 @@ String String::rstrip(const String &p_chars) const { return substr(0, end + 1); } +bool String::is_network_share_path() const { + return begins_with("//") || begins_with("\\\\"); +} + String String::simplify_path() const { String s = *this; String drive; @@ -3570,6 +3554,9 @@ String String::simplify_path() const { } else if (s.begins_with("user://")) { drive = "user://"; s = s.substr(7, s.length()); + } else if (is_network_share_path()) { + drive = s.substr(0, 2); + s = s.substr(2, s.length() - 2); } else if (s.begins_with("/") || s.begins_with("\\")) { drive = s.substr(0, 1); s = s.substr(1, s.length() - 1); @@ -3684,7 +3671,7 @@ bool String::is_valid_identifier() const { } } - bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_'; + bool valid_char = is_ascii_identifier_char(str[i]); if (!valid_char) { return false; @@ -3709,7 +3696,7 @@ String String::uri_encode() const { String res; for (int i = 0; i < temp.length(); ++i) { char ord = temp[i]; - if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) { + if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { res += ord; } else { char h_Val[3]; @@ -3731,9 +3718,9 @@ String String::uri_decode() const { for (int i = 0; i < src.length(); ++i) { if (src[i] == '%' && i + 2 < src.length()) { char ord1 = src[i + 1]; - if (is_digit(ord1) || is_upper_case(ord1)) { + if (is_digit(ord1) || is_ascii_upper_case(ord1)) { char ord2 = src[i + 2]; - if (is_digit(ord2) || is_upper_case(ord2)) { + if (is_digit(ord2) || is_ascii_upper_case(ord2)) { char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, nullptr, 16); i += 2; @@ -3860,7 +3847,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch for (int i = 2; i < p_src_len; i++) { eat = i + 1; char32_t ct = p_src[i]; - if (ct == ';' || ct < '0' || ct > '9') { + if (ct == ';' || !is_digit(ct)) { break; } } @@ -3990,7 +3977,7 @@ String String::pad_zeros(int p_digits) const { int begin = 0; - while (begin < end && (s[begin] < '0' || s[begin] > '9')) { + while (begin < end && !is_digit(s[begin])) { begin++; } @@ -4035,7 +4022,7 @@ bool String::is_valid_int() const { } for (int i = from; i < len; i++) { - if (operator[](i) < '0' || operator[](i) > '9') { + if (!is_digit(operator[](i))) { return false; // no start with number plz } } @@ -4271,13 +4258,13 @@ bool String::is_relative_path() const { String String::get_base_dir() const { int end = 0; - // url scheme style base + // URL scheme style base. int basepos = find("://"); if (basepos != -1) { end = basepos + 3; } - // windows top level directory base + // Windows top level directory base. if (end == 0) { basepos = find(":/"); if (basepos == -1) { @@ -4288,7 +4275,24 @@ String String::get_base_dir() const { } } - // unix root directory base + // Windows UNC network share path. + if (end == 0) { + if (is_network_share_path()) { + basepos = find("/", 2); + if (basepos == -1) { + basepos = find("\\", 2); + } + int servpos = find("/", basepos + 1); + if (servpos == -1) { + servpos = find("\\", basepos + 1); + } + if (servpos != -1) { + end = servpos + 1; + } + } + } + + // Unix root directory base. if (end == 0) { if (begins_with("/")) { end = 1; diff --git a/core/string/ustring.h b/core/string/ustring.h index 4840c236c0..1d302b65a7 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -32,6 +32,7 @@ #define USTRING_GODOT_H // Note: Renamed to avoid conflict with ICU header with the same name. +#include "core/string/char_utils.h" #include "core/templates/cowdata.h" #include "core/templates/vector.h" #include "core/typedefs.h" @@ -285,7 +286,7 @@ public: bool ends_with(const String &p_string) const; bool is_enclosed_in(const String &p_string) const; bool is_subsequence_of(const String &p_string) const; - bool is_subsequence_ofi(const String &p_string) const; + bool is_subsequence_ofn(const String &p_string) const; bool is_quoted() const; Vector<String> bigrams() const; float similarity(const String &p_string) const; @@ -394,6 +395,8 @@ public: Vector<uint8_t> sha256_buffer() const; _FORCE_INLINE_ bool is_empty() const { return length() == 0; } + _FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; } + _FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; } // path functions bool is_absolute_path() const; @@ -405,6 +408,7 @@ public: String get_file() const; static String humanize_size(uint64_t p_size); String simplify_path() const; + bool is_network_share_path() const; String xml_escape(bool p_escape_quotes = false) const; String xml_unescape() const; @@ -530,7 +534,6 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St String RTR(const String &p_text, const String &p_context = ""); String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); -bool is_symbol(char32_t c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) { diff --git a/core/templates/vector.h b/core/templates/vector.h index bd4c6ade86..0877e04e01 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -95,9 +95,7 @@ public: void append_array(Vector<T> p_other); - bool has(const T &p_val) const { - return find(p_val, 0) != -1; - } + _FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; } template <class C> void sort_custom() { diff --git a/core/typedefs.h b/core/typedefs.h index e6034eb375..5929b5123b 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -71,6 +71,17 @@ #endif #endif +// No discard allows the compiler to flag warnings if we don't use the return value of functions / classes +#ifndef _NO_DISCARD_ +#define _NO_DISCARD_ [[nodiscard]] +#endif + +// In some cases _NO_DISCARD_ will get false positives, +// we can prevent the warning in specific cases by preceding the call with a cast. +#ifndef _ALLOW_DISCARD_ +#define _ALLOW_DISCARD_ (void) +#endif + // Windows badly defines a lot of stuff we'll never use. Undefine it. #ifdef _WIN32 #undef min // override standard definition diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 3d2f337442..1b39558dff 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -631,7 +631,7 @@ Variant Array::max() const { } const void *Array::id() const { - return _p->array.ptr(); + return _p; } Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_name, const Variant &p_script) { diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 14f49d530c..b6fdb4d902 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -44,24 +44,42 @@ #include <stdio.h> +// Variant cannot define an implicit cast operator for every Object subclass, so the +// casting is done here, to allow binding methods with parameters more specific than Object * + template <class T> struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; template <class T> struct VariantCaster<T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; template <class T> struct VariantCaster<const T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; @@ -135,7 +153,13 @@ struct PtrToArg<char32_t> { template <typename T> struct VariantObjectClassChecker { static _FORCE_INLINE_ bool check(const Variant &p_variant) { - return true; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + Object *obj = p_variant; + return Object::cast_to<TStripped>(p_variant) || !obj; + } else { + return true; + } } }; @@ -151,24 +175,6 @@ struct VariantObjectClassChecker<const Ref<T> &> { } }; -template <> -struct VariantObjectClassChecker<Node *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Node *node = p_variant; - return node || !obj; - } -}; - -template <> -struct VariantObjectClassChecker<Control *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Control *control = p_variant; - return control || !obj; - } -}; - #ifdef DEBUG_METHODS_ENABLED template <class T> diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index cc04ae712b..0f2f8fc8ed 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -350,7 +350,7 @@ void Dictionary::operator=(const Dictionary &p_dictionary) { } const void *Dictionary::id() const { - return _p->variant_map.id(); + return _p; } Dictionary::Dictionary(const Dictionary &p_from) { diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index fe541c8d4b..8e9fbbc0a4 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -53,22 +53,36 @@ struct GDNativePtr { operator Variant() const { return uint64_t(data); } }; -#define GDVIRTUAL_NATIVE_PTR(m_type) \ - template <> \ - struct GDNativeConstPtr<const m_type> { \ - const m_type *data = nullptr; \ - GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ - static const char *get_name() { return "const " #m_type; } \ - operator const m_type *() const { return data; } \ - operator Variant() const { return uint64_t(data); } \ - }; \ - template <> \ - struct GDNativePtr<m_type> { \ - m_type *data = nullptr; \ - GDNativePtr(m_type *p_assign) { data = p_assign; } \ - static const char *get_name() { return #m_type; } \ - operator m_type *() const { return data; } \ - operator Variant() const { return uint64_t(data); } \ +#define GDVIRTUAL_NATIVE_PTR(m_type) \ + template <> \ + struct GDNativeConstPtr<const m_type> { \ + const m_type *data = nullptr; \ + GDNativeConstPtr() {} \ + GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return "const " #m_type; } \ + operator const m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; \ + template <> \ + struct VariantCaster<GDNativeConstPtr<const m_type>> { \ + static _FORCE_INLINE_ GDNativeConstPtr<const m_type> cast(const Variant &p_variant) { \ + return GDNativeConstPtr<const m_type>((const m_type *)p_variant.operator uint64_t()); \ + } \ + }; \ + template <> \ + struct GDNativePtr<m_type> { \ + m_type *data = nullptr; \ + GDNativePtr() {} \ + GDNativePtr(m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return #m_type; } \ + operator m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; \ + template <> \ + struct VariantCaster<GDNativePtr<m_type>> { \ + static _FORCE_INLINE_ GDNativePtr<m_type> cast(const Variant &p_variant) { \ + return GDNativePtr<m_type>((m_type *)p_variant.operator uint64_t()); \ + } \ }; template <class T> diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index db198da54a..fcfa530388 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -38,8 +38,6 @@ #include "core/math/math_funcs.h" #include "core/string/print_string.h" #include "core/variant/variant_parser.h" -#include "scene/gui/control.h" -#include "scene/main/node.h" String Variant::get_type_name(Variant::Type p_type) { switch (p_type) { @@ -1692,8 +1690,6 @@ String Variant::stringify(int recursion_count) const { pairs.push_back(sp); } - pairs.sort(); - for (int i = 0; i < pairs.size(); i++) { if (i > 0) { str += ", "; @@ -2006,22 +2002,6 @@ Object *Variant::get_validated_object() const { } } -Variant::operator Node *() const { - if (type == OBJECT) { - return Object::cast_to<Node>(_get_obj().obj); - } else { - return nullptr; - } -} - -Variant::operator Control *() const { - if (type == OBJECT) { - return Object::cast_to<Control>(_get_obj().obj); - } else { - return nullptr; - } -} - Variant::operator Dictionary() const { if (type == DICTIONARY) { return *reinterpret_cast<const Dictionary *>(_data._mem); @@ -3256,7 +3236,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const return false; } -bool Variant::is_ref() const { +bool Variant::is_ref_counted() const { return type == OBJECT && _get_obj().id.is_ref_counted(); } @@ -3416,7 +3396,7 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, } String class_name = p_base->get_class(); - Ref<Script> script = p_base->get_script(); + Ref<Resource> script = p_base->get_script(); if (script.is_valid() && script->get_path().is_resource_file()) { class_name += "(" + script->get_path().get_file() + ")"; } diff --git a/core/variant/variant.h b/core/variant/variant.h index 0860e7fdc3..36fa755647 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -53,8 +53,6 @@ #include "core/variant/dictionary.h" class Object; -class Node; // helper -class Control; // helper struct PropertyInfo; struct MethodInfo; @@ -287,7 +285,7 @@ public: static bool can_convert(Type p_type_from, Type p_type_to); static bool can_convert_strict(Type p_type_from, Type p_type_to); - bool is_ref() const; + bool is_ref_counted() const; _FORCE_INLINE_ bool is_num() const { return type == INT || type == FLOAT; } @@ -339,8 +337,6 @@ public: operator ::RID() const; operator Object *() const; - operator Node *() const; - operator Control *() const; operator Callable() const; operator Signal() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8dd48a4c28..750f23902d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1382,7 +1382,7 @@ static void _register_variant_builtin_methods() { bind_methodv(String, begins_with, static_cast<bool (String::*)(const String &) const>(&String::begins_with), sarray("text"), varray()); bind_method(String, ends_with, sarray("text"), varray()); bind_method(String, is_subsequence_of, sarray("text"), varray()); - bind_method(String, is_subsequence_ofi, sarray("text"), varray()); + bind_method(String, is_subsequence_ofn, sarray("text"), varray()); bind_method(String, bigrams, sarray(), varray()); bind_method(String, similarity, sarray("text"), varray()); @@ -1421,6 +1421,7 @@ static void _register_variant_builtin_methods() { bind_method(String, sha1_buffer, sarray(), varray()); bind_method(String, sha256_buffer, sarray(), varray()); bind_method(String, is_empty, sarray(), varray()); + bind_methodv(String, contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray()); bind_method(String, is_absolute_path, sarray(), varray()); bind_method(String, is_relative_path, sarray(), varray()); @@ -1789,6 +1790,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, scaled, sarray("scale"), varray()); bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); + bind_method(Transform3D, sphere_interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, is_equal_approx, sarray("xform"), varray()); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 57875bf50f..e889a1bb40 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -188,7 +188,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; - } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { + } else if (is_hex_digit(ch)) { color_str += ch; } else { @@ -217,6 +217,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } case '"': { String str; + char32_t prev = 0; while (true) { char32_t ch = p_stream->get_char(); @@ -252,22 +253,25 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri case 'r': res = 13; break; + case 'U': case 'u': { - //hex number - for (int j = 0; j < 4; j++) { + // Hexadecimal sequence. + int hex_len = (next == 'U') ? 6 : 4; + for (int j = 0; j < hex_len; j++) { char32_t c = p_stream->get_char(); + if (c == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -290,15 +294,49 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; } + // Parse UTF-16 pair. + if ((res & 0xfffffc00) == 0xd800) { + if (prev == 0) { + prev = res; + continue; + } else { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } else if ((res & 0xfffffc00) == 0xdc00) { + if (prev == 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired trail surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else { + res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev = 0; + } + } + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += res; - } else { + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } if (ch == '\n') { line++; } str += ch; } } + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } if (p_stream->is_utf8()) { str.parse_utf8(str.ascii(true).get_data()); @@ -343,7 +381,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri while (true) { switch (reading) { case READING_INT: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { //pass } else if (c == '.') { reading = READING_DEC; @@ -357,7 +395,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_DEC: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; } else { @@ -366,7 +404,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_EXP: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { @@ -395,11 +433,11 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.value = num.as_int(); } return OK; - } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + } else if (is_ascii_char(cchar) || is_underscore(cchar)) { StringBuffer<> id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { id += cchar; cchar = p_stream->get_char(); first = false; @@ -1457,7 +1495,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::FLOAT: { String s = rtos_fix(p_variant.operator double()); if (s != "inf" && s != "inf_neg" && s != "nan") { - if (s.find(".") == -1 && s.find("e") == -1) { + if (!s.contains(".") && !s.contains("e")) { s += ".0"; } } diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 3fd8eb5474..60950099d2 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -433,7 +433,7 @@ struct VariantUtilityFunctions { static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { if (obj.get_type() == Variant::OBJECT) { r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref()) { + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); REF r = obj; if (r.is_valid()) { |