diff --git a/.efrocachemap b/.efrocachemap
index 9b742b51..eb6aa543 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -4135,16 +4135,16 @@
"assets/build/windows/x64/vc_redist.x64.exe": "https://files.ballistica.net/cache/ba1/ea/19/8b8787d81abcdce158ba608cd24f",
"assets/build/windows/x64/vcruntime140_1d.dll": "https://files.ballistica.net/cache/ba1/11/d8/ff6344b429b00c24d9a1930d4338",
"assets/build/windows/x64/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/20/33/0825e11e6518f87ece3009309933",
- "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ef/00/26669a4c745046379f89f9dd14da",
- "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5b/2c/bd7e5d1aa459ae3c0b21e4d057fa",
- "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/da/39/d33b31a44837a70473c2dc4bcd33",
- "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ca/96/3cc53735853bd6e8d60b433f95a0",
- "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e1/c5/658f143679fd8f56150357e090af",
- "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/38/9b/b62d121f0c6fb665a26c7832a015",
- "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/86/8f/59e767e409088cedb70b7ebad514",
- "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/5c/10/3356ad1eefadf9902dac526d70f8",
- "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/8d/dc/80d1aca955cff4ce0d6e27b67fbd",
- "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/0d/76/aa2714c41e4c996c0f47f3e82699",
- "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/5d/bc/eed0ece2e4a641f74ec3f2d1bee1",
- "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/75/1e/0bcec9cbe6fcc933249cbfcccb9f"
+ "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3f/a1/dbaa655aeffc956c419ecbb3a0f1",
+ "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a5/10/3d7cb0d4b13a27574735e2b1aa63",
+ "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/00/67/1089a04debbb8b0523c7311780d2",
+ "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4c/57/d17251f72bb763fbdb7741d0da9d",
+ "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7b/e9/44c85334c39d41b2f9ea592de605",
+ "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d4/1f/034553946265bacfc722b86d57f8",
+ "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/83/2f/350d0e3ffbe1e4a3eca662fc2e1a",
+ "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/62/38/6615fedcf4c970ea3dc66147de18",
+ "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/13/47/d84222f416e0da118cda7e8862ae",
+ "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7f/3e/0cb90e11e701f560fa037604e919",
+ "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/34/df/1f16b4633ec30aa50a64016d87b1",
+ "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/94/f7/ce16094966391c21be78f541bc2c"
}
\ No newline at end of file
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index b97b270f..9299712b 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -1385,6 +1385,7 @@
pathlib
pathnames
pathstonames
+ pathtmp
patsubst
pausable
pbrowser
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c170868b..ec5bcf28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
### 1.5.10 (20083)
- Streamlined C++ layer bootstrapping process a bit.
+- Creating sys scripts via ba.modutils now works properly.
+- Custom soundtracks should now work again under Android 10.
+- Misc other bug fixes.
### 1.5.9 (20082)
- Reduced some hitches when clicking on certain buttons in the UI
diff --git a/assets/src/ba_data/python/ba/_netutils.py b/assets/src/ba_data/python/ba/_netutils.py
index dc0180c4..08a41f82 100644
--- a/assets/src/ba_data/python/ba/_netutils.py
+++ b/assets/src/ba_data/python/ba/_netutils.py
@@ -105,9 +105,11 @@ class ServerCallThread(threading.Thread):
self._callback(arg)
def run(self) -> None:
+ # pylint: disable=too-many-branches
import urllib.request
import urllib.error
import json
+ import http.client
from ba import _general
try:
self._data = _general.utf8_all(self._data)
@@ -145,16 +147,35 @@ class ServerCallThread(threading.Thread):
response_data = json.loads(raw_data_s)
else:
raise TypeError(f'invalid responsetype: {self._response_type}')
- except (urllib.error.URLError, ConnectionError):
- # Server rejected us, broken pipe, etc. It happens. Ignoring.
- response_data = None
+
except Exception as exc:
- # Any other error here is unexpected, so let's make a note of it.
- print('Exc in ServerCallThread:', exc)
- import traceback
- traceback.print_exc()
+ import errno
+ do_print = False
response_data = None
+ # Ignore common network errors; note unexpected ones.
+ if isinstance(exc, (urllib.error.URLError, ConnectionError,
+ http.client.IncompleteRead)):
+ pass
+ elif isinstance(exc, OSError):
+ if exc.errno == 10051: # Windows unreachable network error.
+ pass
+ elif exc.errno in [errno.ETIMEDOUT]:
+ pass
+ else:
+ do_print = True
+ else:
+ do_print = True
+
+ if do_print:
+ # Any other error here is unexpected,
+ # so let's make a note of it,
+ print(f'Error in ServerCallThread'
+ f' (response-type={self._response_type},'
+ f' response-data={response_data}):')
+ import traceback
+ traceback.print_exc()
+
if self._callback is not None:
_ba.pushcall(_general.Call(self._run_callback, response_data),
from_other_thread=True)
diff --git a/assets/src/ba_data/python/ba/modutils.py b/assets/src/ba_data/python/ba/modutils.py
index f7645821..a4ec3c0a 100644
--- a/assets/src/ba_data/python/ba/modutils.py
+++ b/assets/src/ba_data/python/ba/modutils.py
@@ -27,7 +27,7 @@ import os
import _ba
if TYPE_CHECKING:
- from typing import Optional
+ from typing import Optional, List, Sequence
def get_human_readable_user_scripts_path() -> str:
@@ -55,18 +55,25 @@ def get_human_readable_user_scripts_path() -> str:
return path
+def _request_storage_permission() -> bool:
+ """If needed, requests storage permission from the user (& return true)."""
+ from ba._lang import Lstr
+ from ba._enums import Permission
+ if not _ba.have_permission(Permission.STORAGE):
+ _ba.playsound(_ba.getsound('error'))
+ _ba.screenmessage(Lstr(resource='storagePermissionAccessText'),
+ color=(1, 0, 0))
+ _ba.timer(1.0, lambda: _ba.request_permission(Permission.STORAGE))
+ return True
+ return False
+
+
def show_user_scripts() -> None:
"""Open or nicely print the location of the user-scripts directory."""
- from ba import _lang
- from ba._enums import Permission
app = _ba.app
# First off, if we need permission for this, ask for it.
- if not _ba.have_permission(Permission.STORAGE):
- _ba.playsound(_ba.getsound('error'))
- _ba.screenmessage(_lang.Lstr(resource='storagePermissionAccessText'),
- color=(1, 0, 0))
- _ba.request_permission(Permission.STORAGE)
+ if _request_storage_permission():
return
# Secondly, if the dir doesn't exist, attempt to make it.
@@ -107,31 +114,37 @@ def create_user_system_scripts() -> None:
(for editing and experiment with)
"""
- app = _ba.app
import shutil
+ app = _ba.app
+
+ # First off, if we need permission for this, ask for it.
+ if _request_storage_permission():
+ return
+
path = (app.python_directory_user + '/sys/' + app.version)
+ pathtmp = path + '_tmp'
if os.path.exists(path):
shutil.rmtree(path)
- if os.path.exists(path + '_tmp'):
- shutil.rmtree(path + '_tmp')
- os.makedirs(path + '_tmp', exist_ok=True)
+ if os.path.exists(pathtmp):
+ shutil.rmtree(pathtmp)
- # Hmm; shutil.copytree doesn't seem to work nicely on android,
- # so lets do it manually.
- # NOTE: Should retry this now that we have 3.7 (this note was for 2.7)
- src_dir = app.python_directory_app
- dst_dir = path + '_tmp'
- filenames = os.listdir(app.python_directory_app)
- for fname in filenames:
- print('COPYING', src_dir + '/' + fname, '->', dst_dir)
- shutil.copyfile(src_dir + '/' + fname, dst_dir + '/' + fname)
+ def _ignore_filter(src: str, names: Sequence[str]) -> Sequence[str]:
+ del src, names # Unused
- print('MOVING', path + '_tmp', path)
- shutil.move(path + '_tmp', path)
- print(
- ('Created system scripts at :\'' + path +
- '\'\nRestart Ballistica to use them. (use ba.quit() to exit the game)'
- ))
+ # We simply skip all __pycache__ directories. (the user would have
+ # to blow them away anyway to make changes;
+ # See https://github.com/efroemling/ballistica/wiki
+ # /Knowledge-Nuggets#python-cache-files-gotcha
+ return ('__pycache__', )
+
+ print(f'COPYING "{app.python_directory_app}" -> "{pathtmp}".')
+ shutil.copytree(app.python_directory_app, pathtmp, ignore=_ignore_filter)
+
+ print(f'MOVING "{pathtmp}" -> "{path}".')
+ shutil.move(pathtmp, path)
+ print(f"Created system scripts at :'{path}"
+ f"'\nRestart {_ba.appname()} to use them."
+ f' (use ba.quit() to exit the game)')
if app.platform == 'android':
print('Note: the new files may not be visible via '
'android-file-transfer until you restart your device.')
@@ -144,9 +157,9 @@ def delete_user_system_scripts() -> None:
path = (app.python_directory_user + '/sys/' + app.version)
if os.path.exists(path):
shutil.rmtree(path)
- print(
- 'User system scripts deleted.\nRestart Ballistica to use internal'
- ' scripts. (use ba.quit() to exit the game)')
+ print(f'User system scripts deleted.\n'
+ f'Restart {_ba.appname()} to use internal'
+ f' scripts. (use ba.quit() to exit the game)')
else:
print('User system scripts not found.')
diff --git a/assets/src/ba_data/python/ba/osmusic.py b/assets/src/ba_data/python/ba/osmusic.py
index 0b7c4a36..e188bebf 100644
--- a/assets/src/ba_data/python/ba/osmusic.py
+++ b/assets/src/ba_data/python/ba/osmusic.py
@@ -123,6 +123,7 @@ class _PickFolderSongThread(threading.Thread):
def run(self) -> None:
from ba import _lang
from ba._general import Call
+ do_print_error = True
try:
_ba.set_thread_name('BA_PickFolderSongThread')
all_files: List[str] = []
@@ -134,14 +135,16 @@ class _PickFolderSongThread(threading.Thread):
all_files.insert(random.randrange(len(all_files) + 1),
root + '/' + fname)
if not all_files:
- raise Exception(
+ do_print_error = False
+ raise RuntimeError(
_lang.Lstr(resource='internal.noMusicFilesInFolderText').
evaluate())
_ba.pushcall(Call(self._callback, all_files, None),
from_other_thread=True)
except Exception as exc:
from ba import _error
- _error.print_exception()
+ if do_print_error:
+ _error.print_exception()
try:
err_str = str(exc)
except Exception:
diff --git a/assets/src/ba_data/python/bastd/ui/gather.py b/assets/src/ba_data/python/bastd/ui/gather.py
index 464f27ee..8edcdd24 100644
--- a/assets/src/ba_data/python/bastd/ui/gather.py
+++ b/assets/src/ba_data/python/bastd/ui/gather.py
@@ -435,19 +435,16 @@ class GatherWindow(ba.Window):
sock.connect(('8.8.8.8', 80))
val = sock.getsockname()[0]
sock.close()
- # val = ([
- # (s.connect(('8.8.8.8', 80)),
- # s.getsockname()[0],
- # s.close()) for s in
- # [socket.socket(socket.AF_INET,
- # socket.SOCK_DGRAM)]
- # ][0][1])
ba.pushcall(ba.Call(self._call, val),
from_other_thread=True)
- except Exception:
- # FIXME: Should filter out expected errors and
- # report others here.
- ba.print_exception()
+ except Exception as exc:
+ # Ignore expected network errors; log others.
+ import errno
+ if (isinstance(exc, OSError)
+ and exc.errno == errno.ENETUNREACH):
+ pass
+ else:
+ ba.print_exception()
AddrFetchThread(ba.WeakCall(
self._internet_fetch_local_addr_cb)).start()
diff --git a/docs/ba_module.md b/docs/ba_module.md
index 74d484c8..488ff5a7 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-
last updated on 2020-06-22 for Ballistica version 1.5.9 build 20081
+last updated on 2020-06-23 for Ballistica version 1.5.10 build 20084
This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!