summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/translations/extract.py239
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java17
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java33
-rw-r--r--platform/android/java_godot_lib_jni.cpp2
-rw-r--r--platform/android/java_godot_wrapper.cpp8
-rw-r--r--platform/android/java_godot_wrapper.h4
-rw-r--r--scene/2d/navigation_agent_2d.cpp1
-rw-r--r--scene/3d/navigation_agent.cpp1
8 files changed, 281 insertions, 24 deletions
diff --git a/doc/translations/extract.py b/doc/translations/extract.py
new file mode 100644
index 0000000000..9d7c073b67
--- /dev/null
+++ b/doc/translations/extract.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import re
+import shutil
+from collections import OrderedDict
+
+EXTRACT_TAGS = ["description", "brief_description", "member", "constant", "theme_item", "link"]
+HEADER = '''\
+# LANGUAGE translation of the Godot Engine class reference
+# Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.
+# Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).
+# This file is distributed under the same license as the Godot source code.
+#
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Godot Engine class reference\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8-bit\\n"
+
+'''
+
+## <xml-line-number-hack from="https://stackoverflow.com/a/36430270/10846399">
+import sys
+sys.modules['_elementtree'] = None
+import xml.etree.ElementTree as ET
+
+## override the parser to get the line number
+class LineNumberingParser(ET.XMLParser):
+ def _start(self, *args, **kwargs):
+ ## Here we assume the default XML parser which is expat
+ ## and copy its element position attributes into output Elements
+ element = super(self.__class__, self)._start(*args, **kwargs)
+ element._start_line_number = self.parser.CurrentLineNumber
+ element._start_column_number = self.parser.CurrentColumnNumber
+ element._start_byte_index = self.parser.CurrentByteIndex
+ return element
+
+ def _end(self, *args, **kwargs):
+ element = super(self.__class__, self)._end(*args, **kwargs)
+ element._end_line_number = self.parser.CurrentLineNumber
+ element._end_column_number = self.parser.CurrentColumnNumber
+ element._end_byte_index = self.parser.CurrentByteIndex
+ return element
+## </xml-line-number-hack>
+
+class Desc:
+ def __init__(self, line_no, msg, desc_list=None):
+ ## line_no : the line number where the desc is
+ ## msg : the description string
+ ## desc_list : the DescList it belongs to
+ self.line_no = line_no
+ self.msg = msg
+ self.desc_list = desc_list
+
+class DescList:
+ def __init__(self, doc, path):
+ ## doc : root xml element of the document
+ ## path : file path of the xml document
+ ## list : list of Desc objects for this document
+ self.doc = doc
+ self.path = path
+ self.list = []
+
+def print_error(error):
+ print("ERROR: {}".format(error))
+
+## build classes with xml elements recursively
+def _collect_classes_dir(path, classes):
+ if not os.path.isdir(path):
+ print_error("Invalid directory path: {}".format(path))
+ exit(1)
+ for _dir in map(lambda dir : os.path.join(path, dir), os.listdir(path)):
+ if os.path.isdir(_dir):
+ _collect_classes_dir(_dir, classes)
+ elif os.path.isfile(_dir):
+ if not _dir.endswith(".xml"):
+ #print("Got non-.xml file '{}', skipping.".format(path))
+ continue
+ _collect_classes_file(_dir, classes)
+
+## opens a file and parse xml add to classes
+def _collect_classes_file(path, classes):
+ if not os.path.isfile(path) or not path.endswith(".xml"):
+ print_error("Invalid xml file path: {}".format(path))
+ exit(1)
+ print('Collecting file: {}'.format(os.path.basename(path)))
+
+ try:
+ tree = ET.parse(path, parser=LineNumberingParser())
+ except ET.ParseError as e:
+ print_error("Parse error reading file '{}': {}".format(path, e))
+ exit(1)
+
+ doc = tree.getroot()
+
+ if 'name' in doc.attrib:
+ if 'version' not in doc.attrib:
+ print_error("Version missing from 'doc', file: {}".format(path))
+
+ name = doc.attrib["name"]
+ if name in classes:
+ print_error("Duplicate class {} at path {}".format(name, path))
+ exit(1)
+ classes[name] = DescList(doc, path)
+ else:
+ print_error('Unknown XML file {}, skipping'.format(path))
+
+
+## regions are list of tuples with size 3 (start_index, end_index, indent)
+## indication in string where the codeblock starts, ends, and it's indent
+## if i inside the region returns the indent, else returns -1
+def _get_xml_indent(i, regions):
+ for region in regions:
+ if region[0] < i < region[1] :
+ return region[2]
+ return -1
+
+## find and build all regions of codeblock which we need later
+def _make_codeblock_regions(desc, path=''):
+ code_block_end = False
+ code_block_index = 0
+ code_block_regions = []
+ while not code_block_end:
+ code_block_index = desc.find("[codeblock]", code_block_index)
+ if code_block_index < 0: break
+ xml_indent=0
+ while True :
+ ## [codeblock] always have a trailing new line and some tabs
+ ## those tabs are belongs to xml indentations not code indent
+ if desc[code_block_index+len("[codeblock]\n")+xml_indent] == '\t':
+ xml_indent+=1
+ else: break
+ end_index = desc.find("[/codeblock]", code_block_index)
+ if end_index < 0 :
+ print_error('Non terminating codeblock: {}'.format(path))
+ exit(1)
+ code_block_regions.append( (code_block_index, end_index, xml_indent) )
+ code_block_index += 1
+ return code_block_regions
+
+def _strip_and_split_desc(desc, code_block_regions):
+ desc_strip = '' ## a stripped desc msg
+ total_indent = 0 ## code indent = total indent - xml indent
+ for i in range(len(desc)):
+ c = desc[i]
+ if c == '\n' : c = '\\n'
+ if c == '"': c = '\\"'
+ if c == '\\': c = '\\\\' ## <element \> is invalid for msgmerge
+ if c == '\t':
+ xml_indent = _get_xml_indent(i, code_block_regions)
+ if xml_indent >= 0:
+ total_indent += 1
+ if xml_indent < total_indent:
+ c = '\\t'
+ else:
+ continue
+ else:
+ continue
+ desc_strip += c
+ if c == '\\n':
+ total_indent = 0
+ return desc_strip
+
+## make catlog strings from xml elements
+def _make_translation_catalog(classes):
+ unique_msgs = OrderedDict()
+ for class_name in classes:
+ desc_list = classes[class_name]
+ for elem in desc_list.doc.iter():
+ if elem.tag in EXTRACT_TAGS:
+ if not elem.text or len(elem.text) == 0 : continue
+ line_no = elem._start_line_number if elem.text[0]!='\n' else elem._start_line_number+1
+ desc_str = elem.text.strip()
+ code_block_regions = _make_codeblock_regions(desc_str, desc_list.path)
+ desc_msg = _strip_and_split_desc(desc_str, code_block_regions)
+ desc_obj = Desc(line_no, desc_msg, desc_list)
+ desc_list.list.append(desc_obj)
+
+ if desc_msg not in unique_msgs:
+ unique_msgs[desc_msg] = [desc_obj]
+ else:
+ unique_msgs[desc_msg].append(desc_obj)
+ return unique_msgs
+
+## generate the catlog file
+def _generate_translation_catalog_file(unique_msgs, output):
+ with open(output, 'w', encoding='utf8') as f:
+ f.write(HEADER)
+ for msg in unique_msgs:
+ if len(msg) == 0: continue ## ignore
+
+ f.write('#:')
+ desc_list = unique_msgs[msg]
+ for desc in desc_list:
+ path = desc.desc_list.path.replace('\\', '/')
+ if path.startswith('./'):
+ path = path[2:]
+ f.write(' {}:{}'.format(path, desc.line_no))
+ f.write('\n')
+
+ f.write('msgid "{}"\n'.format(msg))
+ f.write('msgstr ""\n\n')
+
+ ## TODO: what if 'nt'?
+ if (os.name == "posix"):
+ print("Wrapping template at 79 characters for compatibility with Weblate.")
+ os.system("msgmerge -w79 {0} {0} > {0}.wrap".format(output))
+ shutil.move("{}.wrap".format(output), output)
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--path", "-p", default=".", help="The directory containing XML files to collect.")
+ parser.add_argument("--output", "-o", default="translation_catlog.pot", help="The path to the output file.")
+ args = parser.parse_args()
+
+ output = os.path.abspath(args.output)
+ if not os.path.isdir(os.path.dirname(output)) or not output.endswith('.pot'):
+ print_error("Invalid output path: {}".format(output))
+ exit(1)
+ if not os.path.isdir(args.path):
+ print_error("Invalid working directory path: {}".format(args.path))
+ exit(1)
+
+ os.chdir(args.path)
+ print("Current working dir: {}\n".format(os.getcwd()))
+
+ classes = OrderedDict() ## dictionary of key=class_name, value=DescList objects
+ _collect_classes_dir('.', classes)
+ classes = OrderedDict(sorted(classes.items(), key = lambda kv: kv[0].lower() ))
+ unique_msgs = _make_translation_catalog(classes)
+ _generate_translation_catalog_file(unique_msgs, output)
+
+if __name__ == '__main__':
+ main()
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 4e605f9950..1798a1df3a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -55,8 +55,6 @@ import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -64,7 +62,6 @@ import android.provider.Settings.Secure;
import android.support.annotation.CallSuper;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.view.Display;
import android.view.KeyEvent;
@@ -197,12 +194,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
};
/**
- * Invoked on the GL thread when the Godot main loop has started.
+ * Invoked on the render thread when the Godot main loop has started.
*/
@CallSuper
- protected void onGLGodotMainLoopStarted() {
+ protected void onGodotMainLoopStarted() {
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onGLGodotMainLoopStarted();
+ plugin.onGodotMainLoopStarted();
}
}
@@ -247,7 +244,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
// Must occur after GodotLib.setup has completed.
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onGLRegisterPluginWithGodotNative();
+ plugin.onRegisterPluginWithGodotNative();
}
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
@@ -787,11 +784,11 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
/**
- * Queue a runnable to be run on the GL thread.
+ * Queue a runnable to be run on the render thread.
* <p>
- * This must be called after the GL thread has started.
+ * This must be called after the render thread has started.
*/
- public final void runOnGLThread(@NonNull Runnable action) {
+ public final void runOnRenderThread(@NonNull Runnable action) {
if (mView != null) {
mView.queueEvent(action);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index d5bf4fc70e..e745cdd0a5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -34,6 +34,7 @@ import android.app.Activity;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.view.Surface;
import android.view.View;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -84,8 +85,10 @@ public abstract class GodotPlugin {
/**
* Register the plugin with Godot native code.
+ *
+ * This method is invoked on the render thread.
*/
- public final void onGLRegisterPluginWithGodotNative() {
+ public final void onRegisterPluginWithGodotNative() {
nativeRegisterSingleton(getPluginName());
Class clazz = getClass();
@@ -169,9 +172,9 @@ public abstract class GodotPlugin {
public boolean onMainBackPressed() { return false; }
/**
- * Invoked on the GL thread when the Godot main loop has started.
+ * Invoked on the render thread when the Godot main loop has started.
*/
- public void onGLGodotMainLoopStarted() {}
+ public void onGodotMainLoopStarted() {}
/**
* Invoked once per frame on the GL thread after the frame is drawn.
@@ -190,6 +193,22 @@ public abstract class GodotPlugin {
public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
/**
+ * Invoked once per frame on the Vulkan thread after the frame is drawn.
+ */
+ public void onVkDrawFrame() {}
+
+ /**
+ * Called on the Vulkan thread after the surface is created and whenever the surface size
+ * changes.
+ */
+ public void onVkSurfaceChanged(Surface surface, int width, int height) {}
+
+ /**
+ * Called on the Vulkan thread when the surface is created or recreated.
+ */
+ public void onVkSurfaceCreated(Surface surface) {}
+
+ /**
* Returns the name of the plugin.
* <p>
* This value must match the one listed in the plugin '<meta-data>' manifest entry.
@@ -225,12 +244,12 @@ public abstract class GodotPlugin {
}
/**
- * Queue the specified action to be run on the GL thread.
+ * Queue the specified action to be run on the render thread.
*
- * @param action the action to run on the GL thread
+ * @param action the action to run on the render thread
*/
- protected void runOnGLThread(Runnable action) {
- godot.runOnGLThread(action);
+ protected void runOnRenderThread(Runnable action) {
+ godot.runOnRenderThread(action);
}
/**
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 0b1d070441..8bbf41d82d 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -207,7 +207,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
}
os_android->main_loop_begin();
- godot_java->on_gl_godot_main_loop_started(env);
+ godot_java->on_godot_main_loop_started(env);
++step;
}
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 7b677c186e..2a540bb4a9 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -66,7 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
- _on_gl_godot_main_loop_started = p_env->GetMethodID(cls, "onGLGodotMainLoopStarted", "()V");
+ _on_godot_main_loop_started = p_env->GetMethodID(cls, "onGodotMainLoopStarted", "()V");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -108,13 +108,13 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
p_env->CallVoidMethod(godot_instance, _on_video_init);
}
-void GodotJavaWrapper::on_gl_godot_main_loop_started(JNIEnv *p_env) {
- if (_on_gl_godot_main_loop_started) {
+void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
+ if (_on_godot_main_loop_started) {
if (p_env == NULL) {
p_env = ThreadAndroid::get_env();
}
}
- p_env->CallVoidMethod(godot_instance, _on_gl_godot_main_loop_started);
+ p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
void GodotJavaWrapper::restart(JNIEnv *p_env) {
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index cdab2ecc9c..fb77c8ba6a 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -61,7 +61,7 @@ private:
jmethodID _is_activity_resumed = 0;
jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0;
- jmethodID _on_gl_godot_main_loop_started = 0;
+ jmethodID _on_godot_main_loop_started = 0;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
@@ -73,7 +73,7 @@ public:
jobject get_class_loader();
void on_video_init(JNIEnv *p_env = NULL);
- void on_gl_godot_main_loop_started(JNIEnv *p_env = NULL);
+ void on_godot_main_loop_started(JNIEnv *p_env = NULL);
void restart(JNIEnv *p_env = NULL);
void force_quit(JNIEnv *p_env = NULL);
void set_keep_screen_on(bool p_enabled);
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index f5fe113f29..e345a076ae 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -215,6 +215,7 @@ void NavigationAgent2D::set_target_location(Vector2 p_location) {
navigation_path.clear();
target_reached = false;
navigation_finished = false;
+ update_frame_id = 0;
}
Vector2 NavigationAgent2D::get_target_location() const {
diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp
index 728fc947e9..3da1ea66d6 100644
--- a/scene/3d/navigation_agent.cpp
+++ b/scene/3d/navigation_agent.cpp
@@ -234,6 +234,7 @@ void NavigationAgent::set_target_location(Vector3 p_location) {
navigation_path.clear();
target_reached = false;
navigation_finished = false;
+ update_frame_id = 0;
}
Vector3 NavigationAgent::get_target_location() const {