Merge pull request #37289 from akien-mga/3.2-cherrypicks

Cherry-picks for the 3.2 branch (future 3.2.2) - 1st batch
This commit is contained in:
Rémi Verschelde 2020-03-25 13:58:14 +01:00 committed by GitHub
commit 909e303def
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
161 changed files with 1165 additions and 3528 deletions

View file

@ -163,7 +163,7 @@ Error PCKPacker::flush(bool p_verbose) {
src->close();
memdelete(src);
count += 1;
if (p_verbose) {
if (p_verbose && files.size() > 0) {
if (count % 100 == 0) {
printf("%i/%i (%.2f)\r", count, files.size(), float(count) / files.size() * 100);
fflush(stdout);

View file

@ -337,14 +337,14 @@ bool MessageQueue::is_flushing() const {
MessageQueue::MessageQueue() {
ERR_FAIL_COND_MSG(singleton != NULL, "MessageQueue singleton already exist.");
ERR_FAIL_COND_MSG(singleton != NULL, "A MessageQueue singleton already exists.");
singleton = this;
flushing = false;
buffer_end = 0;
buffer_max_used = 0;
buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"));
buffer_size *= 1024;
buffer = memnew_arr(uint8_t, buffer_size);
}

View file

@ -1353,6 +1353,25 @@ Array Object::_get_incoming_connections() const {
return ret;
}
bool Object::has_signal(const StringName &p_name) const {
if (!script.is_null()) {
Ref<Script> scr = script;
if (scr.is_valid() && scr->has_script_signal(p_name)) {
return true;
}
}
if (ClassDB::has_signal(get_class_name(), p_name)) {
return true;
}
if (_has_user_signal(p_name)) {
return true;
}
return false;
}
void Object::get_signal_list(List<MethodInfo> *p_signals) const {
if (!script.is_null()) {
@ -1707,6 +1726,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method);
ClassDB::bind_method(D_METHOD("has_signal", "signal"), &Object::has_signal);
ClassDB::bind_method(D_METHOD("get_signal_list"), &Object::_get_signal_list);
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections);

View file

@ -704,6 +704,7 @@ public:
void add_user_signal(const MethodInfo &p_signal);
Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST);
Error emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount);
bool has_signal(const StringName &p_name) const;
void get_signal_list(List<MethodInfo> *p_signals) const;
void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
void get_all_signal_connections(List<Connection> *p_connections) const;

View file

@ -518,6 +518,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
value = false;
else if (id == "null" || id == "nil")
value = Variant();
else if (id == "inf")
value = Math_INF;
else if (id == "nan")
value = Math_NAN;
else if (id == "Vector2") {
Vector<float> args;
@ -1592,8 +1596,10 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::REAL: {
String s = rtosfix(p_variant.operator real_t());
if (s.find(".") == -1 && s.find("e") == -1)
s += ".0";
if (s != "inf" && s != "nan") {
if (s.find(".") == -1 && s.find("e") == -1)
s += ".0";
}
p_store_string_func(p_store_string_ud, s);
} break;
case Variant::STRING: {

View file

@ -183,6 +183,17 @@
Renames the given node.
</description>
</method>
<method name="replace_node">
<return type="void">
</return>
<argument index="0" name="name" type="String">
</argument>
<argument index="1" name="node" type="AnimationNode">
</argument>
<description>
Replaces the node and keeps its transitions unchanged.
</description>
</method>
<method name="set_end_node">
<return type="void">
</return>

View file

@ -52,6 +52,12 @@
Returns the editor [Viewport].
</description>
</method>
<method name="get_file_system_dock">
<return type="FileSystemDock">
</return>
<description>
</description>
</method>
<method name="get_inspector" qualifiers="const">
<return type="EditorInspector">
</return>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ExternalTexture" inherits="Texture" category="Core" version="3.2">
<class name="ExternalTexture" inherits="Texture" version="3.2">
<brief_description>
Adds support for external textures as defined by https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
</brief_description>
@ -8,14 +8,16 @@
<tutorials>
</tutorials>
<methods>
<method name="get_external_texture_id" qualifiers="const">
<return type="int"/>
<method name="get_external_texture_id">
<return type="int">
</return>
<description>
Returns the external texture name.
</description>
</method>
</methods>
<members>
<member name="flags" type="int" setter="set_flags" getter="get_flags" override="true" default="0" />
<member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2( 1, 1 )">
External texture size.
</member>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FileSystemDock" inherits="VBoxContainer" version="3.2">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="can_drop_data_fw" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="arg0" type="Vector2">
</argument>
<argument index="1" name="arg1" type="Variant">
</argument>
<argument index="2" name="arg2" type="Control">
</argument>
<description>
</description>
</method>
<method name="drop_data_fw">
<return type="void">
</return>
<argument index="0" name="arg0" type="Vector2">
</argument>
<argument index="1" name="arg1" type="Variant">
</argument>
<argument index="2" name="arg2" type="Control">
</argument>
<description>
</description>
</method>
<method name="get_drag_data_fw">
<return type="Variant">
</return>
<argument index="0" name="arg0" type="Vector2">
</argument>
<argument index="1" name="arg1" type="Control">
</argument>
<description>
</description>
</method>
<method name="navigate_to_path">
<return type="void">
</return>
<argument index="0" name="arg0" type="String">
</argument>
<description>
</description>
</method>
</methods>
<signals>
<signal name="display_mode_changed">
<description>
</description>
</signal>
<signal name="file_removed">
<argument index="0" name="file" type="String">
</argument>
<description>
</description>
</signal>
<signal name="files_moved">
<argument index="0" name="old_file" type="String">
</argument>
<argument index="1" name="new_file" type="String">
</argument>
<description>
</description>
</signal>
<signal name="folder_moved">
<argument index="0" name="old_folder" type="String">
</argument>
<argument index="1" name="new_file" type="String">
</argument>
<description>
</description>
</signal>
<signal name="folder_removed">
<argument index="0" name="folder" type="String">
</argument>
<description>
</description>
</signal>
<signal name="inherit">
<argument index="0" name="file" type="String">
</argument>
<description>
</description>
</signal>
<signal name="instance">
<argument index="0" name="files" type="PoolStringArray">
</argument>
<description>
</description>
</signal>
</signals>
<constants>
</constants>
</class>

View file

@ -349,7 +349,8 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
Whether to accumulate similar input events sent by the operating system. Enabled by default.
Enables or disables the accumulation of similar input events sent by the operating system. When input accumulation is enabled, all input events generated during a frame will be merged and emitted when the frame is done rendering. Therefore, this limits the number of input method calls per second to the rendering FPS.
Input accumulation is enabled by default. It can be disabled to get slightly more precise/reactive input at the cost of increased CPU usage. In applications where drawing freehand lines is required, input accumulation should generally be disabled while the user is drawing the line to get results that closely follow the actual input.
</description>
</method>
<method name="start_joy_vibration">

View file

@ -16,7 +16,8 @@
Represents the pressure the user puts on the pen. Ranges from [code]0.0[/code] to [code]1.0[/code].
</member>
<member name="relative" type="Vector2" setter="set_relative" getter="get_relative" default="Vector2( 0, 0 )">
The mouse position relative to the previous position (position at the last frame).
The mouse position relative to the previous position (position at the last frame).
[b]Note:[/b] Since [InputEventMouseMotion] is only emitted when the mouse moves, the last event won't have a relative position of [code]Vector2(0, 0)[/code] when the user stops moving the mouse.
</member>
<member name="speed" type="Vector2" setter="set_speed" getter="get_speed" default="Vector2( 0, 0 )">
The mouse speed in pixels per second.

View file

@ -313,13 +313,22 @@
Returns [code]true[/code] if the object contains the given [code]method[/code].
</description>
</method>
<method name="has_signal" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="signal" type="String">
</argument>
<description>
Returns [code]true[/code] if the given [code]signal[/code] exists.
</description>
</method>
<method name="has_user_signal" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="signal" type="String">
</argument>
<description>
Returns [code]true[/code] if the given user-defined [code]signal[/code] exists.
Returns [code]true[/code] if the given user-defined [code]signal[/code] exists. Only signals added using [method add_user_signal] are taken into account.
</description>
</method>
<method name="is_blocking_signals" qualifiers="const">

View file

@ -55,7 +55,7 @@
<member name="code" type="String" setter="set_code" getter="get_code" default="&quot;&quot;">
Returns the shader's code as the user has written it, not the full generated code used internally.
</member>
<member name="custom_defines" type="String" setter="set_custom_defines" getter="get_custom_defines" default="&quot;&quot;">
<member name="custom_defines" type="String" setter="set_custom_defines" getter="get_custom_defines" default="&quot;#define MAX_LIGHT_DATA_STRUCTS 409#define MAX_FORWARD_LIGHTS 8#define MAX_REFLECTION_DATA_STRUCTS 455#define MAX_SKELETON_BONES 1365&quot;">
Returns the shader's custom defines. Custom defines can be used in Godot to add GLSL preprocessor directives (e.g: extensions) required for the shader logic.
[b]Note:[/b] Custom defines are not validated by the Godot shader parser, so care should be taken when using them.
</member>

View file

@ -5,6 +5,7 @@
</brief_description>
<description>
A node that displays a 2D texture in a 3D environment. The texture displayed can be a region from a larger atlas texture, or a frame from a sprite sheet animation.
[b]Note:[/b] There are [url=https://github.com/godotengine/godot/issues/20855]known performance issues[/url] when using [Sprite3D]. Consider using a [MeshInstance] with a [QuadMesh] as the mesh instead. You can still have billboarding by enabling billboard properties in the QuadMesh's [SpatialMaterial].
</description>
<tutorials>
</tutorials>

View file

@ -5,6 +5,7 @@
</brief_description>
<description>
The VisibilityEnabler will disable [RigidBody] and [AnimationPlayer] nodes when they are not visible. It will only affect other nodes within the same scene as the VisibilityEnabler itself.
Note that VisibilityEnabler will not affect nodes added after scene initialization.
</description>
<tutorials>
</tutorials>

View file

@ -5,6 +5,7 @@
</brief_description>
<description>
The VisibilityEnabler2D will disable [RigidBody2D], [AnimationPlayer], and other nodes when they are not visible. It will only affect nodes with the same root node as the VisibilityEnabler2D, and the root node itself.
Note that VisibilityEnabler2D will not affect nodes added after scene initialization.
</description>
<tutorials>
</tutorials>

View file

@ -4279,13 +4279,13 @@
<constant name="TEXTURE_TYPE_2D" value="0" enum="TextureType">
Normal texture with 2 dimensions, width and height.
</constant>
<constant name="TEXTURE_TYPE_CUBEMAP" value="1" enum="TextureType">
<constant name="TEXTURE_TYPE_CUBEMAP" value="2" enum="TextureType">
Texture made up of six faces, can be looked up with a [code]vec3[/code] in shader.
</constant>
<constant name="TEXTURE_TYPE_2D_ARRAY" value="2" enum="TextureType">
<constant name="TEXTURE_TYPE_2D_ARRAY" value="3" enum="TextureType">
An array of 2-dimensional textures.
</constant>
<constant name="TEXTURE_TYPE_3D" value="3" enum="TextureType">
<constant name="TEXTURE_TYPE_3D" value="4" enum="TextureType">
A 3-dimensional texture with width, height, and depth.
</constant>
<constant name="TEXTURE_FLAG_MIPMAPS" value="1" enum="TextureFlags">

View file

@ -28,12 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "servers/audio_server.h"
#ifdef ALSA_ENABLED
#ifndef AUDIO_DRIVER_ALSA_H
#define AUDIO_DRIVER_ALSA_H
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "servers/audio_server.h"
#include <alsa/asoundlib.h>
@ -87,4 +89,6 @@ public:
~AudioDriverALSA();
};
#endif
#endif // AUDIO_DRIVER_ALSA_H
#endif // ALSA_ENABLED

View file

@ -1,303 +0,0 @@
/*************************************************************************/
/* doc_dump.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
#include "doc_dump.h"
#include "core/os/file_access.h"
#include "core/version.h"
#include "scene/main/node.h"
static void _write_string(FileAccess *f, int p_tablevel, const String &p_string) {
String tab;
for (int i = 0; i < p_tablevel; i++)
tab += "\t";
f->store_string(tab + p_string + "\n");
}
struct _ConstantSort {
String name;
int value;
bool operator<(const _ConstantSort &p_c) const {
String left_a = name.find("_") == -1 ? name : name.substr(0, name.find("_"));
String left_b = p_c.name.find("_") == -1 ? p_c.name : p_c.name.substr(0, p_c.name.find("_"));
if (left_a == left_b)
return value < p_c.value;
else
return left_a < left_b;
}
};
static String _escape_string(const String &p_str) {
String ret = p_str;
ret = ret.replace("&", "&amp;");
ret = ret.replace("<", "&gt;");
ret = ret.replace(">", "&lt;");
ret = ret.replace("'", "&apos;");
ret = ret.replace("\"", "&quot;");
for (char i = 1; i < 32; i++) {
char chr[2] = { i, 0 };
ret = ret.replace(chr, "&#" + String::num(i) + ";");
}
ret = ret.utf8();
return ret;
}
void DocDump::dump(const String &p_file) {
List<StringName> class_list;
ClassDB::get_class_list(&class_list);
class_list.sort_custom<StringName::AlphCompare>();
FileAccess *f = FileAccess::open(p_file, FileAccess::WRITE);
_write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
_write_string(f, 0, String("<doc version=\"") + VERSION_BRANCH + "\" name=\"Engine Types\">");
while (class_list.size()) {
String name = class_list.front()->get();
String header = "<class name=\"" + name + "\"";
String inherits = ClassDB::get_parent_class(name);
if (inherits != "")
header += " inherits=\"" + inherits + "\"";
_write_string(f, 0, header);
_write_string(f, 1, "<brief_description>");
_write_string(f, 1, "</brief_description>");
_write_string(f, 1, "<description>");
_write_string(f, 1, "</description>");
_write_string(f, 1, "<methods>");
List<MethodInfo> method_list;
ClassDB::get_method_list(name, &method_list, true);
method_list.sort();
for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) {
if (E->get().name == "" || E->get().name[0] == '_')
continue; //hidden
MethodBind *m = ClassDB::get_method(name, E->get().name);
String qualifiers;
if (E->get().flags & METHOD_FLAG_CONST)
qualifiers += "qualifiers=\"const\"";
_write_string(f, 2, "<method name=\"" + _escape_string(E->get().name) + "\" " + qualifiers + " >");
for (int i = -1; i < E->get().arguments.size(); i++) {
PropertyInfo arginfo;
if (i == -1) {
arginfo = E->get().return_val;
String type_name = (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) ? arginfo.hint_string : Variant::get_type_name(arginfo.type);
if (arginfo.type == Variant::NIL)
continue;
_write_string(f, 3, "<return type=\"" + type_name + "\">");
} else {
arginfo = E->get().arguments[i];
String type_name;
if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE)
type_name = arginfo.hint_string;
else if (arginfo.type == Variant::NIL)
type_name = "Variant";
else
type_name = Variant::get_type_name(arginfo.type);
if (m && m->has_default_argument(i)) {
Variant default_arg = m->get_default_argument(i);
String default_arg_text = String(_escape_string(m->get_default_argument(i)));
switch (default_arg.get_type()) {
case Variant::NIL:
default_arg_text = "NULL";
break;
// atomic types
case Variant::BOOL:
if (bool(default_arg))
default_arg_text = "true";
else
default_arg_text = "false";
break;
case Variant::INT:
case Variant::REAL:
//keep it
break;
case Variant::STRING:
case Variant::NODE_PATH:
default_arg_text = "\"" + default_arg_text + "\"";
break;
case Variant::TRANSFORM:
if (default_arg.operator Transform() == Transform()) {
default_arg_text = "";
}
default_arg_text = Variant::get_type_name(default_arg.get_type()) + "(" + default_arg_text + ")";
break;
case Variant::VECTOR2:
case Variant::RECT2:
case Variant::VECTOR3:
case Variant::PLANE:
case Variant::QUAT:
case Variant::AABB:
case Variant::BASIS:
case Variant::COLOR:
case Variant::POOL_BYTE_ARRAY:
case Variant::POOL_INT_ARRAY:
case Variant::POOL_REAL_ARRAY:
case Variant::POOL_STRING_ARRAY:
case Variant::POOL_VECTOR3_ARRAY:
case Variant::POOL_COLOR_ARRAY:
default_arg_text = Variant::get_type_name(default_arg.get_type()) + "(" + default_arg_text + ")";
break;
case Variant::OBJECT:
case Variant::DICTIONARY: // 20
case Variant::ARRAY:
case Variant::_RID:
default: {
}
}
_write_string(f, 3, "<argument index=\"" + itos(i) + "\" name=\"" + _escape_string(arginfo.name) + "\" type=\"" + type_name + "\" default=\"" + _escape_string(default_arg_text) + "\">");
} else
_write_string(f, 3, "<argument index=\"" + itos(i) + "\" name=\"" + arginfo.name + "\" type=\"" + type_name + "\">");
}
String hint;
switch (arginfo.hint) {
case PROPERTY_HINT_DIR: hint = "A directory."; break;
case PROPERTY_HINT_RANGE: hint = "Range - min: " + arginfo.hint_string.get_slice(",", 0) + " max: " + arginfo.hint_string.get_slice(",", 1) + " step: " + arginfo.hint_string.get_slice(",", 2); break;
case PROPERTY_HINT_ENUM:
hint = "Values: ";
for (int j = 0; j < arginfo.hint_string.get_slice_count(","); j++) {
if (j > 0) hint += ", ";
hint += arginfo.hint_string.get_slice(",", j) + "=" + itos(j);
}
break;
case PROPERTY_HINT_LENGTH: hint = "Length: " + arginfo.hint_string; break;
case PROPERTY_HINT_FLAGS:
hint = "Values: ";
for (int j = 0; j < arginfo.hint_string.get_slice_count(","); j++) {
if (j > 0) hint += ", ";
hint += arginfo.hint_string.get_slice(",", j) + "=" + itos((uint64_t)1 << j);
}
break;
case PROPERTY_HINT_FILE: hint = "A file:"; break;
default: {
}
//case PROPERTY_HINT_RESOURCE_TYPE: hint="Type: "+arginfo.hint_string; break;
};
if (hint != "")
_write_string(f, 4, hint);
_write_string(f, 3, (i == -1) ? "</return>" : "</argument>");
}
_write_string(f, 3, "<description>");
_write_string(f, 3, "</description>");
_write_string(f, 2, "</method>");
}
_write_string(f, 1, "</methods>");
List<MethodInfo> signal_list;
ClassDB::get_signal_list(name, &signal_list, true);
if (signal_list.size()) {
_write_string(f, 1, "<signals>");
for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) {
_write_string(f, 2, "<signal name=\"" + EV->get().name + "\">");
for (int i = 0; i < EV->get().arguments.size(); i++) {
PropertyInfo arginfo = EV->get().arguments[i];
_write_string(f, 3, "<argument index=\"" + itos(i) + "\" name=\"" + arginfo.name + "\" type=\"" + Variant::get_type_name(arginfo.type) + "\">");
_write_string(f, 3, "</argument>");
}
_write_string(f, 3, "<description>");
_write_string(f, 3, "</description>");
_write_string(f, 2, "</signal>");
}
_write_string(f, 1, "</signals>");
}
_write_string(f, 1, "<constants>");
List<String> constant_list;
ClassDB::get_integer_constant_list(name, &constant_list, true);
/* constants are sorted in a special way */
List<_ConstantSort> constant_sort;
for (List<String>::Element *E = constant_list.front(); E; E = E->next()) {
_ConstantSort cs;
cs.name = E->get();
cs.value = ClassDB::get_integer_constant(name, E->get());
constant_sort.push_back(cs);
}
constant_sort.sort();
for (List<_ConstantSort>::Element *E = constant_sort.front(); E; E = E->next()) {
_write_string(f, 2, "<constant name=\"" + E->get().name + "\" value=\"" + itos(E->get().value) + "\">");
_write_string(f, 2, "</constant>");
}
_write_string(f, 1, "</constants>");
_write_string(f, 0, "</class>");
class_list.erase(name);
}
_write_string(f, 0, "</doc>");
f->close();
memdelete(f);
}

View file

@ -1,41 +0,0 @@
/*************************************************************************/
/* doc_dump.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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 DOC_DUMP_H
#define DOC_DUMP_H
#include "core/class_db.h"
class DocDump {
public:
static void dump(const String &p_file);
};
#endif // DOC_DUMP_H

View file

@ -1707,6 +1707,18 @@ void EditorInspector::update_tree() {
break;
}
}
Vector<String> slices = propname.operator String().split("/");
if (slices.size() == 2 && slices[0].begins_with("custom_")) {
// Likely a theme property.
for (int i = 0; i < F->get().theme_properties.size(); i++) {
if (F->get().theme_properties[i].name == slices[1]) {
descr = F->get().theme_properties[i].description.strip_edges();
break;
}
}
}
if (!F->get().inherits.empty()) {
F = dd->class_list.find(F->get().inherits);
} else {

View file

@ -1400,7 +1400,6 @@ void EditorNode::_mark_unsaved_scenes() {
String path = node->get_filename();
if (!(path == String() || FileAccess::exists(path))) {
node->set_filename("");
if (i == editor_data.get_edited_scene())
set_current_version(-1);
else
@ -6834,6 +6833,9 @@ EditorNode::EditorNode() {
screenshot_timer->connect("timeout", this, "_request_screenshot");
add_child(screenshot_timer);
screenshot_timer->set_owner(get_owner());
String exec = OS::get_singleton()->get_executable_path();
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec); // Save editor executable path for third-party tools
}
EditorNode::~EditorNode() {

View file

@ -137,19 +137,12 @@ void EditorPluginSettings::update_plugins() {
item->set_metadata(1, script);
item->set_text(2, author);
item->set_metadata(2, description);
item->set_cell_mode(3, TreeItem::CELL_MODE_RANGE);
item->set_range_config(3, 0, 1, 1);
item->set_text(3, "Inactive,Active");
item->set_cell_mode(3, TreeItem::CELL_MODE_CHECK);
item->set_text(3, TTR("Enable"));
bool is_active = EditorNode::get_singleton()->is_addon_plugin_enabled(d2);
item->set_checked(3, is_active);
item->set_editable(3, true);
item->add_button(4, get_icon("Edit", "EditorIcons"), BUTTON_PLUGIN_EDIT, false, TTR("Edit Plugin"));
if (EditorNode::get_singleton()->is_addon_plugin_enabled(d2)) {
item->set_custom_color(3, get_color("success_color", "Editor"));
item->set_range(3, 1);
} else {
item->set_custom_color(3, get_color("disabled_font_color", "Editor"));
item->set_range(3, 0);
}
}
}
}
@ -164,7 +157,7 @@ void EditorPluginSettings::_plugin_activity_changed() {
TreeItem *ti = plugin_list->get_edited();
ERR_FAIL_COND(!ti);
bool active = ti->get_range(3);
bool active = ti->is_checked(3);
String name = ti->get_metadata(0);
EditorNode::get_singleton()->set_addon_plugin_enabled(name, active, true);
@ -173,14 +166,9 @@ void EditorPluginSettings::_plugin_activity_changed() {
if (is_active != active) {
updating = true;
ti->set_range(3, is_active ? 1 : 0);
ti->set_checked(3, is_active);
updating = false;
}
if (is_active)
ti->set_custom_color(3, get_color("success_color", "Editor"));
else
ti->set_custom_color(3, get_color("disabled_font_color", "Editor"));
}
void EditorPluginSettings::_create_clicked() {

View file

@ -1,104 +0,0 @@
/*************************************************************************/
/* file_type_cache.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
#include "file_type_cache.h"
#include "core/os/file_access.h"
#include "core/project_settings.h"
FileTypeCache *FileTypeCache::singleton = NULL;
bool FileTypeCache::has_file(const String &p_path) const {
GLOBAL_LOCK_FUNCTION
return file_type_map.has(p_path);
}
String FileTypeCache::get_file_type(const String &p_path) const {
GLOBAL_LOCK_FUNCTION
ERR_FAIL_COND_V(!file_type_map.has(p_path), "");
return file_type_map[p_path];
}
void FileTypeCache::set_file_type(const String &p_path, const String &p_type) {
GLOBAL_LOCK_FUNCTION
file_type_map[p_path] = p_type;
}
void FileTypeCache::load() {
GLOBAL_LOCK_FUNCTION
String project = ProjectSettings::get_singleton()->get_resource_path();
FileAccess *f = FileAccess::open(project + "/file_type_cache.cch", FileAccess::READ);
if (!f) {
WARN_PRINT("Can't open file_type_cache.cch.");
return;
}
file_type_map.clear();
while (!f->eof_reached()) {
String path = f->get_line();
if (f->eof_reached())
break;
String type = f->get_line();
set_file_type(path, type);
}
memdelete(f);
}
void FileTypeCache::save() {
GLOBAL_LOCK_FUNCTION
String project = ProjectSettings::get_singleton()->get_resource_path();
FileAccess *f = FileAccess::open(project + "/file_type_cache.cch", FileAccess::WRITE);
ERR_FAIL_COND_MSG(!f, "Can't open file_type_cache.cch for writing, not saving file type cache!");
const String *K = NULL;
while ((K = file_type_map.next(K))) {
f->store_line(*K);
f->store_line(file_type_map[*K]);
}
memdelete(f);
}
FileTypeCache::FileTypeCache() {
ERR_FAIL_COND_MSG(singleton, "FileTypeCache singleton already exist.");
singleton = this;
}

View file

@ -1,57 +0,0 @@
/*************************************************************************/
/* file_type_cache.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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 FILE_TYPE_CACHE_H
#define FILE_TYPE_CACHE_H
#include "core/object.h"
class FileTypeCache : Object {
GDCLASS(FileTypeCache, Object);
HashMap<String, String> file_type_map;
static FileTypeCache *singleton;
public:
static FileTypeCache *get_singleton() { return singleton; }
bool has_file(const String &p_path) const;
String get_file_type(const String &p_path) const;
void set_file_type(const String &p_path, const String &p_type);
void load();
void save();
FileTypeCache();
};
#endif // FILE_TYPE_CACHE_H

View file

@ -4217,10 +4217,20 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
float prev_zoom = zoom;
zoom = p_zoom;
Point2 ofs = p_position;
ofs = ofs / prev_zoom - ofs / zoom;
view_offset.x = Math::round(view_offset.x + ofs.x);
view_offset.y = Math::round(view_offset.y + ofs.y);
view_offset += p_position / prev_zoom - p_position / zoom;
// We want to align in-scene pixels to screen pixels, this prevents blurry rendering
// in small details (texts, lines).
// This correction adds a jitter movement when zooming, so we correct only when the
// zoom factor is an integer. (in the other cases, all pixels won't be aligned anyway)
float closest_zoom_factor = Math::round(zoom);
if (Math::is_zero_approx(zoom - closest_zoom_factor)) {
// make sure scene pixel at view_offset is aligned on a screen pixel
Vector2 view_offset_int = view_offset.floor();
Vector2 view_offset_frac = view_offset - view_offset_int;
view_offset = view_offset_int + (view_offset_frac * closest_zoom_factor).round() / closest_zoom_factor;
}
_update_zoom_label();
update_viewport();

View file

@ -2389,11 +2389,19 @@ void SpatialEditorViewport::_notification(int p_what) {
if (!se)
continue;
Transform t = sp->get_global_gizmo_transform();
exist = true;
if (se->last_xform == t && !se->last_xform_dirty)
continue;
changed = true;
se->last_xform_dirty = false;
se->last_xform = t;
VisualInstance *vi = Object::cast_to<VisualInstance>(sp);
se->aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp);
Transform t = sp->get_global_gizmo_transform();
t.translate(se->aabb.position);
// apply AABB scaling before item's global transform
@ -2401,11 +2409,6 @@ void SpatialEditorViewport::_notification(int p_what) {
aabb_s.scale(se->aabb.size);
t.basis = t.basis * aabb_s;
exist = true;
if (se->last_xform == t)
continue;
changed = true;
se->last_xform = t;
VisualServer::get_singleton()->instance_set_transform(se->sbox_instance, t);
}
@ -3360,6 +3363,7 @@ void SpatialEditorViewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("_menu_option"), &SpatialEditorViewport::_menu_option);
ClassDB::bind_method(D_METHOD("_toggle_camera_preview"), &SpatialEditorViewport::_toggle_camera_preview);
ClassDB::bind_method(D_METHOD("_preview_exited_scene"), &SpatialEditorViewport::_preview_exited_scene);
ClassDB::bind_method(D_METHOD("_update_camera"), &SpatialEditorViewport::_update_camera);
ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &SpatialEditorViewport::update_transform_gizmo_view);
ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &SpatialEditorViewport::_selection_result_pressed);
ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &SpatialEditorViewport::_selection_menu_hide);
@ -4504,13 +4508,15 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
}
if (d.has("translate_snap"))
snap_translate->set_text(d["translate_snap"]);
snap_translate_value = d["translate_snap"];
if (d.has("rotate_snap"))
snap_rotate->set_text(d["rotate_snap"]);
snap_rotate_value = d["rotate_snap"];
if (d.has("scale_snap"))
snap_scale->set_text(d["scale_snap"]);
snap_scale_value = d["scale_snap"];
_snap_update();
if (d.has("local_coords")) {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]);
@ -4617,6 +4623,20 @@ void SpatialEditor::edit(Spatial *p_spatial) {
}
}
void SpatialEditor::_snap_changed() {
snap_translate_value = snap_translate->get_text().to_double();
snap_rotate_value = snap_rotate->get_text().to_double();
snap_scale_value = snap_scale->get_text().to_double();
}
void SpatialEditor::_snap_update() {
snap_translate->set_text(String::num(snap_translate_value));
snap_rotate->set_text(String::num(snap_rotate_value));
snap_scale->set_text(String::num(snap_scale_value));
}
void SpatialEditor::_xform_dialog_action() {
Transform t;
@ -5848,6 +5868,8 @@ void SpatialEditor::_bind_methods() {
ClassDB::bind_method("_refresh_menu_icons", &SpatialEditor::_refresh_menu_icons);
ClassDB::bind_method("_update_camera_override_button", &SpatialEditor::_update_camera_override_button);
ClassDB::bind_method("_update_camera_override_viewport", &SpatialEditor::_update_camera_override_viewport);
ClassDB::bind_method("_snap_changed", &SpatialEditor::_snap_changed);
ClassDB::bind_method("_snap_update", &SpatialEditor::_snap_update);
ADD_SIGNAL(MethodInfo("transform_key_request"));
ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
@ -6106,25 +6128,30 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
/* SNAP DIALOG */
snap_translate_value = 1;
snap_rotate_value = 15;
snap_scale_value = 10;
snap_dialog = memnew(ConfirmationDialog);
snap_dialog->set_title(TTR("Snap Settings"));
add_child(snap_dialog);
snap_dialog->connect("confirmed", this, "_snap_changed");
snap_dialog->get_cancel()->connect("pressed", this, "_snap_update");
VBoxContainer *snap_dialog_vbc = memnew(VBoxContainer);
snap_dialog->add_child(snap_dialog_vbc);
snap_translate = memnew(LineEdit);
snap_translate->set_text("1");
snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate);
snap_rotate = memnew(LineEdit);
snap_rotate->set_text("15");
snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate);
snap_scale = memnew(LineEdit);
snap_scale->set_text("10");
snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale);
_snap_update();
/* SETTINGS DIALOG */
settings_dialog = memnew(ConfirmationDialog);
@ -6155,6 +6182,10 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500));
settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar);
for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) {
settings_dialog->connect("confirmed", viewports[i], "_update_camera", varray(0.0));
}
/* XFORM DIALOG */
xform_dialog = memnew(ConfirmationDialog);

View file

@ -469,10 +469,14 @@ public:
Transform original; // original location when moving
Transform original_local;
Transform last_xform; // last transform
bool last_xform_dirty;
Spatial *sp;
RID sbox_instance;
SpatialEditorSelectedItem() { sp = NULL; }
SpatialEditorSelectedItem() {
sp = NULL;
last_xform_dirty = true;
}
~SpatialEditorSelectedItem();
};
@ -579,6 +583,9 @@ private:
Ref<SpatialMaterial> plane_gizmo_color_hl[3];
int over_gizmo_handle;
float snap_translate_value;
float snap_rotate_value;
float snap_scale_value;
Ref<ArrayMesh> selection_box;
RID indicators;
@ -658,6 +665,8 @@ private:
SpinBox *settings_znear;
SpinBox *settings_zfar;
void _snap_changed();
void _snap_update();
void _xform_dialog_action();
void _menu_item_pressed(int p_option);
void _menu_item_toggled(bool pressed, int p_option);

View file

@ -1999,6 +1999,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
device_index_label = l;
device_index = memnew(OptionButton);
device_index->set_clip_text(true);
vbc_right->add_child(device_index);
setting = false;

View file

@ -28,8 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "test_main.h"
#include "core/list.h"
#include "core/os/main_loop.h"
#ifdef DEBUG_ENABLED

View file

@ -32,9 +32,10 @@
#define TEST_MAIN_H
#include "core/list.h"
#include "core/os/main_loop.h"
#include "core/ustring.h"
const char **tests_get_names();
MainLoop *test_main(String p_test, const List<String> &p_args);
#endif
#endif // TEST_MAIN_H

View file

@ -30,9 +30,8 @@
#include "test_oa_hash_map.h"
#include "core/os/os.h"
#include "core/oa_hash_map.h"
#include "core/os/os.h"
namespace TestOAHashMap {

View file

@ -37,4 +37,5 @@ namespace TestOAHashMap {
MainLoop *test();
}
#endif // TEST_OA_HASH_MAP_H

View file

@ -28,6 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "test_ordered_hash_map.h"
#include "core/ordered_hash_map.h"
#include "core/os/os.h"
#include "core/pair.h"

View file

@ -31,9 +31,11 @@
#ifndef TEST_ORDERED_HASH_MAP_H
#define TEST_ORDERED_HASH_MAP_H
#include "core/os/main_loop.h"
namespace TestOrderedHashMap {
MainLoop *test();
}
#endif
#endif // TEST_ORDERED_HASH_MAP_H

View file

@ -181,53 +181,6 @@ void unregister_module_types() {
return module_list
def win32_spawn(sh, escape, cmd, args, env):
import subprocess
newargs = ' '.join(args[1:])
cmdline = cmd + " " + newargs
startupinfo = subprocess.STARTUPINFO()
for e in env:
if type(env[e]) != type(""):
env[e] = str(env[e])
proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False, env=env)
_, err = proc.communicate()
rv = proc.wait()
if rv:
print("=====")
print(err)
print("=====")
return rv
"""
def win32_spawn(sh, escape, cmd, args, spawnenv):
import win32file
import win32event
import win32process
import win32security
for var in spawnenv:
spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
sAttrs = win32security.SECURITY_ATTRIBUTES()
StartupInfo = win32process.STARTUPINFO()
newargs = ' '.join(map(escape, args[1:]))
cmdline = cmd + " " + newargs
# check for any special operating system commands
if cmd == 'del':
for arg in args[1:]:
win32file.DeleteFile(arg)
exit_code = 0
else:
# otherwise execute the command.
hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
exit_code = win32process.GetExitCodeProcess(hProcess)
win32file.CloseHandle(hProcess);
win32file.CloseHandle(hThread);
return exit_code
"""
def disable_module(self):
self.disabled_modules.append(self.current_module)

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ASSIMP_REGISTER_TYPES_H
#define ASSIMP_REGISTER_TYPES_H
void register_assimp_types();
void unregister_assimp_types();
#endif // ASSIMP_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef BMP_REGISTER_TYPES_H
#define BMP_REGISTER_TYPES_H
void register_bmp_types();
void unregister_bmp_types();
#endif // BMP_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef CSG_REGISTER_TYPES_H
#define CSG_REGISTER_TYPES_H
void register_csg_types();
void unregister_csg_types();
#endif // CSG_REGISTER_TYPES_H

View file

@ -29,6 +29,13 @@
/*************************************************************************/
#ifdef TOOLS_ENABLED
#ifndef CVTT_REGISTER_TYPES_H
#define CVTT_REGISTER_TYPES_H
void register_cvtt_types();
void unregister_cvtt_types();
#endif
#endif // CVTT_REGISTER_TYPES_H
#endif // TOOLS_ENABLED

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef DDS_REGISTER_TYPES_H
#define DDS_REGISTER_TYPES_H
void register_dds_types();
void unregister_dds_types();
#endif // DDS_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ENET_REGISTER_TYPES_H
#define ENET_REGISTER_TYPES_H
void register_enet_types();
void unregister_enet_types();
#endif // ENET_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ETC_REGISTER_TYPES_H
#define ETC_REGISTER_TYPES_H
void register_etc_types();
void unregister_etc_types();
#endif // ETC_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FREETYPE_REGISTER_TYPES_H
#define FREETYPE_REGISTER_TYPES_H
void register_freetype_types();
void unregister_freetype_types();
#endif // FREETYPE_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ARVR_REGISTER_TYPES_H
#define ARVR_REGISTER_TYPES_H
void register_arvr_types();
void unregister_arvr_types();
#endif // ARVR_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef NATIVESCRIPT_REGISTER_TYPES_H
#define NATIVESCRIPT_REGISTER_TYPES_H
void register_nativescript_types();
void unregister_nativescript_types();
#endif // NATIVESCRIPT_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef NET_REGISTER_TYPES_H
#define NET_REGISTER_TYPES_H
void register_net_types();
void unregister_net_types();
#endif // NET_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PLUGINSCRIPT_REGISTER_TYPES_H
#define PLUGINSCRIPT_REGISTER_TYPES_H
void register_pluginscript_types();
void unregister_pluginscript_types();
#endif // PLUGINSCRIPT_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GDNATIVE_REGISTER_TYPES_H
#define GDNATIVE_REGISTER_TYPES_H
void register_gdnative_types();
void unregister_gdnative_types();
#endif // GDNATIVE_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef VIDEODECODER_REGISTER_TYPES_H
#define VIDEODECODER_REGISTER_TYPES_H
void register_videodecoder_types();
void unregister_videodecoder_types();
#endif // VIDEODECODER_REGISTER_TYPES_H

View file

@ -3103,18 +3103,18 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
Vector<Node *> args;
Vector<double> constants;
bool constant = false;
bool constant = true;
for (int i = 1; i < op->arguments.size(); i++) {
args.push_back(op->arguments[i]);
if (constant && op->arguments[i]->type == Node::TYPE_CONSTANT) {
if (op->arguments[i]->type == Node::TYPE_CONSTANT) {
ConstantNode *c = static_cast<ConstantNode *>(op->arguments[i]);
if (c->value.get_type() == Variant::REAL || c->value.get_type() == Variant::INT) {
constants.push_back(c->value);
constant = true;
}
} else {
constant = false;
break;
}
}
@ -3809,6 +3809,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
StringName argname = tokenizer->get_token_identifier();
for (int i = 0; i < arguments.size(); i++) {
if (arguments[i] == argname) {
_set_error("The argument name \"" + String(argname) + "\" is defined multiple times.");
return;
}
}
arguments.push_back(argname);
#ifdef DEBUG_ENABLED
arguments_usage.push_back(0);

View file

@ -32,32 +32,107 @@
#include "core/io/json.h"
#include "core/os/copymem.h"
#include "core/project_settings.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = NULL;
void GDScriptLanguageProtocol::on_data_received(int p_id) {
lastest_client_id = p_id;
Ref<WebSocketPeer> peer = server->get_peer(p_id);
PoolByteArray data;
if (OK == peer->get_packet_buffer(data)) {
String message;
message.parse_utf8((const char *)data.read().ptr(), data.size());
if (message.begins_with("Content-Length:")) return;
String output = process_message(message);
if (!output.empty()) {
CharString charstr = output.utf8();
peer->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
Error GDScriptLanguageProtocol::LSPeer::handle_data() {
int read = 0;
// Read headers
if (!has_header) {
while (true) {
if (req_pos >= LSP_MAX_BUFFER_SIZE) {
req_pos = 0;
ERR_FAIL_COND_V_MSG(true, ERR_OUT_OF_MEMORY, "Response header too big");
}
Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
if (err != OK)
return FAILED;
else if (read != 1) // Busy, wait until next poll
return ERR_BUSY;
char *r = (char *)req_buf;
int l = req_pos;
// End of headers
if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
r[l - 3] = '\0'; // Null terminate to read string
String header;
header.parse_utf8(r);
content_length = header.substr(16).to_int();
has_header = true;
req_pos = 0;
break;
}
req_pos++;
}
}
if (has_header) {
while (req_pos < content_length) {
if (req_pos >= LSP_MAX_BUFFER_SIZE) {
req_pos = 0;
has_header = false;
ERR_FAIL_COND_V_MSG(req_pos >= LSP_MAX_BUFFER_SIZE, ERR_OUT_OF_MEMORY, "Response content too big");
}
Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
if (err != OK)
return FAILED;
else if (read != 1)
return ERR_BUSY;
req_pos++;
}
// Parse data
String msg;
msg.parse_utf8((const char *)req_buf, req_pos);
// Reset to read again
req_pos = 0;
has_header = false;
// Response
String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg);
if (!output.empty()) {
res_queue.push_back(output.utf8());
}
}
return OK;
}
void GDScriptLanguageProtocol::on_client_connected(int p_id, const String &p_protocal) {
clients.set(p_id, server->get_peer(p_id));
Error GDScriptLanguageProtocol::LSPeer::send_data() {
int sent = 0;
if (!res_queue.empty()) {
CharString c_res = res_queue[0];
if (res_sent < c_res.size()) {
Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent);
if (err != OK) {
return err;
}
res_sent += sent;
}
// Response sent
if (res_sent >= c_res.size() - 1) {
res_sent = 0;
res_queue.remove(0);
}
}
return OK;
}
void GDScriptLanguageProtocol::on_client_disconnected(int p_id, bool p_was_clean_close) {
clients.erase(p_id);
Error GDScriptLanguageProtocol::on_client_connected() {
Ref<StreamPeerTCP> tcp_peer = server->take_connection();
ERR_FAIL_COND_V_MSG(clients.size() >= LSP_MAX_CLIENTS, FAILED, "Max client limits reached");
Ref<LSPeer> peer = memnew(LSPeer);
peer->connection = tcp_peer;
clients.set(next_client_id, peer);
next_client_id++;
EditorNode::get_log()->add_message("Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
}
void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
clients.erase(p_client_id);
EditorNode::get_log()->add_message("Disconnected", EditorLog::MSG_TYPE_EDITOR);
}
String GDScriptLanguageProtocol::process_message(const String &p_text) {
@ -83,11 +158,9 @@ String GDScriptLanguageProtocol::format_output(const String &p_text) {
void GDScriptLanguageProtocol::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialize", "params"), &GDScriptLanguageProtocol::initialize);
ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized);
ClassDB::bind_method(D_METHOD("on_data_received"), &GDScriptLanguageProtocol::on_data_received);
ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
ClassDB::bind_method(D_METHOD("notify_all_clients", "p_method", "p_params"), &GDScriptLanguageProtocol::notify_all_clients, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("notify_client", "p_method", "p_params", "p_client"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("notify_client", "p_method", "p_params"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
@ -116,11 +189,12 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
Dictionary params;
params["path"] = workspace->root;
Dictionary request = make_notification("gdscrip_client/changeWorkspace", params);
if (Ref<WebSocketPeer> *peer = clients.getptr(lastest_client_id)) {
Ref<LSPeer> peer = clients.get(latest_client_id);
if (peer != NULL) {
String msg = JSON::print(request);
msg = format_output(msg);
CharString charstr = msg.utf8();
(*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
(*peer)->res_queue.push_back(msg.utf8());
}
}
@ -153,61 +227,59 @@ void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
}
void GDScriptLanguageProtocol::poll() {
server->poll();
if (server->is_connection_available()) {
on_client_connected();
}
const int *id = NULL;
while ((id = clients.next(id))) {
Ref<LSPeer> peer = clients.get(*id);
StreamPeerTCP::Status status = peer->connection->get_status();
if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) {
on_client_disconnected(*id);
id = NULL;
} else {
if (peer->connection->get_available_bytes() > 0) {
latest_client_id = *id;
Error err = peer->handle_data();
if (err != OK && err != ERR_BUSY) {
on_client_disconnected(*id);
id = NULL;
}
}
Error err = peer->send_data();
if (err != OK && err != ERR_BUSY) {
on_client_disconnected(*id);
id = NULL;
}
}
}
}
Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) {
if (server == NULL) {
server = dynamic_cast<WebSocketServer *>(ClassDB::instance("WebSocketServer"));
ERR_FAIL_COND_V(!server, FAILED);
server->set_buffers(8192, 1024, 8192, 1024); // 8mb should be way more than enough
server->connect("data_received", this, "on_data_received");
server->connect("client_connected", this, "on_client_connected");
server->connect("client_disconnected", this, "on_client_disconnected");
}
server->set_bind_ip(p_bind_ip);
return server->listen(p_port);
return server->listen(p_port, p_bind_ip);
}
void GDScriptLanguageProtocol::stop() {
const int *ptr = clients.next(NULL);
while (ptr) {
clients.get(*ptr)->close();
ptr = clients.next(ptr);
const int *id = NULL;
while ((id = clients.next(id))) {
Ref<LSPeer> peer = clients.get(*id);
peer->connection->disconnect_from_host();
}
server->stop();
clients.clear();
}
void GDScriptLanguageProtocol::notify_all_clients(const String &p_method, const Variant &p_params) {
Dictionary message = make_notification(p_method, p_params);
String msg = JSON::print(message);
msg = format_output(msg);
CharString charstr = msg.utf8();
const int *p_id = clients.next(NULL);
while (p_id != NULL) {
Ref<WebSocketPeer> peer = clients.get(*p_id);
(*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
p_id = clients.next(p_id);
void GDScriptLanguageProtocol::notify_client(const String &p_method, const Variant &p_params, int p_client_id) {
if (p_client_id == -1) {
p_client_id = latest_client_id;
}
}
void GDScriptLanguageProtocol::notify_client(const String &p_method, const Variant &p_params, int p_client) {
if (p_client == -1) {
p_client = lastest_client_id;
}
Ref<WebSocketPeer> *peer = clients.getptr(p_client);
Ref<LSPeer> peer = clients.get(p_client_id);
ERR_FAIL_COND(peer == NULL);
Dictionary message = make_notification(p_method, p_params);
String msg = JSON::print(message);
msg = format_output(msg);
CharString charstr = msg.utf8();
(*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
peer->res_queue.push_back(msg.utf8());
}
bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {
@ -219,7 +291,7 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
}
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
server = NULL;
server.instance();
singleton = this;
_initialized = false;
workspace.instance();
@ -228,9 +300,6 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
set_scope("completionItem", text_document.ptr());
set_scope("workspace", workspace.ptr());
workspace->root = ProjectSettings::get_singleton()->get_resource_path();
}
GDScriptLanguageProtocol::~GDScriptLanguageProtocol() {
memdelete(server);
server = NULL;
latest_client_id = 0;
next_client_id = 0;
}

View file

@ -31,16 +31,36 @@
#ifndef GDSCRIPT_PROTOCAL_SERVER_H
#define GDSCRIPT_PROTOCAL_SERVER_H
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
#include "core/io/tcp_server.h"
#include "gdscript_text_document.h"
#include "gdscript_workspace.h"
#include "lsp.hpp"
#include "modules/jsonrpc/jsonrpc.h"
#include "modules/websocket/websocket_peer.h"
#include "modules/websocket/websocket_server.h"
#define LSP_MAX_BUFFER_SIZE 4194304
#define LSP_MAX_CLIENTS 8
class GDScriptLanguageProtocol : public JSONRPC {
GDCLASS(GDScriptLanguageProtocol, JSONRPC)
private:
struct LSPeer : Reference {
Ref<StreamPeerTCP> connection;
uint8_t req_buf[LSP_MAX_BUFFER_SIZE];
int req_pos = 0;
bool has_header = false;
bool has_content = false;
int content_length = 0;
Vector<CharString> res_queue;
int res_sent = 0;
Error handle_data();
Error send_data();
};
enum LSPErrorCode {
RequestCancelled = -32800,
ContentModified = -32801,
@ -48,16 +68,16 @@ class GDScriptLanguageProtocol : public JSONRPC {
static GDScriptLanguageProtocol *singleton;
HashMap<int, Ref<WebSocketPeer> > clients;
WebSocketServer *server;
int lastest_client_id;
HashMap<int, Ref<LSPeer> > clients;
Ref<TCP_Server> server;
int latest_client_id;
int next_client_id;
Ref<GDScriptTextDocument> text_document;
Ref<GDScriptWorkspace> workspace;
void on_data_received(int p_id);
void on_client_connected(int p_id, const String &p_protocal);
void on_client_disconnected(int p_id, bool p_was_clean_close);
Error on_client_connected();
void on_client_disconnected(const int &p_client_id);
String process_message(const String &p_text);
String format_output(const String &p_text);
@ -80,14 +100,12 @@ public:
Error start(int p_port, const IP_Address &p_bind_ip);
void stop();
void notify_all_clients(const String &p_method, const Variant &p_params = Variant());
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client = -1);
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
bool is_smart_resolve_enabled() const;
bool is_goto_native_symbols_enabled() const;
GDScriptLanguageProtocol();
~GDScriptLanguageProtocol();
};
#endif

View file

@ -33,8 +33,11 @@
#include "../gdscript_parser.h"
#include "core/project_settings.h"
#include "core/script_language.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
#include "gdscript_language_protocol.h"
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
@ -373,6 +376,50 @@ void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
GDScriptLanguageProtocol::get_singleton()->notify_client("textDocument/publishDiagnostics", params);
}
void GDScriptWorkspace::_get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners) {
if (!efsd)
return;
for (int i = 0; i < efsd->get_subdir_count(); i++) {
_get_owners(efsd->get_subdir(i), p_path, owners);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
Vector<String> deps = efsd->get_file_deps(i);
bool found = false;
for (int j = 0; j < deps.size(); j++) {
if (deps[j] == p_path) {
found = true;
break;
}
}
if (!found)
continue;
owners.push_back(efsd->get_file_path(i));
}
}
Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
Node *owner_scene_node = NULL;
List<String> owners;
_get_owners(EditorFileSystem::get_singleton()->get_filesystem(), p_path, owners);
for (int i = 0; i < owners.size(); i++) {
NodePath owner_path = owners[i];
RES owner_res = ResourceLoader::load(owner_path);
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
owner_scene_node = owner_packed_scene->instance();
break;
}
}
return owner_scene_node;
}
void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) {
String path = get_file_path(p_params.textDocument.uri);
@ -380,8 +427,12 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
bool forced = false;
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
Node *owner_scene_node = _get_owner_scene_node(path);
String code = parser->get_text_for_completion(p_params.position);
GDScriptLanguage::get_singleton()->complete_code(code, path, NULL, r_options, forced, call_hint);
GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint);
if (owner_scene_node) {
memdelete(owner_scene_node);
}
}
}

View file

@ -33,12 +33,17 @@
#include "../gdscript_parser.h"
#include "core/variant.h"
#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
class GDScriptWorkspace : public Reference {
GDCLASS(GDScriptWorkspace, Reference);
private:
void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners);
Node *_get_owner_scene_node(String p_path);
protected:
static void _bind_methods();
void remove_cache_parser(const String &p_path);

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GDSCRIPT_REGISTER_TYPES_H
#define GDSCRIPT_REGISTER_TYPES_H
void register_gdscript_types();
void unregister_gdscript_types();
#endif // GDSCRIPT_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GRIDMAP_REGISTER_TYPES_H
#define GRIDMAP_REGISTER_TYPES_H
void register_gridmap_types();
void unregister_gridmap_types();
#endif // GRIDMAP_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef HDR_REGISTER_TYPES_H
#define HDR_REGISTER_TYPES_H
void register_hdr_types();
void unregister_hdr_types();
#endif // HDR_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef JPG_REGISTER_TYPES_H
#define JPG_REGISTER_TYPES_H
void register_jpg_types();
void unregister_jpg_types();
#endif // JPG_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef JSONRPC_REGISTER_TYPES_H
#define JSONRPC_REGISTER_TYPES_H
void register_jsonrpc_types();
void unregister_jsonrpc_types();
#endif // JSONRPC_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MBEDTLS_REGISTER_TYPES_H
#define MBEDTLS_REGISTER_TYPES_H
void register_mbedtls_types();
void unregister_mbedtls_types();
#endif // MBEDTLS_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MOBILE_VR_REGISTER_TYPES_H
#define MOBILE_VR_REGISTER_TYPES_H
void register_mobile_vr_types();
void unregister_mobile_vr_types();
#endif // MOBILE_VR_REGISTER_TYPES_H

View file

@ -9,7 +9,7 @@ def configure(env):
env.use_ptrcall = True
env.add_module_version_string('mono')
from SCons.Script import BoolVariable, PathVariable, Variables
from SCons.Script import BoolVariable, PathVariable, Variables, Help
envvars = Variables()
envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept))
@ -18,6 +18,7 @@ def configure(env):
envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
envvars.Update(env)
Help(envvars.GenerateHelpText(env))
if env['platform'] == 'javascript':
# Mono wasm already has zlib builtin, so we need this workaround to avoid symbol collisions

View file

@ -1,6 +1,8 @@
using GodotTools.Core;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace GodotTools.ProjectEditor
{
@ -118,5 +120,40 @@ EndProject";
const string ProjectPlatformsConfig =
@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
public static void MigrateFromOldConfigNames(string slnPath)
{
if (!File.Exists(slnPath))
return;
var input = File.ReadAllText(slnPath);
if (!Regex.IsMatch(input, Regex.Escape("Tools|Any CPU")))
return;
// This method renames old configurations in solutions to the new ones.
//
// This is the order configs appear in the solution and what we want to rename them to:
// Debug|Any CPU = Debug|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
// Tools|Any CPU = Tools|Any CPU -> Debug|Any CPU = Debug|Any CPU
//
// But we want to move Tools (now Debug) to the top, so it's easier to rename like this:
// Debug|Any CPU = Debug|Any CPU -> Debug|Any CPU = Debug|Any CPU
// Release|Any CPU = Release|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
// Tools|Any CPU = Tools|Any CPU -> ExportRelease|Any CPU = ExportRelease|Any CPU
var dict = new Dictionary<string, string>
{
{"Debug|Any CPU", "Debug|Any CPU"},
{"Release|Any CPU", "ExportDebug|Any CPU"},
{"Tools|Any CPU", "ExportRelease|Any CPU"}
};
var regex = new Regex(string.Join("|",dict.Keys.Select(Regex.Escape)));
var result = regex.Replace(input,m => dict[m.Value]);
if (result != input)
File.WriteAllText(slnPath, result);
}
}
}

View file

@ -17,30 +17,30 @@ namespace GodotTools.ProjectEditor
string path = Path.Combine(dir, name + ".csproj");
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(name, "Tools", out mainGroup);
var root = CreateLibraryProject(name, "Debug", out mainGroup);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'Release' ";
mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'Release' ";
mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'ExportRelease' ";
mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'ExportRelease' ";
var toolsGroup = root.AddPropertyGroup();
toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
toolsGroup.AddProperty("DebugSymbols", "true");
toolsGroup.AddProperty("DebugType", "portable");
toolsGroup.AddProperty("Optimize", "false");
toolsGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
toolsGroup.AddProperty("ErrorReport", "prompt");
toolsGroup.AddProperty("WarningLevel", "4");
toolsGroup.AddProperty("ConsolePause", "false");
var debugGroup = root.AddPropertyGroup();
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
debugGroup.AddProperty("DebugSymbols", "true");
debugGroup.AddProperty("DebugType", "portable");
debugGroup.AddProperty("Optimize", "false");
debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
debugGroup.AddProperty("ErrorReport", "prompt");
debugGroup.AddProperty("WarningLevel", "4");
debugGroup.AddProperty("ConsolePause", "false");
var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
coreApiRef.AddMetadata("Private", "False");
var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
editorApiRef.Condition = " '$(Configuration)' == 'Debug' ";
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
editorApiRef.AddMetadata("Private", "False");
@ -103,24 +103,24 @@ namespace GodotTools.ProjectEditor
mainGroup.AddProperty("TargetFrameworkVersion", "v4.7");
mainGroup.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
var debugGroup = root.AddPropertyGroup();
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
debugGroup.AddProperty("DebugSymbols", "true");
debugGroup.AddProperty("DebugType", "portable");
debugGroup.AddProperty("Optimize", "false");
debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
debugGroup.AddProperty("ErrorReport", "prompt");
debugGroup.AddProperty("WarningLevel", "4");
debugGroup.AddProperty("ConsolePause", "false");
var exportDebugGroup = root.AddPropertyGroup();
exportDebugGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportDebug|AnyCPU' ";
exportDebugGroup.AddProperty("DebugSymbols", "true");
exportDebugGroup.AddProperty("DebugType", "portable");
exportDebugGroup.AddProperty("Optimize", "false");
exportDebugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
exportDebugGroup.AddProperty("ErrorReport", "prompt");
exportDebugGroup.AddProperty("WarningLevel", "4");
exportDebugGroup.AddProperty("ConsolePause", "false");
var releaseGroup = root.AddPropertyGroup();
releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
releaseGroup.AddProperty("DebugType", "portable");
releaseGroup.AddProperty("Optimize", "true");
releaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
releaseGroup.AddProperty("ErrorReport", "prompt");
releaseGroup.AddProperty("WarningLevel", "4");
releaseGroup.AddProperty("ConsolePause", "false");
var exportReleaseGroup = root.AddPropertyGroup();
exportReleaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportRelease|AnyCPU' ";
exportReleaseGroup.AddProperty("DebugType", "portable");
exportReleaseGroup.AddProperty("Optimize", "true");
exportReleaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
exportReleaseGroup.AddProperty("ErrorReport", "prompt");
exportReleaseGroup.AddProperty("WarningLevel", "4");
exportReleaseGroup.AddProperty("ConsolePause", "false");
// References
var referenceGroup = root.AddItemGroup();

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using DotNet.Globbing;
using Microsoft.Build.Construction;
@ -117,6 +118,7 @@ namespace GodotTools.ProjectEditor
globOptions.Evaluation.CaseInsensitive = false;
var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null);
foreach (var itemGroup in root.ItemGroups)
{
@ -158,35 +160,35 @@ namespace GodotTools.ProjectEditor
void AddPropertyIfNotPresent(string name, string condition, string value)
{
if (root.PropertyGroups
.Any(g => (g.Condition == string.Empty || g.Condition == condition) &&
.Any(g => (g.Condition == string.Empty || g.Condition.Trim() == condition) &&
g.Properties
.Any(p => p.Name == name &&
p.Value == value &&
(p.Condition == condition || g.Condition == condition))))
(p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
{
return;
}
root.AddProperty(name, value).Condition = condition;
root.AddProperty(name, value).Condition = " " + condition + " ";
dirty = true;
}
AddPropertyIfNotPresent(name: "ApiConfiguration",
condition: " '$(Configuration)' != 'Release' ",
condition: "'$(Configuration)' != 'ExportRelease'",
value: "Debug");
AddPropertyIfNotPresent(name: "ApiConfiguration",
condition: " '$(Configuration)' == 'Release' ",
condition: "'$(Configuration)' == 'ExportRelease'",
value: "Release");
void SetReferenceHintPath(string referenceName, string condition, string hintPath)
{
foreach (var itemGroup in root.ItemGroups.Where(g =>
g.Condition == string.Empty || g.Condition == condition))
g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
{
var references = itemGroup.Items.Where(item =>
item.ItemType == "Reference" &&
item.Include == referenceName &&
(item.Condition == condition || itemGroup.Condition == condition));
(item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));
var referencesWithHintPath = references.Where(reference =>
reference.Metadata.Any(m => m.Name == "HintPath"));
@ -225,7 +227,7 @@ namespace GodotTools.ProjectEditor
}
// Found no Reference item at all. Add it.
root.AddItem("Reference", referenceName).Condition = condition;
root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
dirty = true;
}
@ -233,7 +235,7 @@ namespace GodotTools.ProjectEditor
const string editorProjectName = "GodotSharpEditor";
const string coreCondition = "";
const string editorCondition = " '$(Configuration)' == 'Tools' ";
const string editorCondition = "'$(Configuration)' == 'Debug'";
var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";
@ -241,6 +243,105 @@ namespace GodotTools.ProjectEditor
SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
if (dirty)
root.Save();
}
public static void MigrateFromOldConfigNames(string projectPath)
{
var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null);
bool dirty = false;
bool hasGodotProjectGeneratorVersion = false;
bool foundOldConfiguration = false;
foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition == string.Empty))
{
if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
hasGodotProjectGeneratorVersion = true;
foreach (var configItem in propertyGroup.Properties
.Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
{
configItem.Value = "Debug";
foundOldConfiguration = true;
dirty = true;
}
}
if (!hasGodotProjectGeneratorVersion)
{
root.PropertyGroups.First(g => g.Condition == string.Empty)?
.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
dirty = true;
}
if (!foundOldConfiguration)
{
var toolsConditions = new[]
{
"'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
"'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
"'$(Configuration)' == 'Tools'",
"'$(Configuration)' != 'Tools'"
};
foundOldConfiguration = root.PropertyGroups
.Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
}
if (foundOldConfiguration)
{
void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
{
void MigrateConditions(string oldCondition, string newCondition)
{
foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
{
propertyGroup.Condition = " " + newCondition + " ";
dirty = true;
}
foreach (var propertyGroup in root.PropertyGroups)
{
foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
{
prop.Condition = " " + newCondition + " ";
dirty = true;
}
}
foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
{
itemGroup.Condition = " " + newCondition + " ";
dirty = true;
}
foreach (var itemGroup in root.ItemGroups)
{
foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
{
item.Condition = " " + newCondition + " ";
dirty = true;
}
}
}
foreach (var op in new[] {"==", "!="})
{
MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
}
}
MigrateConfigurationConditions("Debug", "ExportDebug");
MigrateConfigurationConditions("Release", "ExportRelease");
MigrateConfigurationConditions("Tools", "Debug"); // Must be last
}
if (dirty)
root.Save();
}

View file

@ -166,7 +166,7 @@ namespace GodotTools
Internal.GodotIs32Bits() ? "32" : "64"
};
bool buildSuccess = BuildManager.BuildProjectBlocking("Tools", godotDefines);
bool buildSuccess = BuildManager.BuildProjectBlocking("Debug", godotDefines);
if (!buildSuccess)
return;

View file

@ -166,7 +166,7 @@ namespace GodotTools
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "Release" ? "Release" : "Debug");
string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
@ -242,7 +242,7 @@ namespace GodotTools
Internal.GodotIs32Bits() ? "32" : "64"
};
return BuildProjectBlocking("Tools", godotDefines);
return BuildProjectBlocking("Debug", godotDefines);
}
public static void Initialize()
@ -251,12 +251,12 @@ namespace GodotTools
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var msbuild = BuildTool.MsBuildMono;
if (OS.IsWindows)
msbuild = RiderPathManager.IsRider((string) editorSettings.GetSetting(RiderPathManager.EditorPathSettingName))
msbuild = RiderPathManager.IsExternalEditorSetToRider(editorSettings)
? BuildTool.JetBrainsMsBuild
: BuildTool.MsBuildVs;
EditorDef("mono/builds/build_tool", msbuild);
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Godot.Variant.Type.Int,

View file

@ -32,18 +32,6 @@ namespace GodotTools
ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
}
public static void FixApiHintPath(string projectPath)
{
try
{
ProjectUtils.FixApiHintPath(projectPath);
}
catch (Exception e)
{
GD.PushError(e.ToString());
}
}
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static ulong ConvertToTimestamp(this DateTime value)

View file

@ -150,7 +150,7 @@ namespace GodotTools.Export
string outputDir = new FileInfo(path).Directory?.FullName ??
throw new FileNotFoundException("Base directory not found");
string buildConfig = isDebug ? "Debug" : "Release";
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);

View file

@ -62,7 +62,7 @@ namespace GodotTools
{
Guid = guid,
PathRelativeToSolution = name + ".csproj",
Configs = new List<string> {"Debug", "Release", "Tools"}
Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
solution.AddNewProject(name, projectInfo);
@ -437,8 +437,22 @@ namespace GodotTools
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
// Make sure the existing project has Api assembly references configured correctly
CsProjOperations.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
try
{
// Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
// Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath);
// Apply the other fixes after configurations are migrated
// Make sure the existing project has Api assembly references configured correctly
ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
}
catch (Exception e)
{
GD.PushError(e.ToString());
}
}
else
{

View file

@ -55,6 +55,11 @@ namespace GodotTools.Ides.Rider
}
}
public static bool IsExternalEditorSetToRider(EditorSettings editorSettings)
{
return editorSettings.HasSetting(EditorPathSettingName) && IsRider((string) editorSettings.GetSetting(EditorPathSettingName));
}
public static bool IsRider(string path)
{
if (string.IsNullOrEmpty(path))

View file

@ -8,8 +8,6 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

View file

@ -3,6 +3,9 @@
Import('env')
Import('env_modules')
# Only kept to build the thirdparty library used by the theora and webm
# modules.
env_ogg = env_modules.Clone()
# Thirdparty source files

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OGG_REGISTER_TYPES_H
#define OGG_REGISTER_TYPES_H
void register_ogg_types();
void unregister_ogg_types();
#endif // OGG_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OPENSIMPLEX_REGISTER_TYPES_H
#define OPENSIMPLEX_REGISTER_TYPES_H
void register_opensimplex_types();
void unregister_opensimplex_types();
#endif // OPENSIMPLEX_REGISTER_TYPES_H

View file

@ -3,7 +3,9 @@
Import('env')
Import('env_modules')
stub = True
# Only kept to build the thirdparty library used by the webm module.
# AudioStreamOpus was dropped in 3.0 due to incompatibility with the new audio
# engine. If you want to port it, fetch it from the Git history.
env_opus = env_modules.Clone()
@ -235,9 +237,5 @@ if env['builtin_opus']:
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
if not stub:
# Module files
env_opus.add_source_files(env.modules_sources, "*.cpp")
else:
# Module files
env_opus.add_source_files(env.modules_sources, "stub/register_types.cpp")
# Module files
env_opus.add_source_files(env.modules_sources, "register_types.cpp")

View file

@ -1,379 +0,0 @@
/*************************************************************************/
/* audio_stream_opus.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
#include "audio_stream_opus.h"
/**
@author George Marques <george@gmarqu.es>
*/
const float AudioStreamPlaybackOpus::osrate = 48000.0f;
int AudioStreamPlaybackOpus::_op_read_func(void *_stream, unsigned char *_ptr, int _nbytes) {
FileAccess *fa = (FileAccess *)_stream;
if (fa->eof_reached())
return 0;
uint8_t *dst = (uint8_t *)_ptr;
int read = fa->get_buffer(dst, _nbytes);
return read;
}
int AudioStreamPlaybackOpus::_op_seek_func(void *_stream, opus_int64 _offset, int _whence) {
#ifdef SEEK_SET
FileAccess *fa = (FileAccess *)_stream;
switch (_whence) {
case SEEK_SET: {
fa->seek(_offset);
} break;
case SEEK_CUR: {
fa->seek(fa->get_position() + _offset);
} break;
case SEEK_END: {
fa->seek_end(_offset);
} break;
default: {
ERR_PRINT("Opus seek function failure: Unexpected value in _whence\n");
}
}
int ret = fa->eof_reached() ? -1 : 0;
return ret;
#else
return -1; // no seeking
#endif
}
int AudioStreamPlaybackOpus::_op_close_func(void *_stream) {
if (!_stream)
return 0;
FileAccess *fa = (FileAccess *)_stream;
if (fa->is_open())
fa->close();
return 0;
}
opus_int64 AudioStreamPlaybackOpus::_op_tell_func(void *_stream) {
FileAccess *_fa = (FileAccess *)_stream;
return (opus_int64)_fa->get_position();
}
void AudioStreamPlaybackOpus::_clear_stream() {
if (!stream_loaded)
return;
op_free(opus_file);
_close_file();
stream_loaded = false;
stream_channels = 1;
playing = false;
}
void AudioStreamPlaybackOpus::_close_file() {
if (f) {
memdelete(f);
f = NULL;
}
}
Error AudioStreamPlaybackOpus::_load_stream() {
ERR_FAIL_COND_V(!stream_valid, ERR_UNCONFIGURED);
_clear_stream();
if (file == "")
return ERR_INVALID_DATA;
Error err;
f = FileAccess::open(file, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'.");
int _err = 0;
opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &_err);
switch (_err) {
case OP_EREAD: { // - Can't read the file.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CANT_READ);
} break;
case OP_EVERSION: // - Unrecognized version number.
case OP_ENOTFORMAT: // - Stream is not Opus data.
case OP_EIMPL: { // - Stream used non-implemented feature.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
} break;
case OP_EBADLINK: // - Failed to find old data after seeking.
case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks.
case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_BUG);
} break;
}
repeats = 0;
stream_loaded = true;
return OK;
}
AudioStreamPlaybackOpus::AudioStreamPlaybackOpus() {
loops = false;
playing = false;
f = NULL;
stream_loaded = false;
stream_valid = false;
repeats = 0;
paused = true;
stream_channels = 0;
current_section = 0;
length = 0;
loop_restart_time = 0;
pre_skip = 0;
_op_callbacks.read = _op_read_func;
_op_callbacks.seek = _op_seek_func;
_op_callbacks.tell = _op_tell_func;
_op_callbacks.close = _op_close_func;
}
Error AudioStreamPlaybackOpus::set_file(const String &p_file) {
file = p_file;
stream_valid = false;
Error err;
f = FileAccess::open(file, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'.");
int _err;
opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &_err);
switch (_err) {
case OP_EREAD: { // - Can't read the file.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CANT_READ);
} break;
case OP_EVERSION: // - Unrecognized version number.
case OP_ENOTFORMAT: // - Stream is not Opus data.
case OP_EIMPL: { // - Stream used non-implemented feature.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
} break;
case OP_EBADLINK: // - Failed to find old data after seeking.
case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks.
case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_BUG);
} break;
}
const OpusHead *oinfo = op_head(opus_file, -1);
stream_channels = oinfo->channel_count;
pre_skip = oinfo->pre_skip;
frames_mixed = pre_skip;
ogg_int64_t len = op_pcm_total(opus_file, -1);
if (len < 0) {
length = 0;
} else {
length = (len / osrate);
}
op_free(opus_file);
memdelete(f);
f = NULL;
stream_valid = true;
return OK;
}
void AudioStreamPlaybackOpus::play(float p_from) {
if (playing)
stop();
if (_load_stream() != OK)
return;
frames_mixed = pre_skip;
playing = true;
if (p_from > 0) {
seek(p_from);
}
}
void AudioStreamPlaybackOpus::stop() {
_clear_stream();
playing = false;
}
void AudioStreamPlaybackOpus::seek(float p_time) {
if (!playing) return;
ogg_int64_t pcm_offset = (ogg_int64_t)(p_time * osrate);
bool ok = op_pcm_seek(opus_file, pcm_offset) == 0;
if (!ok) {
ERR_PRINT("Seek time over stream size.");
return;
}
frames_mixed = osrate * p_time;
}
int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) {
if (!playing)
return 0;
int total = p_frames;
while (true) {
int todo = p_frames;
if (todo < MIN_MIX) {
break;
}
int ret = op_read(opus_file, (opus_int16 *)p_buffer, todo * stream_channels, &current_section);
if (ret < 0) {
playing = false;
ERR_BREAK_MSG(ret < 0, "Error reading Opus file: " + file + ".");
} else if (ret == 0) { // end of song, reload?
op_free(opus_file);
_close_file();
f = FileAccess::open(file, FileAccess::READ);
int errv = 0;
opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &errv);
if (errv != 0) {
playing = false;
break; // :(
}
if (!has_loop()) {
playing = false;
repeats = 1;
break;
}
if (loop_restart_time) {
bool ok = op_pcm_seek(opus_file, (loop_restart_time * osrate) + pre_skip) == 0;
if (!ok) {
playing = false;
ERR_PRINT("Loop restart time rejected");
}
frames_mixed = (loop_restart_time * osrate) + pre_skip;
} else {
frames_mixed = pre_skip;
}
repeats++;
continue;
}
stream_channels = op_head(opus_file, current_section)->channel_count;
frames_mixed += ret;
p_buffer += ret * stream_channels;
p_frames -= ret;
}
return total - p_frames;
}
float AudioStreamPlaybackOpus::get_length() const {
if (!stream_loaded) {
if (const_cast<AudioStreamPlaybackOpus *>(this)->_load_stream() != OK)
return 0;
}
return length;
}
float AudioStreamPlaybackOpus::get_playback_position() const {
int32_t frames = int32_t(frames_mixed);
if (frames < 0)
frames = 0;
return double(frames) / osrate;
}
int AudioStreamPlaybackOpus::get_minimum_buffer_size() const {
return MIN_MIX;
}
AudioStreamPlaybackOpus::~AudioStreamPlaybackOpus() {
_clear_stream();
}
RES ResourceFormatLoaderAudioStreamOpus::load(const String &p_path, const String &p_original_path, Error *r_error) {
if (r_error)
*r_error = OK;
AudioStreamOpus *opus_stream = memnew(AudioStreamOpus);
opus_stream->set_file(p_path);
return Ref<AudioStreamOpus>(opus_stream);
}
void ResourceFormatLoaderAudioStreamOpus::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("opus");
}
String ResourceFormatLoaderAudioStreamOpus::get_resource_type(const String &p_path) const {
if (p_path.get_extension().to_lower() == "opus")
return "AudioStreamOpus";
return "";
}
bool ResourceFormatLoaderAudioStreamOpus::handles_type(const String &p_type) const {
return (p_type == "AudioStream" || p_type == "AudioStreamOpus");
}

View file

@ -1,142 +0,0 @@
/*************************************************************************/
/* audio_stream_opus.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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 AUDIO_STREAM_OPUS_H
#define AUDIO_STREAM_OPUS_H
#include "core/io/resource_loader.h"
#include "core/os/file_access.h"
#include "scene/resources/audio_stream.h"
#include <opus/opusfile.h>
/**
@author George Marques <george@gmarqu.es>
*/
class AudioStreamPlaybackOpus : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback);
enum {
MIN_MIX = 1024
};
FileAccess *f;
OpusFileCallbacks _op_callbacks;
float length;
static int _op_read_func(void *_stream, unsigned char *_ptr, int _nbytes);
static int _op_seek_func(void *_stream, opus_int64 _offset, int _whence);
static int _op_close_func(void *_stream);
static opus_int64 _op_tell_func(void *_stream);
static const float osrate;
String file;
int64_t frames_mixed;
bool stream_loaded;
volatile bool playing;
OggOpusFile *opus_file;
int stream_channels;
int current_section;
int pre_skip;
bool paused;
bool loops;
int repeats;
Error _load_stream();
void _clear_stream();
void _close_file();
bool stream_valid;
float loop_restart_time;
public:
Error set_file(const String &p_file);
virtual void play(float p_from = 0);
virtual void stop();
virtual bool is_playing() const { return playing; }
virtual void set_loop_restart_time(float p_time) { loop_restart_time = p_time; }
virtual void set_paused(bool p_paused) { paused = p_paused; }
virtual bool is_paused() const { return paused; }
virtual void set_loop(bool p_enable) { loops = p_enable; }
virtual bool has_loop() const { return loops; }
virtual float get_length() const;
virtual String get_stream_name() const { return ""; }
virtual int get_loop_count() const { return repeats; }
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual int get_channels() const { return stream_channels; }
virtual int get_mix_rate() const { return osrate; }
virtual int get_minimum_buffer_size() const;
virtual int mix(int16_t *p_buffer, int p_frames);
AudioStreamPlaybackOpus();
~AudioStreamPlaybackOpus();
};
class AudioStreamOpus : public AudioStream {
GDCLASS(AudioStreamOpus, AudioStream);
String file;
public:
Ref<AudioStreamPlayback> instance_playback() {
Ref<AudioStreamPlaybackOpus> pb = memnew(AudioStreamPlaybackOpus);
pb->set_file(file);
return pb;
}
void set_file(const String &p_file) { file = p_file; }
};
class ResourceFormatLoaderAudioStreamOpus : public ResourceFormatLoader {
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
#endif // AUDIO_STREAM_OPUS_H

View file

@ -3,11 +3,3 @@ def can_build(env, platform):
def configure(env):
pass
def get_doc_classes():
return [
"AudioStreamOpus",
]
def get_doc_path():
return "doc_classes"

View file

@ -30,23 +30,8 @@
#include "register_types.h"
#include "audio_stream_opus.h"
// Dummy module as libvorbis is needed by other modules (theora ...)
static Ref<ResourceFormatLoaderAudioStreamOpus> opus_stream_loader;
void register_opus_types() {}
void register_opus_types() {
// Sorry guys, do not enable this unless you can figure out a way
// to get Opus to not do any memory allocation or system calls
// in the audio thread.
// Currently the implementation even reads files from the audio thread,
// and this is not how audio programming works.
//opus_stream_loader = memnew(ResourceFormatLoaderAudioStreamOpus);
//ResourceLoader::add_resource_format_loader(opus_stream_loader);
//ClassDB::register_class<AudioStreamOpus>();
}
void unregister_opus_types() {
//memdelete(opus_stream_loader);
}
void unregister_opus_types() {}

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OPUS_REGISTER_TYPES_H
#define OPUS_REGISTER_TYPES_H
void register_opus_types();
void unregister_opus_types();
#endif // OPUS_REGISTER_TYPES_H

View file

@ -1,37 +0,0 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
#include "register_types.h"
// Dummy module as libvorbis is needed by other modules (theora ...)
void register_opus_types() {}
void unregister_opus_types() {}

View file

@ -1,32 +0,0 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
void register_opus_types();
void unregister_opus_types();

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PVR_REGISTER_TYPES_H
#define PVR_REGISTER_TYPES_H
void register_pvr_types();
void unregister_pvr_types();
#endif // PVR_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RECAST_REGISTER_TYPES_H
#define RECAST_REGISTER_TYPES_H
void register_recast_types();
void unregister_recast_types();
#endif // RECAST_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef REGEX_REGISTER_TYPES_H
#define REGEX_REGISTER_TYPES_H
void register_regex_types();
void unregister_regex_types();
#endif // REGEX_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SQUISH_REGISTER_TYPES_H
#define SQUISH_REGISTER_TYPES_H
void register_squish_types();
void unregister_squish_types();
#endif // SQUISH_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef STB_VORBIS_REGISTER_TYPES_H
#define STB_VORBIS_REGISTER_TYPES_H
void register_stb_vorbis_types();
void unregister_stb_vorbis_types();
#endif // STB_VORBIS_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SVG_REGISTER_TYPES_H
#define SVG_REGISTER_TYPES_H
void register_svg_types();
void unregister_svg_types();
#endif // SVG_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TGA_REGISTER_TYPES_H
#define TGA_REGISTER_TYPES_H
void register_tga_types();
void unregister_tga_types();
#endif // TGA_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef THEORA_REGISTER_TYPES_H
#define THEORA_REGISTER_TYPES_H
void register_theora_types();
void unregister_theora_types();
#endif // THEORA_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TINYEXR_REGISTER_TYPES_H
#define TINYEXR_REGISTER_TYPES_H
void register_tinyexr_types();
void unregister_tinyexr_types();
#endif // TINYEXR_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef UPNP_REGISTER_TYPES_H
#define UPNP_REGISTER_TYPES_H
void register_upnp_types();
void unregister_upnp_types();
#endif // UPNP_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef VHACD_REGISTER_TYPES_H
#define VHACD_REGISTER_TYPES_H
void register_vhacd_types();
void unregister_vhacd_types();
#endif // VHACD_REGISTER_TYPES_H

View file

@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef VISUAL_SCRIPT_REGISTER_TYPES_H
#define VISUAL_SCRIPT_REGISTER_TYPES_H
void register_visual_script_types();
void unregister_visual_script_types();
#endif // VISUAL_SCRIPT_REGISTER_TYPES_H

View file

@ -3,6 +3,9 @@
Import('env')
Import('env_modules')
# Only kept to build the thirdparty library used by the theora and webm
# modules. We now use stb_vorbis for AudioStreamOGGVorbis.
env_vorbis = env_modules.Clone()
stub = True
@ -50,9 +53,5 @@ if env['builtin_libvorbis']:
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
if not stub:
# Module files
env_vorbis.add_source_files(env.modules_sources, "*.cpp")
else:
# Module files
env_vorbis.add_source_files(env.modules_sources, "stub/register_types.cpp")
# Module files
env_vorbis.add_source_files(env.modules_sources, "register_types.cpp")

View file

@ -1,406 +0,0 @@
/*************************************************************************/
/* audio_stream_ogg_vorbis.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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. */
/*************************************************************************/
#include "audio_stream_ogg_vorbis.h"
size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst, size_t p_data, size_t p_count, void *_f) {
//printf("read to %p, %i bytes, %i nmemb, %p\n",p_dst,p_data,p_count,_f);
FileAccess *fa = (FileAccess *)_f;
size_t read_total = p_data * p_count;
if (fa->eof_reached())
return 0;
uint8_t *dst = (uint8_t *)p_dst;
int read = fa->get_buffer(dst, read_total);
return read;
}
int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f, ogg_int64_t offs, int whence) {
//printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
#ifdef SEEK_SET
//printf("seek set defined\n");
FileAccess *fa = (FileAccess *)_f;
if (whence == SEEK_SET) {
fa->seek(offs);
} else if (whence == SEEK_CUR) {
fa->seek(fa->get_position() + offs);
} else if (whence == SEEK_END) {
fa->seek_end(offs);
} else {
ERR_PRINT("Vorbis seek function failure: Unexpected value in _whence\n");
}
int ret = fa->eof_reached() ? -1 : 0;
//printf("returning %i\n",ret);
return ret;
#else
return -1; // no seeking
#endif
}
int AudioStreamPlaybackOGGVorbis::_ov_close_func(void *_f) {
//printf("close %p\n",_f);
if (!_f)
return 0;
FileAccess *fa = (FileAccess *)_f;
if (fa->is_open())
fa->close();
return 0;
}
long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) {
//printf("close %p\n",_f);
FileAccess *fa = (FileAccess *)_f;
return fa->get_position();
}
int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) {
if (!playing)
return 0;
int total = p_frames;
while (true) {
int todo = p_frames;
if (todo < MIN_MIX) {
break;
}
#ifdef BIG_ENDIAN_ENABLED
long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, &current_section);
#else
long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, &current_section);
#endif
if (ret < 0) {
playing = false;
ERR_BREAK_MSG(ret < 0, "Error reading OGG Vorbis file: " + file + ".");
} else if (ret == 0) { // end of song, reload?
ov_clear(&vf);
_close_file();
if (!has_loop()) {
playing = false;
repeats = 1;
break;
}
f = FileAccess::open(file, FileAccess::READ);
int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks);
if (errv != 0) {
playing = false;
break; // :(
}
if (loop_restart_time) {
bool ok = ov_time_seek(&vf, loop_restart_time) == 0;
if (!ok) {
playing = false;
ERR_PRINT("Loop restart time rejected");
}
frames_mixed = stream_srate * loop_restart_time;
} else {
frames_mixed = 0;
}
repeats++;
continue;
}
ret /= stream_channels;
ret /= sizeof(int16_t);
frames_mixed += ret;
p_buffer += ret * stream_channels;
p_frames -= ret;
}
return total - p_frames;
}
void AudioStreamPlaybackOGGVorbis::play(float p_from) {
if (playing)
stop();
if (_load_stream() != OK)
return;
frames_mixed = 0;
playing = true;
if (p_from > 0) {
seek(p_from);
}
}
void AudioStreamPlaybackOGGVorbis::_close_file() {
if (f) {
memdelete(f);
f = NULL;
}
}
bool AudioStreamPlaybackOGGVorbis::is_playing() const {
return playing;
}
void AudioStreamPlaybackOGGVorbis::stop() {
_clear_stream();
playing = false;
//_clear();
}
float AudioStreamPlaybackOGGVorbis::get_playback_position() const {
int32_t frames = int32_t(frames_mixed);
if (frames < 0)
frames = 0;
return double(frames) / stream_srate;
}
void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
if (!playing)
return;
bool ok = ov_time_seek(&vf, p_time) == 0;
ERR_FAIL_COND(!ok);
frames_mixed = stream_srate * p_time;
}
String AudioStreamPlaybackOGGVorbis::get_stream_name() const {
return "";
}
void AudioStreamPlaybackOGGVorbis::set_loop(bool p_enable) {
loops = p_enable;
}
bool AudioStreamPlaybackOGGVorbis::has_loop() const {
return loops;
}
int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
return repeats;
}
Error AudioStreamPlaybackOGGVorbis::set_file(const String &p_file) {
file = p_file;
stream_valid = false;
Error err;
f = FileAccess::open(file, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_file + "'.");
int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks);
switch (errv) {
case OV_EREAD: { // - A read from media returned an error.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CANT_READ);
} break;
case OV_EVERSION: // - Vorbis version mismatch.
case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
} break;
case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_BUG);
} break;
}
const vorbis_info *vinfo = ov_info(&vf, -1);
stream_channels = vinfo->channels;
stream_srate = vinfo->rate;
length = ov_time_total(&vf, -1);
ov_clear(&vf);
memdelete(f);
f = NULL;
stream_valid = true;
return OK;
}
Error AudioStreamPlaybackOGGVorbis::_load_stream() {
ERR_FAIL_COND_V(!stream_valid, ERR_UNCONFIGURED);
_clear_stream();
if (file == "")
return ERR_INVALID_DATA;
Error err;
f = FileAccess::open(file, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + file + "'.");
int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks);
switch (errv) {
case OV_EREAD: { // - A read from media returned an error.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CANT_READ);
} break;
case OV_EVERSION: // - Vorbis version mismatch.
case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
} break;
case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
memdelete(f);
f = NULL;
ERR_FAIL_V(ERR_BUG);
} break;
}
repeats = 0;
stream_loaded = true;
return OK;
}
float AudioStreamPlaybackOGGVorbis::get_length() const {
if (!stream_loaded) {
if (const_cast<AudioStreamPlaybackOGGVorbis *>(this)->_load_stream() != OK)
return 0;
}
return length;
}
void AudioStreamPlaybackOGGVorbis::_clear_stream() {
if (!stream_loaded)
return;
ov_clear(&vf);
_close_file();
stream_loaded = false;
//stream_channels=1;
playing = false;
}
void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) {
paused = p_paused;
}
bool AudioStreamPlaybackOGGVorbis::is_paused() const {
return paused;
}
AudioStreamPlaybackOGGVorbis::AudioStreamPlaybackOGGVorbis() {
loops = false;
playing = false;
_ov_callbacks.read_func = _ov_read_func;
_ov_callbacks.seek_func = _ov_seek_func;
_ov_callbacks.close_func = _ov_close_func;
_ov_callbacks.tell_func = _ov_tell_func;
f = NULL;
stream_loaded = false;
stream_valid = false;
repeats = 0;
paused = true;
stream_channels = 0;
stream_srate = 0;
current_section = 0;
length = 0;
loop_restart_time = 0;
}
AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
_clear_stream();
}
RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String &p_original_path, Error *r_error) {
if (r_error)
*r_error = OK;
AudioStreamOGGVorbis *ogg_stream = memnew(AudioStreamOGGVorbis);
ogg_stream->set_file(p_path);
return Ref<AudioStreamOGGVorbis>(ogg_stream);
}
void ResourceFormatLoaderAudioStreamOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ogg");
}
String ResourceFormatLoaderAudioStreamOGGVorbis::get_resource_type(const String &p_path) const {
if (p_path.get_extension().to_lower() == "ogg")
return "AudioStreamOGGVorbis";
return "";
}
bool ResourceFormatLoaderAudioStreamOGGVorbis::handles_type(const String &p_type) const {
return (p_type == "AudioStream" || p_type == "AudioStreamOGG" || p_type == "AudioStreamOGGVorbis");
}

View file

@ -1,137 +0,0 @@
/*************************************************************************/
/* audio_stream_ogg_vorbis.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 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 AUDIO_STREAM_OGG_VORBIS_H
#define AUDIO_STREAM_OGG_VORBIS_H
#include "core/io/resource_loader.h"
#include "core/os/file_access.h"
#include "core/os/thread_safe.h"
#include "scene/resources/audio_stream.h"
#include <vorbis/vorbisfile.h>
class AudioStreamPlaybackOGGVorbis : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlayback);
enum {
MIN_MIX = 1024
};
FileAccess *f;
ov_callbacks _ov_callbacks;
float length;
static size_t _ov_read_func(void *p_dst, size_t p_data, size_t p_count, void *_f);
static int _ov_seek_func(void *_f, ogg_int64_t offs, int whence);
static int _ov_close_func(void *_f);
static long _ov_tell_func(void *_f);
String file;
int64_t frames_mixed;
bool stream_loaded;
volatile bool playing;
OggVorbis_File vf;
int stream_channels;
int stream_srate;
int current_section;
bool paused;
bool loops;
int repeats;
Error _load_stream();
void _clear_stream();
void _close_file();
bool stream_valid;
float loop_restart_time;
public:
Error set_file(const String &p_file);
virtual void play(float p_from = 0);
virtual void stop();
virtual bool is_playing() const;
virtual void set_loop_restart_time(float p_time) { loop_restart_time = p_time; }
virtual void set_paused(bool p_paused);
virtual bool is_paused() const;
virtual void set_loop(bool p_enable);
virtual bool has_loop() const;
virtual float get_length() const;
virtual String get_stream_name() const;
virtual int get_loop_count() const;
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual int get_channels() const { return stream_channels; }
virtual int get_mix_rate() const { return stream_srate; }
virtual int get_minimum_buffer_size() const { return 0; }
virtual int mix(int16_t *p_buffer, int p_frames);
AudioStreamPlaybackOGGVorbis();
~AudioStreamPlaybackOGGVorbis();
};
class AudioStreamOGGVorbis : public AudioStream {
GDCLASS(AudioStreamOGGVorbis, AudioStream);
String file;
public:
Ref<AudioStreamPlayback> instance_playback() {
Ref<AudioStreamPlaybackOGGVorbis> pb = memnew(AudioStreamPlaybackOGGVorbis);
pb->set_file(file);
return pb;
}
void set_file(const String &p_file) { file = p_file; }
};
class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
#endif // AUDIO_STREAM_OGG_H

View file

@ -30,19 +30,8 @@
#include "register_types.h"
#include "audio_stream_ogg_vorbis.h"
// Dummy module as libvorbis is needed by other modules (theora ...)
static Ref<ResourceFormatLoaderAudioStreamOGGVorbis> vorbis_stream_loader;
void register_vorbis_types() {}
void register_vorbis_types() {
vorbis_stream_loader.instance();
ResourceLoader::add_resource_format_loader(vorbis_stream_loader);
ClassDB::register_class<AudioStreamOGGVorbis>();
}
void unregister_vorbis_types() {
ResourceLoader::remove_resource_format_loader(vorbis_stream_loader);
vorbis_stream_loader.unref();
}
void unregister_vorbis_types() {}

Some files were not shown because too many files have changed in this diff Show more