diff --git a/.efrocachemap b/.efrocachemap
index 81c70975..3817aff0 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -3995,51 +3995,51 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
- "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/84/d0/74f2b20adc517beeb0efafb4b4de",
- "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/60/85/8e8cbeee10bf0c4f5b38792cd972",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/84/9e/70d571aa9d9c869ae544e81ceb9c",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/61/4b/9cb4a70e2c5fd5046a2a0aa1d011",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c4/9b/24e583b9bf7bcb406a544f5d9d90",
- "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/4b/b73ae2c2e711699daca03bb46fbc",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/41/d1/b665246e68c64f90819807d6d528",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a4/81/daa5716db4fe65ae8ef4700af5a1",
- "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/47/e5/eabd47ace227af9fb05bdaeea7aa",
- "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/69/8b/c91d3562ac890e40aafb9f35cf5e",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/16/b1/45dcdcd101c5b293f78913f62a0e",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/81/bb/3fba8cad9c1ea3d01199b32385e7",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f3/0f/7c6074e1c2165b976e4a030d3876",
- "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/02/9a/c31ef4286904f9970519f42f0644",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/84/62/f0fba929db90c96deac3d73a135b",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/95/72/64f86aba6b2219f49bc5f89f2189",
- "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a8/a4/237ffde4b291ffc0cfb795863c1b",
- "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3f/58/877ef1ec2c1e7a4c405574ebc544",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/37/7e/bdc6a0ba2c95bfecc16911a80983",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/f5/5c/e70d827e538c18b914f9d1cc52af",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/02/e1d6afd343ce32d53c134c5fc4e7",
- "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ed/18/45e61446f5f7e02624fb87fb8c97",
- "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cf/46/393131611fa1395fd9c173d8426f",
- "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/2e/d4a8ba6f7c46da387394f4542d02",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/df/6c/cafe5794323f445392e63114f14b",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/00/ba/5c85c98957ac9f0832a01ea83168",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/35/0c/c85a8d0a8b43dac24a94417297fb",
- "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f1/ca/c78a414c9d6ec0ffb647585c3e33",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0f/6a/83518a680d4917e3356d21d3650d",
- "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6a/39/2e1794bf48cbc4c67c63b9527a68",
- "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3b/64/48be465941f4f1aa3332a1d32f6a",
- "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/63/03/d9a332d36a337086b639c4240674",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ea/ca/533d54af50f2b5750f7c407dcbd5",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/d6/e9af602218f3dd14a5bedb6c7eb2",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a6/cc/2ee87bb2ead38a3bf7d8b2588f39",
- "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e5/ef/91bb1dfff04d62899cbe363b0d23",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/a2/7a/faf4c2744e2762e58d0119dafa72",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/7d/66/d3aafe52f5d44a23aa426bc8e602",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/63/5b/a3d3b19e7583a72cbdeefbd00f7e",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/60/14/3a28abc1b1b87b14f05d86a2e900",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/6c/a3/ded8df37d31c54c4ab66e224015a",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/2b/c6/a8701e03471178983957c33026ff",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7e/16/307bb68c0561181ba701d0bcd350",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/09/b3/2fd1934cb6f0d2454e222c60ef3b",
- "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/7d/3e/229a581cb2454ed856f1d8b564a7",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9a/b0/687077bb5518ba6297514a21cf79",
+ "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9a/07/a28a396bd3aff9c7d8ea4425c972",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/98/86/2b057bf3acb6707ee2345ae48726",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/13/96/49d0b9a8a5a808a619318e4ed3bd",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ed/87/b71085642185d3150a3dd59ed593",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/62/9b/2b1d5dbfa88a3b71c1476940ded0",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b7/12/f1f6253a38af6278dbcc4ee13345",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c7/79/dc992d142c303287a321d22a51c9",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/8a/90/9e476b436c7f9211f2e49308a063",
+ "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a8/9c/090dbf15aeb803532b96093ffb2e",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e3/2c/0eb229b654494a9bb99a663bee34",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/94/996c258446bcd1587ce55814c10f",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ac/d9/8baaae8352bcd21034d5daea284c",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7f/f3/2d78f72daebb218658f4dc010e8e",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/75/5e/a887e7a232caed9a4a0a9a0909ae",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/30/0d/f0de21829b62bf1256781df266ed",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8d/10/a54cfe97f26717ab57477860cb43",
+ "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/14/30/350f4c8f8b17326c161a05353afa",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/ab/43/79fd942d5e2f99f49726d15fbebd",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/50/fc/8ac0aa34cc5e792dd126a96deb7f",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/f6/d62e6e6d5e7fe1945f08ccbb9a8f",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/c5/44d0082442b06153a7d7dce4c8ce",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d0/00/61378fb26ddcbf023fd3c40e4ffb",
+ "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0a/e2/a01a2de0835fee06fd7b357c700f",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/dd/8a8f84d8ed28fec12137f632e9f7",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6d/b1/34d06bce1b93bcefc4896bd2700a",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/63/61/f48cc804658071c759398f9c00dd",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a2/b7/a28b8ed7c1f13b53dea1c878c5fc",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a5/b2/f29c2fe15f33441de94331743353",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c2/63/bf93d39adb82748decfdb980b562",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/00/6d/ac55bffd774cf75c5944b679194a",
+ "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7b/dd/af154cabc6779257a38ea6c9e833",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/54/88/2de2dfa2c0c0a1766fa0d5f26ed6",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ff/26/da4a58abecf5d9275477eaec0c17",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/5d/8658a206b8c9b741be2422162784",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fc/57/d59920e098d23a2d150c899cde29",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/85/19/3c25e72ea3976b7d854c21696ec4",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/50/21/6c275dad22cc2cc1f95e21652c29",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/a0/1a/f894ee82b89dcb4a43c36ab882ee",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/c3/6a/0e89ae233f6ae76351d33ed2b0e6",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ce/ba/c5a0c9b8224dc8df02c5189e22ac",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/1d/3a/560a77a6c97f6f39b765211b1ed8",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/5b/fb/b8c70b2a72452da1af92844335b8",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/a9/6c/12e1188dc02708b0b0fee30835b9",
+ "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"
}
\ No newline at end of file
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index 6ae45394..8c4ff566 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -64,9 +64,11 @@
aiomain
alarmsound
alibaba
+ allerrors
allpaths
allsettings
allteams
+ allwarnings
aman
amazonaws
aname
@@ -2384,6 +2386,7 @@
startscan
startsplits
starttime
+ startupmsg
statestr
statictest
statictestfiles
@@ -2412,6 +2415,7 @@
stot
strftime
stringified
+ stringifying
stringprep
stringptr
strippable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07af68eb..d719a855 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-### 1.7.7 (build 20842, api 7, 2022-09-13)
+### 1.7.7 (build 20849, api 7, 2022-09-14)
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
- Improved logging of missing playlist game types.
- Some ba.Lstr functionality can now be used in background threads.
@@ -26,7 +26,16 @@
- Simplified C++ bootstrapping to allocate all globals in one place.
- Renamed C++ Game classes to Logic.
- The app now bootstraps Python in the main thread instead of the logic thread. This will keep things more consistent later when we are able to run under an already-existing Python interpreter.
+- As a side-effect of initing Python in the main thread, it seems that Python now catches segfaults in our debug builds and prints Python stack traces. (see https://docs.python.org/3/library/faulthandler.html). We'll have to experiment and see if this is a net positive or something we want to disable or make optional.
- Python and _ba are now completely initialized in public source code. Now we just need to enable the app to survive without _bainternal and it'll be possible to build a 100% open source app.
+- `Logging::Log()` in the C++ layer now takes a LogLevel arg (kDebug, kWarning, kError, etc.) and simply calls the equivalent Python logging.XXX call. This unifies our C++ and Python logging to go through the same place.
+- `ba.log()` is no more. Instead just use standard Python logging functions (logging.info(), logging.error(), etc.).
+- `_ba.getlog()` is now `_ba.get_v1_cloud_log()`. Note that this functionality will go away eventually so you should use ba.app.log_handler and/or standard Python logging functions to get at app logs.
+- Along the same lines, `_ba.get_log_file_path()` is now `_ba.get_v1_cloud_log_file_path()`.
+- Added `_ba.display_log()` function which ships a log message to the in-game terminal and platform-specific places like the Android log. The engine wires up standard Python logging output to go through this.
+- Added `_ba.v1_cloud_log()` which ships a message to the old v1-cloud-log (the log which is gathered and sent to the v1 master server to help me identify problems people are seeing). This is presently wired up to a subset of Python logging output to approximate how it used to work.
+- Note: Previously in the C++ layer some code would mix Python print calls (such as PyErr_PrintEx()) with ballistica::Log() calls. Previously these all wound up going to the same place (Python's sys.stderr) so it worked, but now they no longer do and so this sort of mixing should be avoided. So if you see a weird combination of colored log output lines with non-colored lines that seem to go together, please holler as it means something needs to be fixed.
+
### 1.7.6 (build 20687, api 7, 2022-08-11)
- Cleaned up da MetaSubsystem code.
diff --git a/assets/src/ba_data/python/._ba_sources_hash b/assets/src/ba_data/python/._ba_sources_hash
index b56206f3..d78d9181 100644
--- a/assets/src/ba_data/python/._ba_sources_hash
+++ b/assets/src/ba_data/python/._ba_sources_hash
@@ -1 +1 @@
-126683827977798484003262787310231621875
\ No newline at end of file
+76251027805752156826413428926087661089
\ No newline at end of file
diff --git a/assets/src/ba_data/python/._bainternal_sources_hash b/assets/src/ba_data/python/._bainternal_sources_hash
index 6e9f924c..3307093f 100644
--- a/assets/src/ba_data/python/._bainternal_sources_hash
+++ b/assets/src/ba_data/python/._bainternal_sources_hash
@@ -1 +1 @@
-222094078620857897443553282652634355523
\ No newline at end of file
+139020022013133168311319486434408589898
\ No newline at end of file
diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py
index 0705868d..34e91a0a 100644
--- a/assets/src/ba_data/python/_ba.py
+++ b/assets/src/ba_data/python/_ba.py
@@ -1108,12 +1108,6 @@ def add_clean_frame_callback(call: Callable) -> None:
return None
-def add_transaction(transaction: dict,
- callback: Callable | None = None) -> None:
- """(internal)"""
- return None
-
-
def android_get_external_files_dir() -> str:
"""(internal)
@@ -1472,6 +1466,16 @@ def disconnect_from_host() -> None:
return None
+def display_log(name: str, level: str, message: str) -> None:
+ """(internal)
+
+ Sends a log message to the in-game console and any per-platform
+ log destinations (Android log, etc.). This generally is not called
+ directly and should instead be fed Python logging output.
+ """
+ return None
+
+
def do_once() -> bool:
"""Return whether this is the first time running a line of code.
@@ -1561,15 +1565,6 @@ def focus_window() -> None:
return None
-def game_service_has_leaderboard(game: str, config: str) -> bool:
- """(internal)
-
- Given a game and config string, returns whether there is a leaderboard
- for it on the game service.
- """
- return bool()
-
-
def get_appconfig_builtin_keys() -> list[str]:
"""(internal)"""
return ['blah', 'blah2']
@@ -1700,29 +1695,11 @@ def get_local_active_input_devices_count() -> int:
return int()
-def get_log_file_path() -> str:
- """(internal)
-
- Return the path to the app log file.
- """
- return str()
-
-
def get_low_level_config_value(key: str, default_value: int) -> int:
"""(internal)"""
return int()
-def get_master_server_address(source: int = -1,
- version: int = 1,
- internal: bool = False) -> str:
- """(internal)
-
- Return the address of the master server.
- """
- return str()
-
-
def get_max_graphics_quality() -> str:
"""(internal)
@@ -1731,11 +1708,6 @@ def get_max_graphics_quality() -> str:
return str()
-def get_news_show() -> str:
- """(internal)"""
- return str()
-
-
def get_package_collide_model(package: ba.AssetPackage,
name: str) -> ba.CollideModel:
"""(internal)"""
@@ -1767,16 +1739,6 @@ def get_package_texture(package: ba.AssetPackage, name: str) -> ba.Texture:
return ba.Texture()
-def get_price(item: str) -> str | None:
- """(internal)"""
- return ''
-
-
-def get_public_login_id() -> str | None:
- """(internal)"""
- return ''
-
-
def get_public_party_enabled() -> bool:
"""(internal)"""
return bool()
@@ -1787,16 +1749,6 @@ def get_public_party_max_size() -> int:
return int()
-def get_purchased(item: str) -> bool:
- """(internal)"""
- return bool()
-
-
-def get_purchases_state() -> int:
- """(internal)"""
- return int()
-
-
def get_qrcode_texture(url: str) -> ba.Texture:
"""(internal)"""
import ba # pylint: disable=cyclic-import
@@ -1824,11 +1776,6 @@ def get_replays_dir() -> str:
return str()
-def get_scores_to_beat(level: str, config: str, callback: Callable) -> None:
- """(internal)"""
- return None
-
-
def get_special_widget(name: str) -> Widget:
"""(internal)"""
return Widget()
@@ -1872,56 +1819,16 @@ def get_ui_input_device() -> ba.InputDevice:
return ba.InputDevice()
-def get_v1_account_display_string(full: bool = True) -> str:
+def get_v1_cloud_log() -> str:
"""(internal)"""
return str()
-def get_v1_account_misc_read_val(name: str, default_value: Any) -> Any:
- """(internal)"""
- return _uninferrable()
-
-
-def get_v1_account_misc_read_val_2(name: str, default_value: Any) -> Any:
- """(internal)"""
- return _uninferrable()
-
-
-def get_v1_account_misc_val(name: str, default_value: Any) -> Any:
- """(internal)"""
- return _uninferrable()
-
-
-def get_v1_account_name() -> str:
- """(internal)"""
- return str()
-
-
-def get_v1_account_state() -> str:
- """(internal)"""
- return str()
-
-
-def get_v1_account_state_num() -> int:
- """(internal)"""
- return int()
-
-
-def get_v1_account_ticket_count() -> int:
+def get_v1_cloud_log_file_path() -> str:
"""(internal)
- Returns the number of tickets for the current account.
+ Return the path to the app log file.
"""
- return int()
-
-
-def get_v1_account_type() -> str:
- """(internal)"""
- return str()
-
-
-def get_v2_fleet() -> str:
- """(internal)"""
return str()
@@ -2015,11 +1922,6 @@ def getinputdevice(name: str, unique_id: str, doraise: bool = True) -> Any:
return None
-def getlog() -> str:
- """(internal)"""
- return str()
-
-
def getmodel(name: str) -> ba.Model:
"""Return a model, loading it if necessary.
@@ -2129,11 +2031,6 @@ def have_incentivized_ad() -> bool:
return bool()
-def have_outstanding_transactions() -> bool:
- """(internal)"""
- return bool()
-
-
def have_permission(permission: ba.Permission) -> bool:
"""(internal)"""
return bool()
@@ -2210,15 +2107,10 @@ def imagewidget(edit: ba.Widget | None = None,
return ba.Widget()
-def in_game_purchase(item: str, price: int) -> None:
- """(internal)"""
- return None
-
-
def in_logic_thread() -> bool:
"""(internal)
- Returns whether or not the current thread is the game thread.
+ Returns whether or not the current thread is the logic thread.
"""
return bool()
@@ -2240,11 +2132,6 @@ def increment_analytics_counts_raw(name: str, increment: int = 1) -> None:
return None
-def is_blessed() -> bool:
- """(internal)"""
- return bool()
-
-
def is_in_replay() -> bool:
"""(internal)"""
return bool()
@@ -2301,22 +2188,6 @@ def lock_all_input() -> None:
return None
-def log(message: str, to_stdout: bool = True, to_server: bool = True) -> None:
- """Category: **General Utility Functions**
-
- Log a message. This goes to the default logging mechanism depending
- on the platform (stdout on mac, android log on android, etc).
-
- Log messages also go to the in-game console unless 'to_console'
- is False. They are also sent to the master-server for use in analyzing
- issues unless to_server is False.
-
- Python's standard print() is wired to call this (with default values)
- so in most cases you can just use that.
- """
- return None
-
-
def mac_music_app_get_library_source() -> None:
"""(internal)"""
return None
@@ -2352,14 +2223,6 @@ def mac_music_app_stop() -> None:
return None
-def mark_config_dirty() -> None:
- """(internal)
-
- Category: General Utility Functions
- """
- return None
-
-
def mark_log_sent() -> None:
"""(internal)"""
return None
@@ -2488,11 +2351,6 @@ def playsound(sound: Sound,
return None
-def power_ranking_query(callback: Callable, season: Any = None) -> None:
- """(internal)"""
- return None
-
-
def print_context() -> None:
"""(internal)
@@ -2509,23 +2367,6 @@ def print_load_info() -> None:
return None
-def print_stderr(message: str) -> None:
- """(internal)
-
- Print to system stderr.
- Also forwards to the internal console, etc.
- """
- return None
-
-
-def print_stdout(message: str) -> None:
- """(internal)
- Print to system stdout.
- Also forwards to the internal console, etc.
- """
- return None
-
-
def printnodes() -> None:
"""Print various info about existing nodes; useful for debugging.
@@ -2545,11 +2386,6 @@ def printobjects() -> None:
return None
-def purchase(item: str) -> None:
- """(internal)"""
- return None
-
-
def pushcall(call: Callable,
from_other_thread: bool = False,
suppress_other_thread_warning: bool = False) -> None:
@@ -2560,12 +2396,12 @@ def pushcall(call: Callable,
This can be handy for calls that are disallowed from within other
callbacks, etc.
- This call expects to be used in the game thread, and will automatically
+ This call expects to be used in the logic thread, and will automatically
save and restore the ba.Context to behave seamlessly.
- If you want to push a call from outside of the game thread,
+ If you want to push a call from outside of the logic thread,
however, you can pass 'from_other_thread' as True. In this case
- the call will always run in the UI context on the game thread.
+ the call will always run in the UI context on the logic thread.
"""
return None
@@ -2616,21 +2452,11 @@ def reload_media() -> None:
return None
-def report_achievement(achievement: str, pass_to_account: bool = True) -> None:
- """(internal)"""
- return None
-
-
def request_permission(permission: ba.Permission) -> None:
"""(internal)"""
return None
-def reset_achievements() -> None:
- """(internal)"""
- return None
-
-
def reset_game_activity_tracking() -> None:
"""(internal)"""
return None
@@ -2646,11 +2472,6 @@ def resolve_appconfig_value(key: str) -> Any:
return _uninferrable()
-def restore_purchases() -> None:
- """(internal)"""
- return None
-
-
def rowwidget(edit: ba.Widget | None = None,
parent: ba.Widget | None = None,
size: Sequence[float] | None = None,
@@ -2673,11 +2494,6 @@ def rowwidget(edit: ba.Widget | None = None,
return ba.Widget()
-def run_transactions() -> None:
- """(internal)"""
- return None
-
-
def safecolor(color: Sequence[float],
target_intensity: float = 0.6) -> tuple[float, ...]:
"""Given a color tuple, return a color safe to display as text.
@@ -2953,48 +2769,11 @@ def show_progress_bar() -> None:
return None
-def sign_in_v1(account_type: str) -> None:
- """(internal)
-
- Category: General Utility Functions
- """
- return None
-
-
-def sign_out_v1(v2_embedded: bool = False) -> None:
- """(internal)
-
- Category: General Utility Functions
- """
- return None
-
-
def submit_analytics_counts() -> None:
"""(internal)"""
return None
-def submit_score(game: str,
- config: str,
- name: Any,
- score: int | None,
- callback: Callable,
- friend_callback: Callable | None,
- order: str = 'increasing',
- tournament_id: str | None = None,
- score_type: str = 'points',
- campaign: str | None = None,
- level: str | None = None) -> None:
- """(internal)
-
- Submit a score to the server; callback will be called with the results.
- As a courtesy, please don't send fake scores to the server. I'd prefer
- to devote my time to improving the game instead of trying to make the
- score server more mischief-proof.
- """
- return None
-
-
def textwidget(edit: ba.Widget | None = None,
parent: ba.Widget | None = None,
size: Sequence[float] | None = None,
@@ -3173,12 +2952,6 @@ def timer(time: float,
return None
-def tournament_query(callback: Callable[[dict | None], None],
- args: dict) -> None:
- """(internal)"""
- return None
-
-
def uibounds() -> tuple[float, float, float, float]:
"""(internal)
@@ -3198,6 +2971,14 @@ def unlock_all_input() -> None:
return None
+def v1_cloud_log(message: str) -> None:
+ """(internal)
+
+ Push messages to the old v1 cloud log.
+ """
+ return None
+
+
def value_test(arg: str,
change: float | None = None,
absolute: float | None = None) -> float:
diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py
index 4b06c0ed..43d98979 100644
--- a/assets/src/ba_data/python/ba/__init__.py
+++ b/assets/src/ba_data/python/ba/__init__.py
@@ -13,12 +13,11 @@ from _ba import (
Node, SessionPlayer, Sound, Texture, Timer, Vec3, Widget, buttonwidget,
camerashake, checkboxwidget, columnwidget, containerwidget, do_once,
emitfx, getactivity, getcollidemodel, getmodel, getnodes, getsession,
- getsound, gettexture, hscrollwidget, imagewidget, log, newactivity,
- newnode, playsound, printnodes, printobjects, pushcall, quit, rowwidget,
- safecolor, screenmessage, scrollwidget, set_analytics_screen, charstr,
- textwidget, time, timer, open_url, widget, clipboard_is_supported,
- clipboard_has_text, clipboard_get_text, clipboard_set_text, getdata,
- in_logic_thread)
+ getsound, gettexture, hscrollwidget, imagewidget, newactivity, newnode,
+ playsound, printnodes, printobjects, pushcall, quit, rowwidget, safecolor,
+ screenmessage, scrollwidget, set_analytics_screen, charstr, textwidget,
+ time, timer, open_url, widget, clipboard_is_supported, clipboard_has_text,
+ clipboard_get_text, clipboard_set_text, getdata, in_logic_thread)
from ba._activity import Activity
from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem
from ba._actor import Actor
@@ -103,7 +102,7 @@ __all__ = [
'ImpactDamageMessage', 'in_logic_thread', 'InputDevice',
'InputDeviceNotFoundError', 'InputType', 'IntChoiceSetting', 'IntSetting',
'is_browser_likely_available', 'is_point_in_box', 'Keyboard',
- 'LanguageSubsystem', 'Level', 'Lobby', 'log', 'Lstr', 'Map', 'Material',
+ 'LanguageSubsystem', 'Level', 'Lobby', 'Lstr', 'Map', 'Material',
'MetadataSubsystem', 'Model', 'MultiTeamSession', 'MusicPlayer',
'MusicPlayMode', 'MusicSubsystem', 'MusicType', 'newactivity', 'newnode',
'Node', 'NodeActor', 'NodeNotFoundError', 'normalized_color',
diff --git a/assets/src/ba_data/python/ba/_app.py b/assets/src/ba_data/python/ba/_app.py
index b1c012c9..b976517b 100644
--- a/assets/src/ba_data/python/ba/_app.py
+++ b/assets/src/ba_data/python/ba/_app.py
@@ -26,6 +26,7 @@ if TYPE_CHECKING:
import asyncio
from typing import Any, Callable
+ import efro.log
import ba
from ba._cloud import CloudSubsystem
from bastd.actor import spazappearance
@@ -49,6 +50,7 @@ class App:
# Implementations for these will be filled in by internal libs.
accounts_v2: AccountV2Subsystem
cloud: CloudSubsystem
+ log_handler: efro.log.LogHandler
class State(Enum):
"""High level state the app can be in."""
@@ -384,7 +386,7 @@ class App:
# If there's a leftover log file, attempt to upload it to the
# master-server and/or get rid of it.
- _apputils.handle_leftover_log_file()
+ _apputils.handle_leftover_v1_cloud_log_file()
# Only do this stuff if our config file is healthy so we don't
# overwrite a broken one or whatnot and wipe out data.
diff --git a/assets/src/ba_data/python/ba/_appconfig.py b/assets/src/ba_data/python/ba/_appconfig.py
index 13270319..4fd28deb 100644
--- a/assets/src/ba_data/python/ba/_appconfig.py
+++ b/assets/src/ba_data/python/ba/_appconfig.py
@@ -128,12 +128,6 @@ def read_config() -> tuple[AppConfig, bool]:
shutil.copyfile(config_file_path, config_file_path + '.broken')
except Exception as exc2:
print('EXC copying broken config:', exc2)
- try:
- _ba.log('broken config contents:\n' +
- config_contents.replace('\000', ''),
- to_stdout=False)
- except Exception as exc2:
- print('EXC logging broken config contents:', exc2)
config = AppConfig()
# Now attempt to read one of our 'prev' backup copies.
diff --git a/assets/src/ba_data/python/ba/_apputils.py b/assets/src/ba_data/python/ba/_apputils.py
index e1d5d626..d7c2cfe9 100644
--- a/assets/src/ba_data/python/ba/_apputils.py
+++ b/assets/src/ba_data/python/ba/_apputils.py
@@ -50,7 +50,7 @@ def should_submit_debug_info() -> bool:
return _ba.app.config.get('Submit Debug Info', True)
-def handle_log() -> None:
+def handle_v1_cloud_log() -> None:
"""Called on debug log prints.
When this happens, we can upload our log to the server
@@ -74,7 +74,7 @@ def handle_log() -> None:
activityname = 'unavailable'
info = {
- 'log': _ba.getlog(),
+ 'log': _ba.get_v1_cloud_log(),
'version': app.version,
'build': app.build_number,
'userAgentString': app.user_agent_string,
@@ -108,7 +108,7 @@ def handle_log() -> None:
def _reset() -> None:
app.log_upload_timer_started = False
if app.log_have_new:
- handle_log()
+ handle_v1_cloud_log()
if not _ba.is_log_full():
with _ba.Context('ui'):
@@ -118,14 +118,15 @@ def handle_log() -> None:
suppress_format_warning=True)
-def handle_leftover_log_file() -> None:
- """Handle an un-uploaded log from a previous run."""
+def handle_leftover_v1_cloud_log_file() -> None:
+ """Handle an un-uploaded v1-cloud-log from a previous run."""
try:
import json
from ba._net import master_server_post
- if os.path.exists(_ba.get_log_file_path()):
- with open(_ba.get_log_file_path(), encoding='utf-8') as infile:
+ if os.path.exists(_ba.get_v1_cloud_log_file_path()):
+ with open(_ba.get_v1_cloud_log_file_path(),
+ encoding='utf-8') as infile:
info = json.loads(infile.read())
infile.close()
do_send = should_submit_debug_info()
@@ -136,7 +137,7 @@ def handle_leftover_log_file() -> None:
# lets kill it.
if data is not None:
try:
- os.remove(_ba.get_log_file_path())
+ os.remove(_ba.get_v1_cloud_log_file_path())
except FileNotFoundError:
# Saw this in the wild. The file just existed
# a moment ago but I suppose something could have
@@ -146,7 +147,7 @@ def handle_leftover_log_file() -> None:
master_server_post('bsLog', info, response)
else:
# If they don't want logs uploaded just kill it.
- os.remove(_ba.get_log_file_path())
+ os.remove(_ba.get_v1_cloud_log_file_path())
except Exception:
from ba import _error
_error.print_exception('Error handling leftover log file.')
diff --git a/assets/src/ba_data/python/ba/_asyncio.py b/assets/src/ba_data/python/ba/_asyncio.py
index afb3adc4..3639d2af 100644
--- a/assets/src/ba_data/python/ba/_asyncio.py
+++ b/assets/src/ba_data/python/ba/_asyncio.py
@@ -18,7 +18,7 @@ import os
if TYPE_CHECKING:
import ba
-# Our timer and event loop for the ballistica game thread.
+# Our timer and event loop for the ballistica logic thread.
_asyncio_timer: ba.Timer | None = None
_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
diff --git a/assets/src/ba_data/python/ba/_bootstrap.py b/assets/src/ba_data/python/ba/_bootstrap.py
index 5dafc7f5..52b9bde7 100644
--- a/assets/src/ba_data/python/ba/_bootstrap.py
+++ b/assets/src/ba_data/python/ba/_bootstrap.py
@@ -5,13 +5,13 @@ from __future__ import annotations
import os
import sys
-import threading
from typing import TYPE_CHECKING
+from efro.log import setup_logging, LogLevel
import _ba
if TYPE_CHECKING:
- from typing import Any, TextIO, Callable
+ from efro.log import LogEntry
_g_did_bootstrap = False # pylint: disable=invalid-name
@@ -19,25 +19,31 @@ _g_did_bootstrap = False # pylint: disable=invalid-name
def bootstrap() -> None:
"""Run bootstrapping logic.
- This is the very first userland code that runs.
+ This is the very first ballistica code that runs (aside from imports).
It sets up low level environment bits and creates the app instance.
"""
+
global _g_did_bootstrap # pylint: disable=global-statement, invalid-name
if _g_did_bootstrap:
raise RuntimeError('Bootstrap has already been called.')
_g_did_bootstrap = True
- # The first thing we set up is capturing/redirecting Python
- # stdout/stderr so we can at least debug problems on systems where
- # native stdout/stderr is not easily accessible (looking at you, Android).
- sys.stdout = _Redirect(sys.stdout, _ba.print_stdout) # type: ignore
- sys.stderr = _Redirect(sys.stderr, _ba.print_stderr) # type: ignore
+ # The first thing we do is set up our logging system and feed
+ # Python's stdout/stderr into it. Then we can at least debug problems
+ # on systems where native stdout/stderr is not easily accessible
+ # such as Android.
+ log_handler = setup_logging(log_path=None,
+ level=LogLevel.DEBUG,
+ suppress_non_root_debug=True,
+ log_stdout_stderr=True)
+
+ log_handler.add_callback(_on_log)
env = _ba.env()
# Give a soft warning if we're being used with a different binary
# version than we expect.
- expected_build = 20842
+ expected_build = 20849
running_build: int = env['build_number']
if running_build != expected_build:
print(
@@ -85,42 +91,6 @@ def bootstrap() -> None:
os.environ['SSL_CERT_FILE'] = os.environ['REQUESTS_CA_BUNDLE'] = (
certifi.where())
- # FIXME: I think we should init Python in the main thread, which should
- # also avoid these issues. (and also might help us play better with
- # Python debuggers?)
-
- # Gloriously hacky workaround here:
- # Our 'main' Python thread is the game thread (not the app's main
- # thread) which means it has a small stack compared to the main
- # thread (at least on apple). Sadly it turns out this causes the
- # debug build of Python to blow its stack immediately when doing
- # some big imports.
- # Normally we'd just give the game thread the same stack size as
- # the main thread and that'd be the end of it. However
- # we're using std::threads which it turns out have no way to set
- # the stack size (as of fall '19). Grumble.
- #
- # However python threads *can* take custom stack sizes.
- # (and it appears they might use the main thread's by default?..)
- # ...so as a workaround in the debug version, we can run problematic
- # heavy imports here in another thread and all is well.
- # If we ever see stack overflows in our release build we'll have
- # to take more drastic measures like switching from std::threads
- # to pthreads.
-
- if debug_build:
-
- # noinspection PyUnresolvedReferences
- def _thread_func() -> None:
- # pylint: disable=unused-import
- import json
- import urllib.request
-
- testthread = threading.Thread(target=_thread_func)
- testthread.start()
- testthread.join()
- del testthread
-
# Clear out the standard quit/exit messages since they don't work for us.
# pylint: disable=c-extension-no-member
if not TYPE_CHECKING:
@@ -132,54 +102,20 @@ def bootstrap() -> None:
from ba._app import App
import ba
_ba.app = ba.app = App()
+ _ba.app.log_handler = log_handler
-class _Redirect:
+def _on_log(entry: LogEntry) -> None:
- def __init__(self, original: TextIO, call: Callable[[str], None]) -> None:
- self._lock = threading.Lock()
- self._linebits: list[str] = []
- self._original = original
- self._call = call
- self._pending_ship = False
+ # Just forward this along to the engine to display in the in-game console,
+ # in the Android log, etc.
+ _ba.display_log(name=entry.name,
+ level=entry.level.name,
+ message=entry.message)
- def write(self, sval: Any) -> None:
- """Override standard write call."""
-
- self._call(sval)
-
- # Now do logging:
- # Add it to our accumulated line.
- # If the message ends in a newline, we can ship it
- # immediately as a log entry. Otherwise, schedule a ship
- # next cycle (if it hasn't yet at that point) so that we
- # can accumulate subsequent prints.
- # (so stuff like print('foo', 123, 'bar') will ship as one entry)
- with self._lock:
- self._linebits.append(sval)
- if sval.endswith('\n'):
- self._shiplog()
- else:
- _ba.pushcall(self._shiplog,
- from_other_thread=True,
- suppress_other_thread_warning=True)
-
- def _shiplog(self) -> None:
- with self._lock:
- line = ''.join(self._linebits)
- if not line:
- return
- self._linebits = []
-
- # Log messages aren't expected to have trailing newlines.
- if line.endswith('\n'):
- line = line[:-1]
- _ba.log(line, to_stdout=False)
-
- def flush(self) -> None:
- """Flush the file."""
- self._original.flush()
-
- def isatty(self) -> bool:
- """Are we a terminal?"""
- return self._original.isatty()
+ # We also want to feed some logs to the old V1-cloud-log system.
+ # Let's go with anything warning or higher as well as the stdout/stderr
+ # log messages that ba.app.log_handler creates for us.
+ if entry.level.value >= LogLevel.WARNING.value or entry.name in ('stdout',
+ 'stderr'):
+ _ba.v1_cloud_log(entry.message)
diff --git a/assets/src/ba_data/python/ba/_error.py b/assets/src/ba_data/python/ba/_error.py
index bb82a78a..5e6aba42 100644
--- a/assets/src/ba_data/python/ba/_error.py
+++ b/assets/src/ba_data/python/ba/_error.py
@@ -125,6 +125,11 @@ class WidgetNotFoundError(NotFoundError):
"""
+# TODO: Should integrate some sort of context printing into our
+# log handling so we can just use logging.exception() and kill these
+# two functions.
+
+
def print_exception(*args: Any, **keywds: Any) -> None:
"""Print info about an exception along with pertinent context state.
diff --git a/assets/src/ba_data/python/ba/_hooks.py b/assets/src/ba_data/python/ba/_hooks.py
index 9d103449..ab7e36cd 100644
--- a/assets/src/ba_data/python/ba/_hooks.py
+++ b/assets/src/ba_data/python/ba/_hooks.py
@@ -28,7 +28,7 @@ def finish_bootstrapping() -> None:
assert _ba.in_logic_thread()
# Kick off our asyncio event handling, allowing us to use coroutines
- # in our game thread alongside our internal event handling.
+ # in our logic thread alongside our internal event handling.
# setup_asyncio()
# Ok, bootstrapping is done; time to get the show started.
diff --git a/assets/src/ba_data/python/ba/_meta.py b/assets/src/ba_data/python/ba/_meta.py
index 40f75619..1faca382 100644
--- a/assets/src/ba_data/python/ba/_meta.py
+++ b/assets/src/ba_data/python/ba/_meta.py
@@ -192,13 +192,13 @@ class MetadataSubsystem:
color=(1, 0, 0))
_ba.playsound(_ba.getsound('error'))
if results.warnings:
- _ba.log(textwrap.indent('\n'.join(results.warnings),
- 'Warning (meta-scan): '),
- to_server=False)
+ allwarnings = textwrap.indent('\n'.join(results.warnings),
+ 'Warning (meta-scan): ')
+ logging.warning(allwarnings)
if results.errors:
- _ba.log(
- textwrap.indent('\n'.join(results.errors),
- 'Error (meta-scan): '))
+ allerrors = textwrap.indent('\n'.join(results.errors),
+ 'Error (meta-scan): ')
+ logging.error(allerrors)
# Let the game know we're done.
assert self._scan_complete_cb is not None
diff --git a/assets/src/ba_data/python/ba/_playlist.py b/assets/src/ba_data/python/ba/_playlist.py
index 1550934c..5518601a 100644
--- a/assets/src/ba_data/python/ba/_playlist.py
+++ b/assets/src/ba_data/python/ba/_playlist.py
@@ -5,6 +5,7 @@
from __future__ import annotations
import copy
+import logging
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
@@ -29,7 +30,6 @@ def filter_playlist(playlist: PlaylistType,
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
- import _ba
from ba._map import get_filtered_map_name
from ba._store import get_unowned_maps, get_unowned_game_types
from ba._general import getclass
@@ -140,8 +140,8 @@ def filter_playlist(playlist: PlaylistType,
entry['settings'][setting.name] = setting.default
goodlist.append(entry)
except ImportError as exc:
- _ba.log(f'Import failed while scanning playlist \'{name}\': {exc}',
- to_server=False)
+ logging.warning('Import failed while scanning playlist \'%s\': %s',
+ name, exc)
except Exception:
from ba import _error
_error.print_exception()
diff --git a/assets/src/ba_data/python/ba/_plugin.py b/assets/src/ba_data/python/ba/_plugin.py
index c4702045..d05cdbe9 100644
--- a/assets/src/ba_data/python/ba/_plugin.py
+++ b/assets/src/ba_data/python/ba/_plugin.py
@@ -4,6 +4,7 @@
from __future__ import annotations
+import logging
from typing import TYPE_CHECKING
from dataclasses import dataclass
@@ -127,8 +128,7 @@ class PluginSubsystem:
subs=[('${PLUGIN}', plugkey),
('${ERROR}', str(exc))]),
color=(1, 0, 0))
- _ba.log(f"Error loading plugin class '{plugkey}': {exc}",
- to_server=False)
+ logging.exception("Error loading plugin class '%s'", plugkey)
continue
try:
plugin = cls()
@@ -155,10 +155,8 @@ class PluginSubsystem:
color=(1, 1, 0),
)
plugnames = ', '.join(disappeared_plugs)
- _ba.log(
- f'{len(disappeared_plugs)} plugin(s) no longer found:'
- f' {plugnames}.',
- to_server=False)
+ logging.warning('%d plugin(s) no longer found: %s.',
+ len(disappeared_plugs), plugnames)
for goneplug in disappeared_plugs:
del _ba.app.config['Plugins'][goneplug]
_ba.app.config.commit()
diff --git a/assets/src/ba_data/python/ba/_servermode.py b/assets/src/ba_data/python/ba/_servermode.py
index ebc7f20d..8c8ed981 100644
--- a/assets/src/ba_data/python/ba/_servermode.py
+++ b/assets/src/ba_data/python/ba/_servermode.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import sys
import time
+import logging
from typing import TYPE_CHECKING
from efro.terminal import Clr
@@ -334,11 +335,11 @@ class ServerController:
if self._first_run:
curtimestr = time.strftime('%c')
- _ba.log(
+ startupmsg = (
f'{Clr.BLD}{Clr.BLU}{_ba.appnameupper()} {app.version}'
f' ({app.build_number})'
- f' entering server-mode {curtimestr}{Clr.RST}',
- to_server=False)
+ f' entering server-mode {curtimestr}{Clr.RST}')
+ logging.info(startupmsg)
if sessiontype is FreeForAllSession:
appcfg['Free-for-All Playlist Selection'] = self._playlist_name
diff --git a/ballisticacore-cmake/.idea/dictionaries/ericf.xml b/ballisticacore-cmake/.idea/dictionaries/ericf.xml
index 16d6b197..487dfc87 100644
--- a/ballisticacore-cmake/.idea/dictionaries/ericf.xml
+++ b/ballisticacore-cmake/.idea/dictionaries/ericf.xml
@@ -41,7 +41,9 @@
airborn
alext
alibaba
+ allerrors
allocs
+ allwarnings
alot
alphaimg
alphapixels
@@ -1243,6 +1245,7 @@
startpos
startsplits
starttime
+ startupmsg
startx
starty
statestr
@@ -1262,6 +1265,7 @@
strdup
stringi
stringified
+ stringifying
strlen
strs
strtof
diff --git a/src/ballistica/app/app.h b/src/ballistica/app/app.h
index babeecd3..754d42e1 100644
--- a/src/ballistica/app/app.h
+++ b/src/ballistica/app/app.h
@@ -32,10 +32,10 @@ class App {
std::vector pausable_threads;
TouchInput* touch_input{};
std::string console_startup_messages;
- std::mutex log_mutex;
- std::string log;
- bool put_log{};
- bool log_full{};
+ std::mutex v1_cloud_log_mutex;
+ std::string v1_cloud_log;
+ bool did_put_v1_cloud_log{};
+ bool v1_cloud_log_full{};
int master_server_source{0};
int session_count{};
bool shutting_down{};
diff --git a/src/ballistica/app/app_config.h b/src/ballistica/app/app_config.h
index ed45fdc9..9a970b4a 100644
--- a/src/ballistica/app/app_config.h
+++ b/src/ballistica/app/app_config.h
@@ -13,7 +13,7 @@ namespace ballistica {
// This class wrangles user config values for the app.
// The underlying config data currently lives in the Python layer,
-// so at the moment these calls are only usable from the game thread,
+// so at the moment these calls are only usable from the logic thread,
// but that may change in the future.
class AppConfig {
public:
diff --git a/src/ballistica/assets/assets.cc b/src/ballistica/assets/assets.cc
index 9393e7a8..8bcc5ee1 100644
--- a/src/ballistica/assets/assets.cc
+++ b/src/ballistica/assets/assets.cc
@@ -294,7 +294,7 @@ void Assets::PrintLoadInfo() {
snprintf(buffer, sizeof(buffer), " %-50s %10s %10s", "FILE",
"PRELOAD_TIME", "LOAD_TIME");
s += buffer;
- Log(s, true, false);
+ Log(LogLevel::kInfo, s);
millisecs_t total_preload_time = 0;
millisecs_t total_load_time = 0;
assert(asset_lists_locked_);
@@ -307,7 +307,7 @@ void Assets::PrintLoadInfo() {
i.second->GetName().c_str(),
static_cast_check_fit(preload_time),
static_cast_check_fit(load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
num++;
}
assert(asset_lists_locked_);
@@ -320,7 +320,7 @@ void Assets::PrintLoadInfo() {
i.second->GetName().c_str(),
static_cast_check_fit(preload_time),
static_cast_check_fit(load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
num++;
}
assert(asset_lists_locked_);
@@ -333,7 +333,7 @@ void Assets::PrintLoadInfo() {
i.second->GetName().c_str(),
static_cast_check_fit(preload_time),
static_cast_check_fit(load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
num++;
}
assert(asset_lists_locked_);
@@ -346,7 +346,7 @@ void Assets::PrintLoadInfo() {
i.second->GetName().c_str(),
static_cast_check_fit(preload_time),
static_cast_check_fit(load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
num++;
}
assert(asset_lists_locked_);
@@ -359,7 +359,7 @@ void Assets::PrintLoadInfo() {
i.second->file_name_full().c_str(),
static_cast_check_fit(preload_time),
static_cast_check_fit(load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
num++;
}
snprintf(buffer, sizeof(buffer),
@@ -367,7 +367,7 @@ void Assets::PrintLoadInfo() {
"(feeding data to OpenGL, etc): %i",
static_cast(total_preload_time),
static_cast(total_load_time));
- Log(buffer, true, false);
+ Log(LogLevel::kInfo, buffer);
}
void Assets::MarkAllAssetsForLoad() {
@@ -810,8 +810,8 @@ auto Assets::RunPendingLoadList(std::vector*>* c_list) -> bool {
}
}
- // if we dumped anything on the pending loads done list, shake the game thread
- // to tell it to kill the reference..
+ // if we dumped anything on the pending loads done list, shake the logic
+ // thread to tell it to kill the reference..
if (!l_finished.empty()) {
assert(g_logic);
g_logic->PushHavePendingLoadsDoneCall();
@@ -977,7 +977,7 @@ void Assets::Prune(int level) {
if (current_time - collide_model_data->last_used_time()
> standard_asset_prune_time
&& (collide_model_data->object_strong_ref_count() <= 1)) {
- // we can unload it immediately since that happens in the game thread...
+ // we can unload it immediately since that happens in the logic thread...
collide_model_data->Unload();
auto i_next = i;
++i_next;
@@ -1151,10 +1151,12 @@ auto Assets::FindAssetFile(FileType type, const std::string& name)
// We wanna fail gracefully for some types.
if (type == FileType::kSound && name != "blank") {
- Log("Unable to load audio: '" + name + "'; trying fallback...");
+ Log(LogLevel::kError,
+ "Unable to load audio: '" + name + "'; trying fallback...");
return FindAssetFile(type, "blank");
} else if (type == FileType::kTexture && name != "white") {
- Log("Unable to load texture: '" + name + "'; trying fallback...");
+ Log(LogLevel::kError,
+ "Unable to load texture: '" + name + "'; trying fallback...");
return FindAssetFile(type, "white");
}
@@ -1181,7 +1183,7 @@ void Assets::AddPendingLoad(Object::Ref* c) {
break;
}
default: {
- // Tell the game thread there's pending loads.
+ // Tell the logic thread there's pending loads.
{
std::scoped_lock lock(pending_load_list_mutex_);
pending_loads_other_.push_back(c);
@@ -1198,7 +1200,7 @@ void Assets::ClearPendingLoadsDoneList() {
std::scoped_lock lock(pending_load_list_mutex_);
// Our explicitly-allocated reference pointer has made it back to us here in
- // the game thread.
+ // the logic thread.
// We can now kill the reference knowing that it's safe for this component
// to die at any time (anyone needing it to be alive now should be holding a
// reference themselves).
@@ -1213,7 +1215,7 @@ void Assets::AddPackage(const std::string& name, const std::string& path) {
assert(InLogicThread());
#if BA_DEBUG_BUILD
if (packages_.find(name) != packages_.end()) {
- Log("WARNING: adding duplicate package: '" + name + "'");
+ Log(LogLevel::kWarning, "adding duplicate package: '" + name + "'");
}
#endif // BA_DEBUG_BUILD
packages_[name] = path;
diff --git a/src/ballistica/assets/assets.h b/src/ballistica/assets/assets.h
index 529d4856..a23bcefe 100644
--- a/src/ballistica/assets/assets.h
+++ b/src/ballistica/assets/assets.h
@@ -67,7 +67,7 @@ class Assets {
/// recreating/adjusting the renderer.
auto UnloadRendererBits(bool textures, bool models) -> void;
- /// Should be called from the game thread after UnloadRendererBits();
+ /// Should be called from the logic thread after UnloadRendererBits();
/// kicks off bg loads for all existing unloaded assets.
auto MarkAllAssetsForLoad() -> void;
auto PrintLoadInfo() -> void;
diff --git a/src/ballistica/assets/assets_server.cc b/src/ballistica/assets/assets_server.cc
index 733e9b35..81168ff9 100644
--- a/src/ballistica/assets/assets_server.cc
+++ b/src/ballistica/assets/assets_server.cc
@@ -57,7 +57,8 @@ void AssetsServer::PushBeginWriteReplayCall() {
// we only allow writing one replay at once; make sure that's actually the
// case
if (writing_replay_) {
- Log("AssetsServer got BeginWriteReplayCall while already writing");
+ Log(LogLevel::kError,
+ "AssetsServer got BeginWriteReplayCall while already writing");
WriteReplayMessages();
if (replay_out_file_) {
fclose(replay_out_file_);
@@ -76,7 +77,8 @@ void AssetsServer::PushBeginWriteReplayCall() {
replay_bytes_written_ = 0;
if (!replay_out_file_) {
- Log("ERROR: unable to open output-stream file: '" + file_path + "'");
+ Log(LogLevel::kError,
+ "unable to open output-stream file: '" + file_path + "'");
} else {
// write file id and protocol-version
// NOTE - we always write replays in our host protocol version
@@ -87,8 +89,8 @@ void AssetsServer::PushBeginWriteReplayCall() {
|| (fwrite(&version, sizeof(version), 1, replay_out_file_) != 1)) {
fclose(replay_out_file_);
replay_out_file_ = nullptr;
- Log("error writing replay file header: "
- + g_platform->GetErrnoString());
+ Log(LogLevel::kError, "error writing replay file header: "
+ + g_platform->GetErrnoString());
}
replay_bytes_written_ = 5;
}
@@ -108,7 +110,8 @@ void AssetsServer::PushAddMessageToReplayCall(
// sanity check..
if (!writing_replay_) {
- Log("AssetsServer got AddMessageToReplayCall while not writing replay");
+ Log(LogLevel::kError,
+ "AssetsServer got AddMessageToReplayCall while not writing replay");
replays_broken_ = true;
return;
}
@@ -118,7 +121,8 @@ void AssetsServer::PushAddMessageToReplayCall(
// if we've got too much data built up (lets go with 10 megs for now),
// abort
if (replay_message_bytes_ > 10000000) {
- Log("replay output buffer exceeded 10 megs; aborting replay");
+ Log(LogLevel::kError,
+ "replay output buffer exceeded 10 megs; aborting replay");
fclose(replay_out_file_);
replay_out_file_ = nullptr;
replay_message_bytes_ = 0;
@@ -139,7 +143,7 @@ void AssetsServer::PushEndWriteReplayCall() {
// sanity check..
if (!writing_replay_) {
- Log("_finishWritingReplay called while not writing");
+ Log(LogLevel::kError, "_finishWritingReplay called while not writing");
replays_broken_ = true;
return;
}
@@ -176,7 +180,8 @@ void AssetsServer::WriteReplayMessages() {
if (fwrite(&len8, 1, 1, replay_out_file_) != 1) {
fclose(replay_out_file_);
replay_out_file_ = nullptr;
- Log("error writing replay file: " + g_platform->GetErrnoString());
+ Log(LogLevel::kError,
+ "error writing replay file: " + g_platform->GetErrnoString());
return;
}
}
@@ -187,14 +192,16 @@ void AssetsServer::WriteReplayMessages() {
if (fwrite(&len16, 2, 1, replay_out_file_) != 1) {
fclose(replay_out_file_);
replay_out_file_ = nullptr;
- Log("error writing replay file: " + g_platform->GetErrnoString());
+ Log(LogLevel::kError,
+ "error writing replay file: " + g_platform->GetErrnoString());
return;
}
} else {
if (fwrite(&len32, 4, 1, replay_out_file_) != 1) {
fclose(replay_out_file_);
replay_out_file_ = nullptr;
- Log("error writing replay file: " + g_platform->GetErrnoString());
+ Log(LogLevel::kError,
+ "error writing replay file: " + g_platform->GetErrnoString());
return;
}
}
@@ -205,7 +212,8 @@ void AssetsServer::WriteReplayMessages() {
if (result != 1) {
fclose(replay_out_file_);
replay_out_file_ = nullptr;
- Log("error writing replay file: " + g_platform->GetErrnoString());
+ Log(LogLevel::kError,
+ "error writing replay file: " + g_platform->GetErrnoString());
return;
}
replay_bytes_written_ += data_compressed.size() + 2;
diff --git a/src/ballistica/assets/data/data_data.cc b/src/ballistica/assets/data/data_data.cc
index 770899e6..0a509fc4 100644
--- a/src/ballistica/assets/data/data_data.cc
+++ b/src/ballistica/assets/data/data_data.cc
@@ -20,8 +20,8 @@ void DataData::DoPreload() {
// in the following case:
// - asset thread grabs payload lock for Preload()
// - asset thread tries to grab GIL in Preload(); spins.
- // - meanwhile, something in game thread has called Load()
- // - game thread holds GIL by default and now spins waiting on payload lock.
+ // - meanwhile, something in logic thread has called Load()
+ // - logic thread holds GIL by default and now spins waiting on payload lock.
// - deadlock :-(
// ...so the new plan is to simply load the file into a string in Preload()
diff --git a/src/ballistica/assets/data/sound_data.cc b/src/ballistica/assets/data/sound_data.cc
index 804b7fb9..5b257c0d 100644
--- a/src/ballistica/assets/data/sound_data.cc
+++ b/src/ballistica/assets/data/sound_data.cc
@@ -56,8 +56,8 @@ static auto LoadOgg(const char* file_name, std::vector* buffer,
f = g_platform->FOpen(file_name, "rb");
if (f == nullptr) {
fallback = true;
- Log(std::string("Error: Can't open sound file '") + file_name
- + "' for reading...");
+ Log(LogLevel::kError, std::string("Can't open sound file '") + file_name
+ + "' for reading...");
// Attempt a fallback standin; if that doesn't work, throw in the towel.
file_name = "data/global/audio/blank.ogg";
@@ -77,7 +77,8 @@ static auto LoadOgg(const char* file_name, std::vector* buffer,
// Try opening the given file
if (ov_open_callbacks(f, &ogg_file, nullptr, 0, callbacks) != 0) {
- Log(std::string("Error decoding sound file '") + file_name + "'");
+ Log(LogLevel::kError,
+ std::string("Error decoding sound file '") + file_name + "'");
fclose(f);
@@ -199,8 +200,9 @@ static void LoadCachedOgg(const char* file_name, std::vector* buffer,
// with invalid formats of 0 once. Report and ignore if we see
// something like that.
if (*format != AL_FORMAT_MONO16 && *format != AL_FORMAT_STEREO16) {
- Log(std::string("Ignoring invalid audio cache of ") + file_name
- + " with format " + std::to_string(*format));
+ Log(LogLevel::kError, std::string("Ignoring invalid audio cache of ")
+ + file_name + " with format "
+ + std::to_string(*format));
} else {
return; // SUCCESS!!!!
}
diff --git a/src/ballistica/audio/al_sys.cc b/src/ballistica/audio/al_sys.cc
index 11518df6..c39a14d2 100644
--- a/src/ballistica/audio/al_sys.cc
+++ b/src/ballistica/audio/al_sys.cc
@@ -16,13 +16,14 @@ namespace ballistica {
void _check_al_error(const char* file, int line) {
if (g_audio_server->paused()) {
- Log(Utils::BaseName(file) + ":" + std::to_string(line)
- + ": Checking OpenAL error while paused.");
+ Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line)
+ + ": Checking OpenAL error while paused.");
}
ALenum al_err = alGetError();
if (al_err != AL_NO_ERROR) {
- Log(Utils::BaseName(file) + ":" + std::to_string(line)
- + ": OpenAL Error: " + GetALErrorString(al_err) + ";");
+ Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line)
+ + ": OpenAL Error: " + GetALErrorString(al_err)
+ + ";");
}
}
diff --git a/src/ballistica/audio/audio_server.cc b/src/ballistica/audio/audio_server.cc
index a63451d7..295bb803 100644
--- a/src/ballistica/audio/audio_server.cc
+++ b/src/ballistica/audio/audio_server.cc
@@ -144,7 +144,7 @@ struct AudioServer::SoundFadeNode {
void AudioServer::SetPaused(bool pause) {
if (!paused_) {
if (!pause) {
- Log("Error: got audio unpause request when already unpaused.");
+ Log(LogLevel::kError, "Got audio unpause request when already unpaused.");
} else {
#if BA_OSTYPE_IOS_TVOS
// apple recommends this during audio-interruptions..
@@ -163,7 +163,7 @@ void AudioServer::SetPaused(bool pause) {
} else {
// unpause if requested..
if (pause) {
- Log("Error: Got audio pause request when already paused.");
+ Log(LogLevel::kError, "Got audio pause request when already paused.");
} else {
#if BA_OSTYPE_IOS_TVOS
// apple recommends this during audio-interruptions..
@@ -357,7 +357,7 @@ auto AudioServer::OnAppStartInThread() -> void {
ALboolean enumeration =
alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT");
if (enumeration == AL_FALSE) {
- Log("OpenAL enumeration extensions missing.");
+ Log(LogLevel::kError, "OpenAL enumeration extensions missing.");
} else {
const ALCchar* devices =
alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
@@ -414,8 +414,8 @@ auto AudioServer::OnAppStartInThread() -> void {
sound_source_refs_.push_back(s);
sources_.push_back(&(*s));
} else {
- Log("Error: Made " + std::to_string(i) + " sources; (wanted "
- + std::to_string(target_source_count) + ").");
+ Log(LogLevel::kError, "Made " + std::to_string(i) + " sources; (wanted "
+ + std::to_string(target_source_count) + ").");
break;
}
}
@@ -473,9 +473,10 @@ void AudioServer::UpdateAvailableSources() {
// that probably means somebody's grabbing a source but never
// resubmitting it.
if (t - i->client_source()->last_lock_time() > 10000) {
- Log("Error: Client audio source has been locked for too long; "
+ Log(LogLevel::kError,
+ "Client audio source has been locked for too long; "
"probably leaked. (debug id "
- + std::to_string(i->client_source()->lock_debug_id()) + ")");
+ + std::to_string(i->client_source()->lock_debug_id()) + ")");
}
continue;
}
@@ -694,8 +695,8 @@ AudioServer::ThreadSource::ThreadSource(AudioServer* audio_thread_in, int id_in,
ALenum err = alGetError();
valid_ = (err == AL_NO_ERROR);
if (!valid_) {
- Log(std::string("Error: AL Error ") + GetALErrorString(err)
- + " on source creation.");
+ Log(LogLevel::kError, std::string("AL Error ") + GetALErrorString(err)
+ + " on source creation.");
} else {
// In vr mode we keep the microphone a bit closer to the camera
// for realism purposes, so we need stuff louder in general.
@@ -878,9 +879,9 @@ void AudioServer::ThreadSource::SetPosition(float x, float y, float z) {
z = 500;
}
if (oob) {
- BA_LOG_ONCE(
- "Error: AudioServer::ThreadSource::SetPosition"
- " got out-of-bounds value.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "AudioServer::ThreadSource::SetPosition"
+ " got out-of-bounds value.");
}
ALfloat source_pos[] = {x, y, z};
alSourcefv(source_, AL_POSITION, source_pos);
@@ -1098,7 +1099,7 @@ void AudioServer::PushSetSoundPitchCall(float val) {
void AudioServer::PushSetPausedCall(bool pause) {
thread()->PushCall([this, pause] {
if (g_buildconfig.ostype_android()) {
- Log("Error: Shouldn't be getting SetPausedCall on android.");
+ Log(LogLevel::kError, "Shouldn't be getting SetPausedCall on android.");
}
SetPaused(pause);
});
@@ -1111,7 +1112,7 @@ void AudioServer::PushComponentUnloadCall(
for (auto&& i : components) {
(**i).Unload();
}
- // ...and then ship these pointers back to the game thread, so it can free
+ // ...and then ship these pointers back to the logic thread, so it can free
// the references.
g_logic->PushFreeAssetComponentRefsCall(components);
});
@@ -1129,7 +1130,7 @@ void AudioServer::AddSoundRefDelete(const Object::Ref* c) {
std::scoped_lock lock(sound_ref_delete_list_mutex_);
sound_ref_delete_list_.push_back(c);
}
- // Now push a call to the game thread to do the deletes.
+ // Now push a call to the logic thread to do the deletes.
g_logic->thread()->PushCall(
[] { g_audio_server->ClearSoundRefDeleteList(); });
}
@@ -1154,7 +1155,7 @@ void AudioServer::BeginInterruption() {
break;
}
if (GetRealTime() - t > 1000) {
- Log("Error: Timed out waiting for audio pause.");
+ Log(LogLevel::kError, "Timed out waiting for audio pause.");
break;
}
Platform::SleepMS(2);
@@ -1176,7 +1177,7 @@ void AudioServer::EndInterruption() {
break;
}
if (GetRealTime() - t > 1000) {
- Log("Error: Timed out waiting for audio unpause.");
+ Log(LogLevel::kError, "Timed out waiting for audio unpause.");
break;
}
Platform::SleepMS(2);
diff --git a/src/ballistica/audio/audio_server.h b/src/ballistica/audio/audio_server.h
index 9efb480a..e8d6720e 100644
--- a/src/ballistica/audio/audio_server.h
+++ b/src/ballistica/audio/audio_server.h
@@ -101,7 +101,7 @@ class AudioServer {
// Some threads such as audio hold onto allocated Media-Component-Refs to keep
// media components alive that they need. Media-Component-Refs, however, must
- // be disposed of in the game thread, so they are passed back to it through
+ // be disposed of in the logic thread, so they are passed back to it through
// this function.
auto AddSoundRefDelete(const Object::Ref* c) -> void;
diff --git a/src/ballistica/audio/audio_source.cc b/src/ballistica/audio/audio_source.cc
index 492fd0ec..4e391f34 100644
--- a/src/ballistica/audio/audio_source.cc
+++ b/src/ballistica/audio/audio_source.cc
@@ -41,7 +41,7 @@ void AudioSource::SetPosition(float x, float y, float z) {
assert(client_queue_size_ > 0);
#if BA_DEBUG_BUILD
if (std::isnan(x) || std::isnan(y) || std::isnan(z)) {
- Log("Error: Got nan value in AudioSource::SetPosition.");
+ Log(LogLevel::kError, "Got nan value in AudioSource::SetPosition.");
}
#endif
g_audio_server->PushSourceSetPositionCall(play_id_, Vector3f(x, y, z));
diff --git a/src/ballistica/audio/audio_streamer.cc b/src/ballistica/audio/audio_streamer.cc
index d44a9982..9cdee4a1 100644
--- a/src/ballistica/audio/audio_streamer.cc
+++ b/src/ballistica/audio/audio_streamer.cc
@@ -86,8 +86,9 @@ void AudioStreamer::Update() {
// A fun anomaly in the linux version; we sometimes get more
// "processed" buffers than we have queued.
if (queued < processed) {
- Log("Error: streamer oddness: queued(" + std::to_string(queued)
- + "); processed(" + std::to_string(processed) + ")");
+ Log(LogLevel::kError, "Streamer oddness: queued(" + std::to_string(queued)
+ + "); processed(" + std::to_string(processed)
+ + ")");
processed = queued;
}
diff --git a/src/ballistica/audio/ogg_stream.cc b/src/ballistica/audio/ogg_stream.cc
index 93286491..68b40ef6 100644
--- a/src/ballistica/audio/ogg_stream.cc
+++ b/src/ballistica/audio/ogg_stream.cc
@@ -88,7 +88,8 @@ void OggStream::DoStream(char* pcm, int* size, unsigned int* rate) {
static bool reported_error = false;
if (!reported_error) {
reported_error = true;
- Log("Error streaming ogg file: '" + file_name() + "'.");
+ Log(LogLevel::kError,
+ "Error streaming ogg file: '" + file_name() + "'.");
}
if (loops()) {
ov_pcm_seek(&ogg_file_, 0);
diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc
index 355a3e30..4c2ea9b2 100644
--- a/src/ballistica/ballistica.cc
+++ b/src/ballistica/ballistica.cc
@@ -32,13 +32,13 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kAppBuildNumber = 20842;
+const int kAppBuildNumber = 20849;
const char* kAppVersion = "1.7.7";
// Our standalone globals.
// These are separated out for easy access.
// Everything else should go into App (or more ideally into a class).
-int g_early_log_writes{10};
+int g_early_v1_cloud_log_writes{10};
App* g_app{};
AppConfig* g_app_config{};
@@ -86,8 +86,8 @@ auto BallisticaMain(int argc, char** argv) -> int {
// avoid any logic that accesses other globals since they may
// not yet exist.
- // Minimal globals we must assign immediately as they are needed
- // during construction of the others.
+ // Minimal globals we must assign immediately as they ARE needed
+ // for construction of the others (would be great to eliminate this need).
g_platform = Platform::Create();
g_app = new App(argc, argv);
g_app_internal = CreateAppInternal();
@@ -267,49 +267,56 @@ auto GetAppInstanceUUID() -> const std::string& {
}
if (!have_session_id) {
// As an emergency fallback simply use a single random number.
- Log("WARNING: GetSessionUUID() using rand fallback.");
+ Log(LogLevel::kWarning, "GetSessionUUID() using rand fallback.");
srand(static_cast(
Platform::GetCurrentMilliseconds())); // NOLINT
session_id = std::to_string(static_cast(rand())); // NOLINT
have_session_id = true;
}
if (session_id.size() >= 100) {
- Log("WARNING: session id longer than it should be.");
+ Log(LogLevel::kWarning, "session id longer than it should be.");
}
}
return session_id;
}
+auto InMainThread() -> bool {
+ assert(g_main_thread); // Root out early use of this.
+ return (g_main_thread->IsCurrent());
+}
+
auto InLogicThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_logic && g_logic->thread()->IsCurrent());
}
-auto InMainThread() -> bool {
- return (g_app && std::this_thread::get_id() == g_app->main_thread_id);
-}
-
auto InGraphicsThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_graphics_server && g_graphics_server->thread()->IsCurrent());
}
auto InAudioThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_audio_server && g_audio_server->thread()->IsCurrent());
}
auto InBGDynamicsThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_bg_dynamics_server && g_bg_dynamics_server->thread()->IsCurrent());
}
auto InAssetsThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_assets_server && g_assets_server->thread()->IsCurrent());
}
auto InNetworkWriteThread() -> bool {
+ assert(g_app && g_app->is_bootstrapped); // Root out early use of this.
return (g_network_writer && g_network_writer->thread()->IsCurrent());
}
-auto Log(const std::string& msg, bool to_stdout, bool to_server) -> void {
- Logging::Log(msg, to_stdout, to_server);
+auto Log(LogLevel level, const std::string& msg) -> void {
+ Logging::Log(level, msg);
}
auto IsVRMode() -> bool { return g_app->vr_mode; }
@@ -318,7 +325,8 @@ void ScreenMessage(const std::string& s, const Vector3f& color) {
if (g_logic) {
g_logic->PushScreenMessage(s, color);
} else {
- Log("ScreenMessage before g_logic init (will be lost): '" + s + "'");
+ Log(LogLevel::kError,
+ "ScreenMessage before g_logic init (will be lost): '" + s + "'");
}
}
diff --git a/src/ballistica/ballistica.h b/src/ballistica/ballistica.h
index eaab6d8b..33cf255a 100644
--- a/src/ballistica/ballistica.h
+++ b/src/ballistica/ballistica.h
@@ -102,7 +102,7 @@ const float kGameStepSeconds =
(static_cast(kGameStepMilliseconds) / 1000.0f);
// Globals.
-extern int g_early_log_writes;
+extern int g_early_v1_cloud_log_writes;
extern V1Account* g_v1_account;
extern AppFlavor* g_app_flavor;
extern AppConfig* g_app_config;
@@ -168,8 +168,7 @@ auto GetCurrentThreadName() -> std::string;
/// Write a string to the log.
/// This will go to stdout, windows debug log, android log, etc.
/// A trailing newline will be added.
-auto Log(const std::string& msg, bool to_stdout = true, bool to_server = true)
- -> void;
+auto Log(LogLevel level, const std::string& msg) -> void;
/// Log a fatal error and kill the app.
/// Can be called from any thread at any time.
diff --git a/src/ballistica/core/context.cc b/src/ballistica/core/context.cc
index 632f9f1b..c99d265e 100644
--- a/src/ballistica/core/context.cc
+++ b/src/ballistica/core/context.cc
@@ -100,7 +100,7 @@ auto ContextTarget::NewTimer(TimeType timetype, TimerMedium length, bool repeat,
void ContextTarget::DeleteTimer(TimeType timetype, int timer_id) {
// We throw on NewTimer; lets just ignore anything that comes
// through here to avoid messing up destructors.
- Log("ContextTarget::DeleteTimer() called; unexpected.");
+ Log(LogLevel::kError, "ContextTarget::DeleteTimer() called; unexpected.");
}
auto ContextTarget::GetTime(TimeType timetype) -> millisecs_t {
diff --git a/src/ballistica/core/context.h b/src/ballistica/core/context.h
index 68b0d101..bd96ec13 100644
--- a/src/ballistica/core/context.h
+++ b/src/ballistica/core/context.h
@@ -17,13 +17,13 @@ class Context {
static auto current() -> const Context& {
assert(g_context);
- // Context can only be accessed from the game thread.
+ // Context can only be accessed from the logic thread.
BA_PRECONDITION(InLogicThread());
return *g_context;
}
static void set_current(const Context& context) {
- // Context can only be accessed from the game thread.
+ // Context can only be accessed from the logic thread.
BA_PRECONDITION(InLogicThread());
*g_context = context;
diff --git a/src/ballistica/core/fatal_error.cc b/src/ballistica/core/fatal_error.cc
index 8459de92..7dc75d06 100644
--- a/src/ballistica/core/fatal_error.cc
+++ b/src/ballistica/core/fatal_error.cc
@@ -69,11 +69,15 @@ auto FatalError::ReportFatalError(const std::string& message,
}
}
- // Prevent the early-log insta-send mechanism from firing since we do
- // basically the same thing ourself here (avoid sending the same logs twice).
- g_early_log_writes = 0;
+ // Prevent the early-v1-cloud-log insta-send mechanism from firing since
+ // we do basically the same thing ourself here (avoid sending the same
+ // logs twice).
+ g_early_v1_cloud_log_writes = 0;
- Logging::Log(logmsg);
+ // Add this to our V1CloudLog which we'll be attempting to send momentarily,
+ // and also try to present it directly to the user.
+ Logging::V1CloudLog(logmsg);
+ Logging::DisplayLog("root", LogLevel::kCritical, logmsg);
std::string prefix = "FATAL-ERROR-LOG:";
std::string suffix;
@@ -83,7 +87,7 @@ auto FatalError::ReportFatalError(const std::string& message,
if (g_app == nullptr) {
suffix = logmsg;
}
- g_app_internal->DirectSendLogs(prefix, suffix, true, &result);
+ g_app_internal->DirectSendV1CloudLogs(prefix, suffix, true, &result);
// If we're able to show a fatal-error dialog synchronously, do so.
if (g_platform && g_platform->CanShowBlockingFatalErrorDialog()) {
@@ -148,10 +152,10 @@ auto FatalError::HandleFatalError(bool exit_cleanly,
// bring the app down ourself.
if (!in_top_level_exception_handler) {
if (exit_cleanly) {
- Log("Calling exit(1)...");
+ Logging::DisplayLog("root", LogLevel::kCritical, "Calling exit(1)...");
exit(1);
} else {
- Log("Calling abort()...");
+ Logging::DisplayLog("root", LogLevel::kCritical, "Calling abort()...");
abort();
}
}
diff --git a/src/ballistica/core/logging.cc b/src/ballistica/core/logging.cc
index 69a892b9..35805da9 100644
--- a/src/ballistica/core/logging.cc
+++ b/src/ballistica/core/logging.cc
@@ -13,98 +13,93 @@
namespace ballistica {
-static void PrintCommon(const std::string& s) {
+auto Logging::Log(LogLevel level, const std::string& msg) -> void {
+ // This is basically up to Python to handle, but its up to us
+ // if Python's not up yet.
+ if (!g_python) {
+ // Make an attempt to get it at least seen (and note the fact
+ // that its super-early).
+ const char* errmsg{"Logging::Log() called before g_python exists."};
+ if (g_platform) {
+ g_platform->DisplayLog("root", LogLevel::kError, errmsg);
+ g_platform->DisplayLog("root", level, msg);
+ }
+ fprintf(stderr, "%s\n%s\n", errmsg, msg.c_str());
+ return;
+ }
+
+ // All we want to do is call Python logging.XXX. That's on Python.
+ g_python->LoggingCall(level, msg);
+}
+
+auto Logging::DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) -> void {
// Print to in-game console.
{
if (g_logic != nullptr) {
- g_logic->PushConsolePrintCall(s);
+ g_logic->PushConsolePrintCall(msg);
} else {
if (g_platform != nullptr) {
- g_platform->HandleLog(
- "Warning: Log() called before game-thread setup; "
- "will not appear on in-game console.\n");
+ g_platform->DisplayLog("root", LogLevel::kWarning,
+ "DisplayLog() called before logic-thread setup; "
+ "will not appear on in-game console.");
}
}
}
+
// Print to any telnet clients.
if (g_app && g_app->telnet_server) {
- g_app->telnet_server->PushPrint(s);
+ g_app->telnet_server->PushPrint(msg);
}
+
+ // Ship to platform-specific display mechanisms (android log, etc).
+ g_platform->DisplayLog(name, level, msg);
}
-void Logging::PrintStdout(const std::string& s, bool flush) {
- fprintf(stdout, "%s", s.c_str());
- if (flush) {
- fflush(stdout);
- }
- PrintCommon(s);
-}
-
-void Logging::PrintStderr(const std::string& s, bool flush) {
- fprintf(stderr, "%s", s.c_str());
- if (flush) {
- fflush(stderr);
- }
- PrintCommon(s);
-}
-
-void Logging::Log(const std::string& msg, bool to_stdout, bool to_server) {
- if (to_stdout) {
- PrintStdout(msg + "\n", true);
+auto Logging::V1CloudLog(const std::string& msg) -> void {
+ // Route through platform-specific loggers if present.
+ // (things like Crashlytics crash-logging)
+ if (g_platform) {
+ Platform::DebugLog(msg);
}
- // Ship to the platform logging mechanism (android-log, stderr, etc.)
- // if that's available yet.
- if (g_platform != nullptr) {
- g_platform->HandleLog(msg);
- }
-
- // Ship to master-server/etc.
- if (to_server) {
- // Route through platform-specific loggers if present.
- // (things like Crashlytics crash-logging)
- if (g_platform) {
- Platform::DebugLog(msg);
- }
-
- // Add to our complete log.
- if (g_app != nullptr) {
- std::scoped_lock lock(g_app->log_mutex);
- if (!g_app->log_full) {
- (g_app->log) += (msg + "\n");
- if ((g_app->log).size() > 10000) {
- // Allow some reasonable overflow for last statement.
- if ((g_app->log).size() > 100000) {
- // FIXME: This could potentially chop up utf-8 chars.
- (g_app->log).resize(100000);
- }
- g_app->log += "\n\n";
- g_app->log_full = true;
+ // Add to our complete v1-cloud-log.
+ if (g_app != nullptr) {
+ std::scoped_lock lock(g_app->v1_cloud_log_mutex);
+ if (!g_app->v1_cloud_log_full) {
+ (g_app->v1_cloud_log) += (msg + "\n");
+ if ((g_app->v1_cloud_log).size() > 10000) {
+ // Allow some reasonable overflow for last statement.
+ if ((g_app->v1_cloud_log).size() > 100000) {
+ // FIXME: This could potentially chop up utf-8 chars.
+ (g_app->v1_cloud_log).resize(100000);
}
+ g_app->v1_cloud_log += "\n\n";
+ g_app->v1_cloud_log_full = true;
}
}
+ }
- // If the game is fully bootstrapped, let the Python layer handle logs.
- // It will group log messages intelligently and ship them to the
- // master server with various other context info included.
- if (g_app && g_app->is_bootstrapped) {
- assert(g_python != nullptr);
- g_python->PushObjCall(Python::ObjID::kHandleLogCall);
- } else {
- // For log messages during bootstrapping we ship them immediately since
- // we don't know if the Python layer is (or will be) able to.
- if (g_early_log_writes > 0) {
- g_early_log_writes -= 1;
- std::string logprefix = "EARLY-LOG:";
- std::string logsuffix;
+ // If the game is fully bootstrapped, let the Python layer handle logs.
+ // It will group log messages intelligently and ship them to the
+ // master server with various other context info included.
+ if (g_app && g_app->is_bootstrapped) {
+ assert(g_python != nullptr);
+ g_python->PushObjCall(Python::ObjID::kHandleV1CloudLogCall);
+ } else {
+ // For log messages during bootstrapping we ship them immediately since
+ // we don't know if the Python layer is (or will be) able to.
+ if (g_early_v1_cloud_log_writes > 0) {
+ g_early_v1_cloud_log_writes -= 1;
+ std::string logprefix = "EARLY-LOG:";
+ std::string logsuffix;
- // If we're an early enough error, our global log isn't even available,
- // so include this specific message as a suffix instead.
- if (g_app == nullptr) {
- logsuffix = msg;
- }
- g_app_internal->DirectSendLogs(logprefix, logsuffix, false);
+ // If we're an early enough error, our global log isn't even available,
+ // so include this specific message as a suffix instead.
+ if (g_app == nullptr) {
+ logsuffix = msg;
}
+ g_app_internal->DirectSendV1CloudLogs(logprefix, logsuffix, false);
}
}
}
diff --git a/src/ballistica/core/logging.h b/src/ballistica/core/logging.h
index 66d41ed9..6c7fb319 100644
--- a/src/ballistica/core/logging.h
+++ b/src/ballistica/core/logging.h
@@ -5,23 +5,33 @@
#include
+#include "ballistica/ballistica.h"
+
namespace ballistica {
class Logging {
public:
- /// Print a string directly to stdout as well as the in-game console
- /// and any connected telnet consoles.
- static auto PrintStdout(const std::string& s, bool flush = false) -> void;
+ /// Write a message to the log. Intended for logging use in C++ code.
+ /// This is safe to call by any thread at any time. In general it simply
+ /// passes through to the equivalent Python log calls: logging.info,
+ /// logging.warning, etc.
+ /// Note that log messages often must go through some background
+ /// processing before being seen by the user, meaning they may not
+ /// always work well for tight debugging purposes. In cases such as these,
+ /// printf() type calls or calling DisplayLog() directly may work better.
+ /// (though both of these should be avoided in permanent code).
+ static auto Log(LogLevel level, const std::string& msg) -> void;
- /// Print a string directly to stderr as well as the in-game console
- /// and any connected telnet consoles.
- static auto PrintStderr(const std::string& s, bool flush = false) -> void;
+ /// Immediately display a log message in the in-game console,
+ /// platform-specific logs, etc. This generally should not be called
+ /// directly but instead wired up to display messages coming from the
+ /// Python logging system.
+ static auto DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) -> void;
- /// Write a string to the debug log.
- /// This will go to stdout, windows debug log, android log, etc. depending
- /// on the platform.
- static auto Log(const std::string& msg, bool to_stdout = true,
- bool to_server = true) -> void;
+ /// Write a message to the v1 cloud log. This is considered legacy
+ /// and will be phased out eventually.
+ static auto V1CloudLog(const std::string& msg) -> void;
};
} // namespace ballistica
diff --git a/src/ballistica/core/macros.cc b/src/ballistica/core/macros.cc
index ed4a25bb..27f436bf 100644
--- a/src/ballistica/core/macros.cc
+++ b/src/ballistica/core/macros.cc
@@ -18,8 +18,8 @@ void MacroFunctionTimerEnd(millisecs_t starttime, millisecs_t time,
}
millisecs_t endtime = g_platform->GetTicks();
if (endtime - starttime > time) {
- Log("Warning: " + std::to_string(endtime - starttime)
- + " milliseconds spent in " + funcname);
+ Log(LogLevel::kWarning, std::to_string(endtime - starttime)
+ + " milliseconds spent in " + funcname);
}
}
@@ -32,9 +32,9 @@ void MacroFunctionTimerEndThread(millisecs_t starttime, millisecs_t time,
}
millisecs_t endtime = g_platform->GetTicks();
if (endtime - starttime > time) {
- Log("Warning: " + std::to_string(endtime - starttime)
- + " milliseconds spent by " + ballistica::GetCurrentThreadName()
- + " thread in " + funcname);
+ Log(LogLevel::kWarning,
+ std::to_string(endtime - starttime) + " milliseconds spent by "
+ + ballistica::GetCurrentThreadName() + " thread in " + funcname);
}
}
@@ -47,8 +47,9 @@ void MacroFunctionTimerEndEx(millisecs_t starttime, millisecs_t time,
}
millisecs_t endtime = g_platform->GetTicks();
if (endtime - starttime > time) {
- Log("Warning: " + std::to_string(endtime - starttime)
- + " milliseconds spent in " + funcname + " for " + what);
+ Log(LogLevel::kWarning, std::to_string(endtime - starttime)
+ + " milliseconds spent in " + funcname + " for "
+ + what);
}
}
@@ -62,9 +63,10 @@ void MacroFunctionTimerEndThreadEx(millisecs_t starttime, millisecs_t time,
}
millisecs_t endtime = g_platform->GetTicks();
if (endtime - starttime > time) {
- Log("Warning: " + std::to_string(endtime - starttime)
- + " milliseconds spent by " + ballistica::GetCurrentThreadName()
- + " thread in " + funcname + " for " + what);
+ Log(LogLevel::kWarning, std::to_string(endtime - starttime)
+ + " milliseconds spent by "
+ + ballistica::GetCurrentThreadName()
+ + " thread in " + funcname + " for " + what);
}
}
@@ -77,9 +79,9 @@ void MacroTimeCheckEnd(millisecs_t starttime, millisecs_t time,
}
millisecs_t e = g_platform->GetTicks();
if (e - starttime > time) {
- Log(std::string("Warning: ") + name + " took "
- + std::to_string(e - starttime) + " milliseconds; " + file + " line "
- + std::to_string(line));
+ Log(LogLevel::kWarning,
+ std::string(name) + " took " + std::to_string(e - starttime)
+ + " milliseconds; " + file + " line " + std::to_string(line));
}
}
@@ -88,19 +90,19 @@ void MacroLogErrorTrace(const std::string& msg, const char* fname, int line) {
snprintf(buffer, sizeof(buffer), "%s:%d:", fname, line);
buffer[sizeof(buffer) - 1] = 0;
Python::PrintStackTrace();
- Log(std::string(buffer) + " error: " + msg);
+ Log(LogLevel::kError, std::string(buffer) + " error: " + msg);
}
void MacroLogError(const std::string& msg, const char* fname, int line) {
char e_buffer[2048];
snprintf(e_buffer, sizeof(e_buffer), "%s:%d:", fname, line);
e_buffer[sizeof(e_buffer) - 1] = 0;
- ballistica::Log(std::string(e_buffer) + " error: " + msg);
+ Log(LogLevel::kError, std::string(e_buffer) + " error: " + msg);
}
void MacroLogPythonTrace(const std::string& msg) {
Python::PrintStackTrace();
- Log(msg);
+ Log(LogLevel::kError, msg);
}
} // namespace ballistica
diff --git a/src/ballistica/core/macros.h b/src/ballistica/core/macros.h
index 65b81043..85c70105 100644
--- a/src/ballistica/core/macros.h
+++ b/src/ballistica/core/macros.h
@@ -86,11 +86,11 @@
} \
((void)0) // (see 'Trailing-semicolon note' at top)
-#define BA_LOG_ONCE(msg) \
+#define BA_LOG_ONCE(lvl, msg) \
{ \
static bool did_log_here = false; \
if (!did_log_here) { \
- ballistica::Log(msg); \
+ ballistica::Log(lvl, msg); \
did_log_here = true; \
} \
} \
@@ -120,12 +120,12 @@
/// Test a condition and simply print a log message if it fails (on both debug
/// and release builds)
-#define BA_PRECONDITION_LOG(b) \
- { \
- if (!(b)) { \
- Log("Precondition failed: " #b); \
- } \
- } \
+#define BA_PRECONDITION_LOG(b) \
+ { \
+ if (!(b)) { \
+ Log(LogLevel::kError, "Precondition failed: " #b); \
+ } \
+ } \
((void)0) // (see 'Trailing-semicolon note' at top)
/// Test a condition and abort the program if it fails (on both debug
diff --git a/src/ballistica/core/object.cc b/src/ballistica/core/object.cc
index dc194534..ee3280ee 100644
--- a/src/ballistica/core/object.cc
+++ b/src/ballistica/core/object.cc
@@ -54,9 +54,9 @@ void Object::PrintObjects() {
assert(count == g_app->object_count);
}
}
- Log(s);
+ Log(LogLevel::kInfo, s);
#else
- Log("PrintObjects() only functions in debug builds.");
+ Log(LogLevel::kInfo, "PrintObjects() only functions in debug builds.");
#endif // BA_DEBUG_BUILD
}
diff --git a/src/ballistica/core/thread.cc b/src/ballistica/core/thread.cc
index 805faef9..de2b5985 100644
--- a/src/ballistica/core/thread.cc
+++ b/src/ballistica/core/thread.cc
@@ -43,26 +43,54 @@ auto Thread::RunLogicThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunLogicThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
+
auto Thread::RunAudioThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunAudioThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
auto Thread::RunBGDynamicThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunBGDynamicThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
+
auto Thread::RunNetworkWriteThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunNetworkWriteThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
+
auto Thread::RunStdInputThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunStdInputThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
auto Thread::RunAssetsThread(void* data) -> int {
return static_cast(data)->ThreadMain();
}
+auto Thread::RunAssetsThreadP(void* data) -> void* {
+ static_cast(data)->ThreadMain();
+ return nullptr;
+}
+
void Thread::SetPaused(bool paused) {
// Can be toggled from the main thread only.
assert(std::this_thread::get_id() == g_app->main_thread_id);
@@ -213,34 +241,58 @@ Thread::Thread(ThreadTag identifier_in, ThreadSource source)
switch (source_) {
case ThreadSource::kCreate: {
int (*func)(void*);
+ void* (*funcp)(void*);
switch (identifier_) {
case ThreadTag::kLogic:
func = RunLogicThread;
+ funcp = RunLogicThreadP;
break;
case ThreadTag::kAssets:
func = RunAssetsThread;
+ funcp = RunAssetsThreadP;
break;
case ThreadTag::kMain:
// Shouldn't happen; this thread gets wrapped; not launched.
throw Exception();
case ThreadTag::kAudio:
func = RunAudioThread;
+ funcp = RunAudioThreadP;
break;
case ThreadTag::kBGDynamics:
func = RunBGDynamicThread;
+ funcp = RunBGDynamicThreadP;
break;
case ThreadTag::kNetworkWrite:
func = RunNetworkWriteThread;
+ funcp = RunNetworkWriteThreadP;
break;
case ThreadTag::kStdin:
func = RunStdInputThread;
+ funcp = RunStdInputThreadP;
break;
default:
throw Exception();
}
- // Let 'er rip.
- thread_ = new std::thread(func, this);
+ // Let 'er rip.
+
+ // NOTE: Apple platforms have a default secondary thread stack size
+ // of 512k which I've found to be insufficient in cases of heavy
+ // Python recursion or large simulations. It sounds like Windows
+ // and Android might have 1mb as default; let's try to standardize
+ // on that across the board. Unfortunately we have to use pthreads
+ // to get custom stack sizes; std::thread stupidly doesn't support it.
+
+ // FIXME - move this to platform.
+#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX
+ pthread_attr_t attr;
+ BA_PRECONDITION(pthread_attr_init(&attr) == 0);
+ BA_PRECONDITION(pthread_attr_setstacksize(&attr, 1024 * 1024) == 0);
+ pthread_t thread;
+ pthread_create(&thread, &attr, funcp, this);
+#else
+ new std::thread(func, this);
+#endif
// Block until the thread is bootstrapped.
// (maybe not necessary, but let's be cautious in case we'd
@@ -376,8 +428,9 @@ void Thread::LogThreadMessageTally() {
writing_tally_ = true;
std::unordered_map tally;
- Log("Thread message tally (" + std::to_string(thread_messages_.size())
- + " in list):");
+ Log(LogLevel::kError, "Thread message tally ("
+ + std::to_string(thread_messages_.size())
+ + " in list):");
for (auto&& m : thread_messages_) {
std::string s;
switch (m.type) {
@@ -411,8 +464,8 @@ void Thread::LogThreadMessageTally() {
}
int entry = 1;
for (auto&& i : tally) {
- Log(" #" + std::to_string(entry++) + " (" + std::to_string(i.second)
- + "x): " + i.first);
+ Log(LogLevel::kError, " #" + std::to_string(entry++) + " ("
+ + std::to_string(i.second) + "x): " + i.first);
}
writing_tally_ = false;
}
@@ -445,7 +498,8 @@ void Thread::PushThreadMessage(const ThreadMessage& t) {
// Show count periodically.
if ((std::this_thread::get_id() == g_app->main_thread_id) && foo > 100) {
foo = 0;
- Log("MSG COUNT " + std::to_string(thread_messages_.size()));
+ Log(LogLevel::kInfo,
+ "MSG COUNT " + std::to_string(thread_messages_.size()));
}
}
@@ -453,8 +507,8 @@ void Thread::PushThreadMessage(const ThreadMessage& t) {
static bool sent_error = false;
if (!sent_error) {
sent_error = true;
- Log("Error: ThreadMessage list > 1000 in thread: "
- + GetCurrentThreadName());
+ Log(LogLevel::kError,
+ "ThreadMessage list > 1000 in thread: " + GetCurrentThreadName());
LogThreadMessageTally();
}
}
diff --git a/src/ballistica/core/thread.h b/src/ballistica/core/thread.h
index 1a007e1e..d5a63d01 100644
--- a/src/ballistica/core/thread.h
+++ b/src/ballistica/core/thread.h
@@ -141,11 +141,17 @@ class Thread {
// different thread groups makes its easy to see which thread is which
// in profilers, backtraces, etc.
static auto RunLogicThread(void* data) -> int;
+ static auto RunLogicThreadP(void* data) -> void*;
static auto RunAudioThread(void* data) -> int;
+ static auto RunAudioThreadP(void* data) -> void*;
static auto RunBGDynamicThread(void* data) -> int;
+ static auto RunBGDynamicThreadP(void* data) -> void*;
static auto RunNetworkWriteThread(void* data) -> int;
+ static auto RunNetworkWriteThreadP(void* data) -> void*;
static auto RunStdInputThread(void* data) -> int;
+ static auto RunStdInputThreadP(void* data) -> void*;
static auto RunAssetsThread(void* data) -> int;
+ static auto RunAssetsThreadP(void* data) -> void*;
auto ThreadMain() -> int;
auto GetThreadMessages(std::list* messages) -> void;
@@ -159,7 +165,7 @@ class Thread {
std::list> runnables_;
std::list pause_callbacks_;
std::list resume_callbacks_;
- std::thread* thread_{};
+ // std::thread* thread_{};
std::condition_variable thread_message_cv_;
std::mutex thread_message_mutex_;
std::list thread_messages_;
diff --git a/src/ballistica/core/types.h b/src/ballistica/core/types.h
index 8792b79f..663d0ad9 100644
--- a/src/ballistica/core/types.h
+++ b/src/ballistica/core/types.h
@@ -424,6 +424,8 @@ enum class PyExcType {
kWidgetNotFound
};
+enum class LogLevel { kDebug, kInfo, kWarning, kError, kCritical };
+
enum class SystemTextureID {
kUIAtlas,
kButtonSquare,
diff --git a/src/ballistica/dynamics/bg/bg_dynamics.h b/src/ballistica/dynamics/bg/bg_dynamics.h
index 934811ad..264b0b26 100644
--- a/src/ballistica/dynamics/bg/bg_dynamics.h
+++ b/src/ballistica/dynamics/bg/bg_dynamics.h
@@ -45,7 +45,7 @@ class BGDynamicsEmission {
BGDynamicsTendrilType tendril_type{BGDynamicsTendrilType::kSmoke};
};
-// client (game thread) functionality for bg dynamics
+// client (logic thread) functionality for bg dynamics
class BGDynamics {
public:
BGDynamics();
diff --git a/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h b/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h
index 625b8f76..777843ae 100644
--- a/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h
+++ b/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h
@@ -10,7 +10,7 @@
namespace ballistica {
// Big chunk of data sent back from the bg-dynamics server thread
-// to the game thread for drawing.
+// to the logic thread for drawing.
class BGDynamicsDrawSnapshot {
public:
struct TendrilShadow {
diff --git a/src/ballistica/dynamics/bg/bg_dynamics_server.cc b/src/ballistica/dynamics/bg/bg_dynamics_server.cc
index f43b4b8d..cbcd8821 100644
--- a/src/ballistica/dynamics/bg/bg_dynamics_server.cc
+++ b/src/ballistica/dynamics/bg/bg_dynamics_server.cc
@@ -539,12 +539,12 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot(
auto* ibuf = Object::NewDeferred(p_count * 6);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
*index_buffer = Object::MakeRefCounted(ibuf);
auto* vbuf = Object::NewDeferred(p_count * 4);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
*buffer = Object::MakeRefCounted(vbuf);
@@ -1359,7 +1359,8 @@ void BGDynamicsServer::Emit(const BGDynamicsEmission& def) {
}
default: {
int t = static_cast(def.emit_type);
- BA_LOG_ONCE("Invalid bg-dynamics emit type: " + std::to_string(t));
+ BA_LOG_ONCE(LogLevel::kError,
+ "Invalid bg-dynamics emit type: " + std::to_string(t));
break;
}
}
@@ -1596,14 +1597,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
if (shadow_max_count > 0) {
auto* ibuf = Object::NewDeferred(shadow_max_count * 6);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->shadow_indices = Object::MakeRefCounted(ibuf);
s_index = &ss->shadow_indices->elements[0];
auto* vbuf =
Object::NewDeferred(shadow_max_count * 4);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->shadow_vertices = Object::MakeRefCounted(vbuf);
s_vertex = &ss->shadow_vertices->elements[0];
@@ -1612,14 +1613,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
if (light_max_count > 0) {
auto* ibuf = Object::NewDeferred(light_max_count * 6);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->light_indices = Object::MakeRefCounted(ibuf);
l_index = &ss->light_indices->elements[0];
auto* vbuf =
Object::NewDeferred(light_max_count * 4);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->light_vertices = Object::MakeRefCounted(vbuf);
l_vertex = &ss->light_vertices->elements[0];
@@ -1968,14 +1969,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
if (smoke_slice_count > 0) {
auto* ibuf = Object::NewDeferred(smoke_index_count);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->tendril_indices = Object::MakeRefCounted(ibuf);
uint16_t* index = &ss->tendril_indices->elements[0];
auto* vbuf =
Object::NewDeferred(smoke_slice_count * 2);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->tendril_vertices = Object::MakeRefCounted(vbuf);
VertexSmokeFull* v = &ss->tendril_vertices->elements[0];
@@ -2133,13 +2134,13 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
auto* ibuf = Object::NewDeferred(index_count);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->fuse_indices = Object::MakeRefCounted(ibuf);
auto* vbuf =
Object::NewDeferred(vertex_count);
- // Game thread is default owner; needs to be us until we hand it over.
+ // Logic thread is default owner; needs to be us until we hand it over.
vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing);
ss->fuse_vertices = Object::MakeRefCounted(vbuf);
diff --git a/src/ballistica/dynamics/dynamics.cc b/src/ballistica/dynamics/dynamics.cc
index c5e69ed9..3b9aa0b1 100644
--- a/src/ballistica/dynamics/dynamics.cc
+++ b/src/ballistica/dynamics/dynamics.cc
@@ -141,7 +141,8 @@ Dynamics::Dynamics(Scene* scene_in)
Dynamics::~Dynamics() {
if (in_process_) {
- Log("Error: Dynamics going down within Process() call;"
+ Log(LogLevel::kError,
+ "Dynamics going down within Process() call;"
" should not happen.");
}
ShutdownODE();
diff --git a/src/ballistica/dynamics/material/material_component.cc b/src/ballistica/dynamics/material/material_component.cc
index e211ff15..76f4ace9 100644
--- a/src/ballistica/dynamics/material/material_component.cc
+++ b/src/ballistica/dynamics/material/material_component.cc
@@ -213,8 +213,9 @@ void MaterialComponent::Restore(const char** buffer, ClientSession* cs) {
action = Object::New();
break;
default:
- Log("Error: Invalid material action: '"
- + std::to_string(static_cast(type)) + "'.");
+ Log(LogLevel::kError, "Invalid material action: '"
+ + std::to_string(static_cast(type))
+ + "'.");
throw Exception();
}
action->Restore(buffer, cs);
diff --git a/src/ballistica/dynamics/part.cc b/src/ballistica/dynamics/part.cc
index 2b7436b3..17263e5e 100644
--- a/src/ballistica/dynamics/part.cc
+++ b/src/ballistica/dynamics/part.cc
@@ -97,7 +97,8 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding,
for (auto&& i : collisions_) {
if (i.node == node_id && i.part == part) {
BA_PRECONDITION(node());
- Log("Error: Got SetCollidingWith for part already colliding with.");
+ Log(LogLevel::kError,
+ "Got SetCollidingWith for part already colliding with.");
return;
}
}
@@ -117,7 +118,8 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding,
return;
}
}
- Log("Error: Got SetCollidingWith (separated) call for part we're "
+ Log(LogLevel::kError,
+ "Got SetCollidingWith (separated) call for part we're "
"not colliding with.");
}
}
diff --git a/src/ballistica/dynamics/rigid_body.cc b/src/ballistica/dynamics/rigid_body.cc
index 2bbf37f3..2e795adb 100644
--- a/src/ballistica/dynamics/rigid_body.cc
+++ b/src/ballistica/dynamics/rigid_body.cc
@@ -168,7 +168,7 @@ auto RigidBody::Check() -> void {
if (std::isnan(q[3])) err = true;
if (err) {
- Log("Error: Got error in rbd values!");
+ Log(LogLevel::kError, "Got error in rbd values!");
}
#if BA_DEBUG_BUILD
for (int i = 0; i < 3; i++) {
diff --git a/src/ballistica/generic/timer_list.cc b/src/ballistica/generic/timer_list.cc
index 533b6c0b..b92b8b5d 100644
--- a/src/ballistica/generic/timer_list.cc
+++ b/src/ballistica/generic/timer_list.cc
@@ -20,14 +20,14 @@ TimerList::~TimerList() {
if (g_buildconfig.debug_build()) {
if (timer_count_active_ != 0) {
- Log("Error: Invalid timerlist state on teardown.");
+ Log(LogLevel::kError, "Invalid timerlist state on teardown.");
}
if (timer_count_inactive_ != 0) {
- Log("Error: Invalid timerlist state on teardown.");
+ Log(LogLevel::kError, "Invalid timerlist state on teardown.");
}
if (!((timer_count_total_ == 0)
|| (client_timer_ != nullptr && timer_count_total_ == 1))) {
- Log("Error: Invalid timerlist state on teardown.");
+ Log(LogLevel::kError, "Invalid timerlist state on teardown.");
}
}
}
diff --git a/src/ballistica/generic/utils.cc b/src/ballistica/generic/utils.cc
index a7f341d1..2ac25a04 100644
--- a/src/ballistica/generic/utils.cc
+++ b/src/ballistica/generic/utils.cc
@@ -136,9 +136,10 @@ Utils::Utils() {
// it was parsed from. Use this to adjust the filtering as necessary so
// the resulting type name matches what is expected.
if (explicit_bool(false)) {
- Log("static_type_name check; name is '"
- + static_type_name() + "' debug_full is '"
- + static_type_name(true) + "'");
+ Log(LogLevel::kError,
+ "static_type_name check; name is '"
+ + static_type_name() + "' debug_full is '"
+ + static_type_name(true) + "'");
}
// We now bake these in so they match across platforms...
@@ -261,8 +262,9 @@ auto Utils::GetValidUTF8(const char* str, const char* loc) -> std::string {
}
}
logged_count++;
- Log("GOT INVALID UTF8 SEQUENCE: (" + log_str + "); RETURNING '" + to
- + "'; LOC '" + loc + "'");
+ Log(LogLevel::kError, "GOT INVALID UTF8 SEQUENCE: (" + log_str
+ + "); RETURNING '" + to + "'; LOC '" + loc
+ + "'");
}
} else {
diff --git a/src/ballistica/graphics/component/render_component.h b/src/ballistica/graphics/component/render_component.h
index 08ac4fea..a85f9bff 100644
--- a/src/ballistica/graphics/component/render_component.h
+++ b/src/ballistica/graphics/component/render_component.h
@@ -15,7 +15,8 @@ class RenderComponent {
: state_(State::kConfiguring), pass_(pass), cmd_buffer_(nullptr) {}
~RenderComponent() {
if (state_ != State::kSubmitted) {
- Log("Error: RenderComponent dying without submit() having been called.");
+ Log(LogLevel::kError,
+ "RenderComponent dying without submit() having been called.");
}
}
void DrawModel(ModelData* model, uint32_t flags = 0) {
diff --git a/src/ballistica/graphics/frame_def.h b/src/ballistica/graphics/frame_def.h
index 8e794ea0..90db34ae 100644
--- a/src/ballistica/graphics/frame_def.h
+++ b/src/ballistica/graphics/frame_def.h
@@ -12,8 +12,8 @@
namespace ballistica {
-/// A flattened representation of a frame; generated by the game thread and sent
-/// to the graphics thread to render.
+/// A flattened representation of a frame; generated by the logic thread and
+/// sent to the graphics thread to render.
class FrameDef {
public:
auto light_pass() -> RenderPass* { return light_pass_.get(); }
diff --git a/src/ballistica/graphics/gl/gl_sys.cc b/src/ballistica/graphics/gl/gl_sys.cc
index 7709f2af..221a5e35 100644
--- a/src/ballistica/graphics/gl/gl_sys.cc
+++ b/src/ballistica/graphics/gl/gl_sys.cc
@@ -315,7 +315,7 @@ void GLContext::SetVSync(bool enable) {
GLContext::~GLContext() {
if (!InMainThread()) {
- Log("Error: GLContext dying in non-graphics thread");
+ Log(LogLevel::kError, "GLContext dying in non-graphics thread");
}
#if BA_SDL2_BUILD
diff --git a/src/ballistica/graphics/gl/renderer_gl.cc b/src/ballistica/graphics/gl/renderer_gl.cc
index 7df0554e..5b0ccef3 100644
--- a/src/ballistica/graphics/gl/renderer_gl.cc
+++ b/src/ballistica/graphics/gl/renderer_gl.cc
@@ -144,10 +144,10 @@ static void _check_gl_error(int line) {
const char* version = (const char*)glGetString(GL_VERSION);
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
- Log("Error: OpenGL Error at line " + std::to_string(line) + ": "
- + GLErrorToString(err) + "\nrenderer: " + renderer
- + "\nvendor: " + vendor + "\nversion: " + version
- + "\ntime: " + std::to_string(GetRealTime()));
+ Log(LogLevel::kError, "OpenGL Error at line " + std::to_string(line) + ": "
+ + GLErrorToString(err) + "\nrenderer: " + renderer
+ + "\nvendor: " + vendor + "\nversion: " + version
+ + "\ntime: " + std::to_string(GetRealTime()));
}
}
@@ -256,16 +256,16 @@ void RendererGL::CheckGLExtensions() {
// if we require ES3
if (have_es3) {
g_running_es3 = true;
- Log(std::string("Using OpenGL ES 3 (vendor: ") + vendor
- + ", renderer: " + renderer + ", version: " + version_str + ")",
- false, false);
+ Log(LogLevel::kInfo, std::string("Using OpenGL ES 3 (vendor: ") + vendor
+ + ", renderer: " + renderer
+ + ", version: " + version_str + ")");
} else {
#if !BA_USE_ES3_INCLUDES
g_running_es3 = false;
- Log(std::string("USING OPENGL ES2 (vendor: ") + vendor
- + ", renderer: " + renderer + ", version: " + version_str + ")",
- false, false);
+ Log(LogLevel::kInfo, std::string("USING OPENGL ES2 (vendor: ") + vendor
+ + ", renderer: " + renderer
+ + ", version: " + version_str + ")");
// Can still support some stuff like framebuffer-blit with es2 extensions.
assert(glBlitFramebuffer == nullptr || !first_extension_check_);
@@ -381,7 +381,7 @@ void RendererGL::CheckGLExtensions() {
c_types.push_back(TextureCompressionType::kETC1);
} else {
#if BA_OSTYPE_ANDROID
- Log("Android device missing ETC1 support");
+ Log(LogLevel::kError, "Android device missing ETC1 support");
#endif
}
@@ -509,7 +509,7 @@ void RendererGL::CheckGLExtensions() {
&samples[0]);
g_msaa_max_samples_rgb565 = samples[0];
} else {
- BA_LOG_ONCE("Got 0 samplecounts for RGB565");
+ BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB565");
g_msaa_max_samples_rgb565 = 0;
}
}
@@ -525,7 +525,7 @@ void RendererGL::CheckGLExtensions() {
&samples[0]);
g_msaa_max_samples_rgb8 = samples[0];
} else {
- BA_LOG_ONCE("Got 0 samplecounts for RGB8");
+ BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB8");
g_msaa_max_samples_rgb8 = 0;
}
}
@@ -539,10 +539,10 @@ void RendererGL::CheckGLExtensions() {
#if MSAA_ERROR_TEST
if (enable_msaa_) {
ScreenMessage("MSAA ENABLED");
- Log("Ballistica MSAA Test: MSAA ENABLED", false, false);
+ Log(LogLevel::kInfo, "Ballistica MSAA Test: MSAA ENABLED");
} else {
ScreenMessage("MSAA DISABLED");
- Log("Ballistica MSAA Test: MSAA DISABLED", false, false);
+ Log(LogLevel::kInfo, "Ballistica MSAA Test: MSAA DISABLED");
}
#endif // MSAA_ERROR_TEST
@@ -1023,11 +1023,12 @@ class RendererGL::ShaderGL : public Object {
const char* renderer = (const char*)glGetString(GL_RENDERER);
// Let's not crash here. We have a better chance of calling home this way
// and theres a chance the game will still be playable.
- Log(std::string("Compile failed for ") + GetTypeName()
- + " shader:\n------------SOURCE BEGIN-------------\n" + src_in
- + "\n-----------SOURCE END-------------\n" + GetInfo()
- + "\nrenderer: " + renderer + "\nvendor: " + vendor
- + "\nversion:" + version);
+ Log(LogLevel::kError,
+ std::string("Compile failed for ") + GetTypeName()
+ + " shader:\n------------SOURCE BEGIN-------------\n" + src_in
+ + "\n-----------SOURCE END-------------\n" + GetInfo()
+ + "\nrenderer: " + renderer + "\nvendor: " + vendor
+ + "\nversion:" + version);
} else {
assert(compile_status == GL_TRUE);
std::string info = GetInfo();
@@ -1038,10 +1039,12 @@ class RendererGL::ShaderGL : public Object {
const char* version = (const char*)glGetString(GL_VERSION);
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
- Log(std::string("WARNING: info returned for ") + GetTypeName()
- + " shader:\n------------SOURCE BEGIN-------------\n" + src_in
- + "\n-----------SOURCE END-------------\n" + info + "\nrenderer: "
- + renderer + "\nvendor: " + vendor + "\nversion:" + version);
+ Log(LogLevel::kError,
+ std::string("WARNING: info returned for ") + GetTypeName()
+ + " shader:\n------------SOURCE BEGIN-------------\n" + src_in
+ + "\n-----------SOURCE END-------------\n" + info
+ + "\nrenderer: " + renderer + "\nvendor: " + vendor
+ + "\nversion:" + version);
}
}
DEBUG_CHECK_GL_ERROR;
@@ -1131,7 +1134,8 @@ class RendererGL::ProgramGL {
GLint linkStatus;
glGetProgramiv(program_, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
- Log("Link failed for program '" + name_ + "':\n" + GetInfo());
+ Log(LogLevel::kError,
+ "Link failed for program '" + name_ + "':\n" + GetInfo());
} else {
assert(linkStatus == GL_TRUE);
@@ -1140,8 +1144,8 @@ class RendererGL::ProgramGL {
&& (strstr(info.c_str(), "error:") || strstr(info.c_str(), "warning:")
|| strstr(info.c_str(), "Error:")
|| strstr(info.c_str(), "Warning:"))) {
- Log("WARNING: program using frag shader '" + name_
- + "' returned info:\n" + info);
+ Log(LogLevel::kError, "WARNING: program using frag shader '" + name_
+ + "' returned info:\n" + info);
}
}
@@ -1278,8 +1282,9 @@ class RendererGL::ProgramGL {
int c = glGetUniformLocation(program_, tex_name);
if (c == -1) {
#if !MSAA_ERROR_TEST
- Log("Error: ShaderGL: " + name_ + ": Can't set texture unit for texture '"
- + tex_name + "'");
+ Log(LogLevel::kError, "ShaderGL: " + name_
+ + ": Can't set texture unit for texture '"
+ + tex_name + "'");
DEBUG_CHECK_GL_ERROR;
#endif
} else {
@@ -1511,7 +1516,8 @@ class RendererGL::SimpleProgramGL : public RendererGL::ProgramGL {
"}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -1613,7 +1619,8 @@ class RendererGL::SimpleProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_{}, g_{}, b_{}, a_{};
@@ -1846,7 +1853,8 @@ class RendererGL::ObjectProgramGL : public RendererGL::ProgramGL {
}
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -1918,7 +1926,8 @@ class RendererGL::ObjectProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_, g_, b_, a_;
@@ -2035,7 +2044,8 @@ class RendererGL::SmokeProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -2077,7 +2087,8 @@ class RendererGL::SmokeProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_, g_, b_, a_;
@@ -2164,7 +2175,8 @@ class RendererGL::BlurProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -2198,7 +2210,8 @@ class RendererGL::BlurProgramGL : public RendererGL::ProgramGL {
" + texture2D(colorTex,vUV8));\n"
"}";
if (flags & SHD_DEBUG_PRINT) {
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
}
return s;
}
@@ -2248,7 +2261,8 @@ class RendererGL::ShieldProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -2301,7 +2315,8 @@ class RendererGL::ShieldProgramGL : public RendererGL::ProgramGL {
//" gl_FragColor = vec4(vec3(depth),1);\n"
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
@@ -2510,7 +2525,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -2636,7 +2652,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
#endif // msaa bug test
@@ -2751,7 +2768,8 @@ class RendererGL::SpriteProgramGL : public RendererGL::ProgramGL {
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
@@ -2787,7 +2805,8 @@ class RendererGL::SpriteProgramGL : public RendererGL::ProgramGL {
}
s += "}";
if (flags & SHD_DEBUG_PRINT)
- Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
+ Log(LogLevel::kInfo,
+ "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_, g_, b_, a_;
@@ -2808,7 +2827,7 @@ class RendererGL::TextureDataGL : public TextureRendererData {
~TextureDataGL() override {
if (!InGraphicsThread()) {
- Log("Error: TextureDataGL dying outside of graphics thread.");
+ Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread.");
} else {
// if we're currently bound as anything, clear that out
// (otherwise a new texture with that same ID won't be bindable)
@@ -3272,6 +3291,7 @@ class RendererGL::ModelDataGL : public ModelRendererData {
}
case 4: {
BA_LOG_ONCE(
+ LogLevel::kWarning,
"GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!");
elem_count_ = static_cast(model.indices32().size());
index_type_ = GL_UNSIGNED_INT;
@@ -3473,7 +3493,8 @@ class RendererGL::MeshDataGL : public MeshRendererData {
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
index_state_ = data->state;
have_index_data_ = true;
- BA_LOG_ONCE("GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!");
+ BA_LOG_ONCE(LogLevel::kWarning,
+ "GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!");
index_type_ = GL_UNSIGNED_INT;
}
}
@@ -3889,6 +3910,7 @@ class RendererGL::RenderTargetGL : public RenderTarget {
// this needs to be on for glClear to work on depth.
if (!renderer_->depth_writing_enabled_) {
BA_LOG_ONCE(
+ LogLevel::kWarning,
"RendererGL: depth-writing not enabled when clearing depth");
}
clear_mask |= GL_DEPTH_BUFFER_BIT;
@@ -5743,7 +5765,8 @@ void RendererGL::UpdateVignetteTex(bool force) {
if (err != GL_NO_ERROR) {
static bool reported = false;
if (!reported) {
- Log("Error: 32-bit vignette creation failed; falling back to 16.");
+ Log(LogLevel::kError,
+ "32-bit vignette creation failed; falling back to 16.");
reported = true;
}
const int kVignetteTexWidth = 64;
@@ -5800,14 +5823,15 @@ void RendererGL::UpdateVignetteTex(bool force) {
auto RendererGL::GetFunkyDepthIssue() -> bool {
if (!funky_depth_issue_set_) {
- BA_LOG_ONCE("fetching funky depth issue but not set");
+ BA_LOG_ONCE(LogLevel::kError, "fetching funky depth issue but not set");
}
return funky_depth_issue_;
}
auto RendererGL::GetDrawsShieldsFunny() -> bool {
if (!draws_shields_funny_set_) {
- BA_LOG_ONCE("fetching draws-shields-funny value but not set");
+ BA_LOG_ONCE(LogLevel::kError,
+ "fetching draws-shields-funny value but not set");
}
return draws_shields_funny_;
}
diff --git a/src/ballistica/graphics/graphics.cc b/src/ballistica/graphics/graphics.cc
index ac573e62..d2fd216d 100644
--- a/src/ballistica/graphics/graphics.cc
+++ b/src/ballistica/graphics/graphics.cc
@@ -877,7 +877,8 @@ void Graphics::FadeScreen(bool to, millisecs_t time, PyObject* endcall) {
// (otherwise, overlapping fades can cause things to get lost)
if (fade_end_call_.exists()) {
if (g_buildconfig.debug_build()) {
- Log("WARNING: 2 fades overlapping; running first fade-end-call early");
+ Log(LogLevel::kWarning,
+ "2 fades overlapping; running first fade-end-call early");
}
g_logic->PushPythonCall(fade_end_call_);
fade_end_call_.Clear();
@@ -1070,6 +1071,7 @@ void Graphics::BuildAndPushFrameDef() {
if (frame_def->GetOverlayFlatPass()->HasDrawCommands()) {
if (!g_ui->IsWindowPresent()) {
BA_LOG_ONCE(
+ LogLevel::kError,
"Drawing in overlay pass in VR mode without UI; shouldn't "
"happen!");
}
@@ -1213,7 +1215,7 @@ void Graphics::DrawFades(FrameDef* frame_def, millisecs_t real_time) {
if (fade_ <= 0.0f && fade_out_) {
millisecs_t faded_time = real_time - (fade_start_ + fade_time_);
if (faded_time > 15000) {
- Log("FORCE-ENDING STUCK FADE");
+ Log(LogLevel::kError, "FORCE-ENDING STUCK FADE");
fade_out_ = false;
fade_ = 1.0f;
fade_time_ = 1000;
@@ -1503,6 +1505,7 @@ void Graphics::WaitForRendererToExist() {
while (g_graphics_server == nullptr
|| g_graphics_server->renderer() == nullptr) {
BA_LOG_ONCE(
+ LogLevel::kWarning,
"BuildAndPushFrameDef() called before renderer is up; spinning...");
Platform::SleepMS(100);
sleep_count++;
diff --git a/src/ballistica/graphics/graphics.h b/src/ballistica/graphics/graphics.h
index 4c6b02cb..8d8c309f 100644
--- a/src/ballistica/graphics/graphics.h
+++ b/src/ballistica/graphics/graphics.h
@@ -43,7 +43,7 @@ const float kBackingDepth1 = 0.0f;
const float kShadowNeutral = 0.5f;
-// Client class for graphics operations (used from the game thread).
+// Client class for graphics operations (used from the logic thread).
class Graphics {
public:
Graphics();
diff --git a/src/ballistica/graphics/graphics_server.cc b/src/ballistica/graphics/graphics_server.cc
index 9cabd091..f0abe83f 100644
--- a/src/ballistica/graphics/graphics_server.cc
+++ b/src/ballistica/graphics/graphics_server.cc
@@ -50,7 +50,7 @@ void GraphicsServer::SetRenderHold() {
void GraphicsServer::SetFrameDef(FrameDef* framedef) {
// Note: we're just setting the framedef directly here
- // even though this gets called from the game thread.
+ // even though this gets called from the logic thread.
// Ideally it would seem we should push these to our thread
// event list, but currently we spin-lock waiting for new
// frames to appear which would prevent that from working;
@@ -77,15 +77,15 @@ auto GraphicsServer::GetRenderFrameDef() -> FrameDef* {
g_assets->RunPendingGraphicsLoads();
// Spin and wait for a short bit for a frame_def to appear. If it does, we
- // grab it, render it, and also message the game thread to start generating
+ // grab it, render it, and also message the logic thread to start generating
// another one.
while (true) {
if (frame_def_) {
FrameDef* frame_def = frame_def_;
frame_def_ = nullptr;
- // Tell the game thread we're ready for the next frame_def so it can start
- // building it while we render this one.
+ // Tell the logic thread we're ready for the next frame_def so it can
+ // start building it while we render this one.
g_logic->PushFrameDefRequest();
return frame_def;
}
@@ -173,7 +173,7 @@ void GraphicsServer::TryRender() {
FinishRenderFrameDef(frame_def);
}
- // Send this frame_def back to the game thread for deletion.
+ // Send this frame_def back to the logic thread for deletion.
g_graphics->ReturnCompletedFrameDef(frame_def);
}
}
@@ -194,7 +194,7 @@ void GraphicsServer::ReloadMedia() {
assert(g_graphics_server);
SetRenderHold();
- // Now tell the game thread to kick off loads for everything, flip on
+ // Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs.
g_logic->thread()->PushCall([this] {
@@ -209,7 +209,7 @@ void GraphicsServer::RebuildLostContext() {
assert(InGraphicsThread());
if (!renderer_) {
- Log("Error: No renderer on GraphicsServer::_rebuildContext.");
+ Log(LogLevel::kError, "No renderer on GraphicsServer::_rebuildContext.");
return;
}
@@ -246,8 +246,9 @@ void GraphicsServer::RebuildLostContext() {
// we won't hitch if we actually render them.)
SetRenderHold();
- // Now tell the game thread to kick off loads for everything, flip on progress
- // bar drawing, and then tell the graphics thread to stop ignoring frame-defs.
+ // Now tell the logic thread to kick off loads for everything, flip on
+ // progress bar drawing, and then tell the graphics thread to stop ignoring
+ // frame-defs.
g_logic->thread()->PushCall([this] {
g_assets->MarkAllAssetsForLoad();
g_graphics->EnableProgressBar(false);
@@ -351,7 +352,7 @@ void GraphicsServer::SetScreen(bool fullscreen, int width, int height,
}
// The first time we complete setting up our screen, we send a message
- // back to the game thread to complete the init process.. (they can't start
+ // back to the logic thread to complete the init process.. (they can't start
// loading graphics and things until we have our context set up so we know
// what types of textures to load, etc)
if (!initial_screen_created_) {
@@ -402,7 +403,7 @@ void GraphicsServer::HandleFullContextScreenRebuild(
UpdateVirtualScreenRes();
- // Inform the game thread of the latest values.
+ // Inform the logic thread of the latest values.
g_logic->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_,
res_y_);
}
@@ -459,7 +460,7 @@ void GraphicsServer::HandleFullContextScreenRebuild(
// so we won't hitch if we actually render them.)
SetRenderHold();
- // Now tell the game thread to kick off loads for everything, flip on
+ // Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs.
g_logic->thread()->PushCall([this] {
@@ -513,7 +514,7 @@ void GraphicsServer::VideoResize(float h, float v) {
res_y_ = v;
UpdateVirtualScreenRes();
- // Inform the game thread of the latest values.
+ // Inform the logic thread of the latest values.
g_logic->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_, res_y_);
if (renderer_) {
renderer_->ScreenSizeChanged();
@@ -758,7 +759,7 @@ void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) {
gl_context_->SetVSync(v_sync_);
}
} else {
- Log("Error: Got SetVSyncCall with no gl context.");
+ Log(LogLevel::kError, "Got SetVSyncCall with no gl context.");
}
}
}
@@ -773,8 +774,8 @@ void GraphicsServer::PushComponentUnloadCall(
for (auto&& i : components) {
(**i).Unload();
}
- // ..and then ship these pointers back to the game thread so it can free the
- // references.
+ // ..and then ship these pointers back to the logic thread so it can free
+ // the references.
g_logic->PushFreeAssetComponentRefsCall(components);
});
}
@@ -784,7 +785,7 @@ void GraphicsServer::PushRemoveRenderHoldCall() {
assert(render_hold_);
render_hold_--;
if (render_hold_ < 0) {
- Log("Error: RenderHold < 0");
+ Log(LogLevel::kError, "RenderHold < 0");
render_hold_ = 0;
}
});
diff --git a/src/ballistica/graphics/graphics_server.h b/src/ballistica/graphics/graphics_server.h
index aaa590fa..061da29c 100644
--- a/src/ballistica/graphics/graphics_server.h
+++ b/src/ballistica/graphics/graphics_server.h
@@ -32,7 +32,7 @@ class GraphicsServer {
const std::vector*>& components) -> void;
auto SetRenderHold() -> void;
- // Used by the game thread to pass frame-defs to the graphics server
+ // Used by the logic thread to pass frame-defs to the graphics server
// for rendering.
auto SetFrameDef(FrameDef* framedef) -> void;
diff --git a/src/ballistica/graphics/mesh/mesh_buffer_base.h b/src/ballistica/graphics/mesh/mesh_buffer_base.h
index 0a498f49..04f264a6 100644
--- a/src/ballistica/graphics/mesh/mesh_buffer_base.h
+++ b/src/ballistica/graphics/mesh/mesh_buffer_base.h
@@ -7,10 +7,10 @@
namespace ballistica {
-// Buffers used by the game thread to pass indices/vertices/etc. to meshes in
+// Buffers used by the logic thread to pass indices/vertices/etc. to meshes in
// the graphics thread. Note that it is safe to create these in other threads;
// you just need to turn off thread-checks until you pass ownership to the game
-// thread. (or just avoid creating references outside of the game thread)
+// thread. (or just avoid creating references outside of the logic thread)
class MeshBufferBase : public Object {
public:
uint32_t state; // which dynamicState value on the mesh this corresponds to
diff --git a/src/ballistica/graphics/mesh/mesh_data.h b/src/ballistica/graphics/mesh/mesh_data.h
index 1dd956c9..016b98c1 100644
--- a/src/ballistica/graphics/mesh/mesh_data.h
+++ b/src/ballistica/graphics/mesh/mesh_data.h
@@ -17,7 +17,7 @@ class MeshData {
: type_(type), draw_type_(draw_type) {}
virtual ~MeshData() {
if (renderer_data_) {
- Log("Error: MeshData going down with rendererData intact!");
+ Log(LogLevel::kError, "MeshData going down with rendererData intact!");
}
}
std::list::iterator iterator_;
diff --git a/src/ballistica/graphics/mesh/mesh_indexed_base.h b/src/ballistica/graphics/mesh/mesh_indexed_base.h
index e8daed0c..5b372252 100644
--- a/src/ballistica/graphics/mesh/mesh_indexed_base.h
+++ b/src/ballistica/graphics/mesh/mesh_indexed_base.h
@@ -76,9 +76,10 @@ class MeshIndexedBase : public Mesh {
// For use by subclasses in their IsValid() overrides
auto IndexSizeIsValid(size_t data_size) const -> bool {
if (index_data_size() == 2 && data_size > 65535) {
- BA_LOG_ONCE("ERROR: got mesh data with > 65535 elems and 16 bit indices: "
- + GetObjectDescription()
- + ". This case requires 32 bit indices.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Got mesh data with > 65535 elems and 16 bit indices: "
+ + GetObjectDescription()
+ + ". This case requires 32 bit indices.");
return false;
}
return true;
diff --git a/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h b/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h
index a1aeff0a..eb792a64 100644
--- a/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h
+++ b/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h
@@ -32,7 +32,8 @@ class MeshIndexedStaticDynamic : public MeshIndexedBase {
// Static and dynamic data sizes should always match, right?
if (static_data_->elements.size() != dynamic_data_->elements.size()) {
- BA_LOG_ONCE("ERROR: mesh static and dynamic data sizes do not match");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Mesh static and dynamic data sizes do not match");
return false;
}
diff --git a/src/ballistica/graphics/text/text_graphics.cc b/src/ballistica/graphics/text/text_graphics.cc
index c0153431..5e045591 100644
--- a/src/ballistica/graphics/text/text_graphics.cc
+++ b/src/ballistica/graphics/text/text_graphics.cc
@@ -331,7 +331,7 @@ TextGraphics::TextGraphics() {
if (g.tex_max_x > 1.0f || g.tex_max_x < 0.0f || g.tex_min_x > 1.0
|| g.tex_min_x < 0.0f || g.tex_max_y > 1.0f || g.tex_max_y < 0.0
|| g.tex_min_y > 1.0f || g.tex_min_y < 0.0f) {
- BA_LOG_ONCE("Warning: glyph bounds error");
+ BA_LOG_ONCE(LogLevel::kWarning, "glyph bounds error");
}
}
}
@@ -1021,6 +1021,7 @@ void TextGraphics::GetOSTextSpanBoundsAndWidth(const std::string& s, Rect* r,
g_platform->GetTextBoundsAndWidth(s, &entry->r, &entry->width);
} else {
BA_LOG_ONCE(
+ LogLevel::kError,
"FIXME: GetOSTextSpanBoundsAndWidth unimplemented on this platform");
r->l = 0.0f;
r->r = 1.0f;
diff --git a/src/ballistica/graphics/text/text_graphics.h b/src/ballistica/graphics/text/text_graphics.h
index e0ff8bda..c293da30 100644
--- a/src/ballistica/graphics/text/text_graphics.h
+++ b/src/ballistica/graphics/text/text_graphics.h
@@ -19,7 +19,7 @@ namespace ballistica {
const int kTextMaxUnicodeVal = 999999;
const float kTextRowHeight = 32.0f;
-// Encapsulates text-display functionality used by the game thread.
+// Encapsulates text-display functionality used by the logic thread.
class TextGraphics {
public:
TextGraphics();
diff --git a/src/ballistica/input/device/client_input_device.cc b/src/ballistica/input/device/client_input_device.cc
index 378292d0..d146c1a1 100644
--- a/src/ballistica/input/device/client_input_device.cc
+++ b/src/ballistica/input/device/client_input_device.cc
@@ -27,7 +27,8 @@ auto ClientInputDevice::GetClientID() const -> int {
if (ConnectionToClient* c = connection_to_client_.get()) {
return c->id();
} else {
- Log("ClientInputDevice::get_client_id(): connection_to_client no longer "
+ Log(LogLevel::kError,
+ "ClientInputDevice::get_client_id(): connection_to_client no longer "
"exists; returning -1..");
return -1;
}
diff --git a/src/ballistica/input/device/input_device.cc b/src/ballistica/input/device/input_device.cc
index b1ff66bd..9c7f4859 100644
--- a/src/ballistica/input/device/input_device.cc
+++ b/src/ballistica/input/device/input_device.cc
@@ -139,13 +139,15 @@ InputDevice::~InputDevice() {
// when the host-session tells us to attach to a player
void InputDevice::AttachToLocalPlayer(Player* player) {
if (player_.exists()) {
- Log("Error: InputDevice::AttachToLocalPlayer() called with already "
+ Log(LogLevel::kError,
+ "InputDevice::AttachToLocalPlayer() called with already "
"existing "
"player");
return;
}
if (remote_player_.exists()) {
- Log("Error: InputDevice::AttachToLocalPlayer() called with already "
+ Log(LogLevel::kError,
+ "InputDevice::AttachToLocalPlayer() called with already "
"existing "
"remote-player");
return;
@@ -158,13 +160,15 @@ void InputDevice::AttachToRemotePlayer(ConnectionToHost* connection_to_host,
int remote_player_id) {
assert(connection_to_host);
if (player_.exists()) {
- Log("Error: InputDevice::AttachToRemotePlayer()"
+ Log(LogLevel::kError,
+ "InputDevice::AttachToRemotePlayer()"
" called with already existing "
"player");
return;
}
if (remote_player_.exists()) {
- Log("Error: InputDevice::AttachToRemotePlayer()"
+ Log(LogLevel::kError,
+ "InputDevice::AttachToRemotePlayer()"
" called with already existing "
"remote-player");
return;
@@ -180,7 +184,8 @@ void InputDevice::RemoveRemotePlayerFromGame() {
data[1] = static_cast_check_fit(index());
connection_to_host->SendReliableMessage(data);
} else {
- Log("Error: RemoveRemotePlayerFromGame called without remote player");
+ Log(LogLevel::kError,
+ "RemoveRemotePlayerFromGame called without remote player");
}
}
@@ -210,12 +215,14 @@ void InputDevice::RequestPlayer() {
last_input_time_ = g_logic->master_time();
if (player_.exists()) {
- Log("Error: InputDevice::RequestPlayer()"
+ Log(LogLevel::kError,
+ "InputDevice::RequestPlayer()"
" called with already-existing player");
return;
}
if (remote_player_.exists()) {
- Log("Error: InputDevice::RequestPlayer() called with already-existing "
+ Log(LogLevel::kError,
+ "InputDevice::RequestPlayer() called with already-existing "
"remote-player");
return;
}
diff --git a/src/ballistica/input/device/input_device.h b/src/ballistica/input/device/input_device.h
index b9559fd2..012efa0c 100644
--- a/src/ballistica/input/device/input_device.h
+++ b/src/ballistica/input/device/input_device.h
@@ -13,8 +13,8 @@ namespace ballistica {
/// Base class for game input devices (keyboard, joystick, etc).
/// InputDevices can be allocated in any thread (generally on the main
/// thread in response to some system event). An AddInputDevice() call
-/// should then be pushed to the game thread to inform it of the new device.
-/// Deletion of the input-device is then handled by the game thread
+/// should then be pushed to the logic thread to inform it of the new device.
+/// Deletion of the input-device is then handled by the logic thread
/// and can be triggered by pushing a RemoveInputDevice() call to it.
class InputDevice : public Object {
public:
diff --git a/src/ballistica/input/device/joystick.cc b/src/ballistica/input/device/joystick.cc
index 5ee52393..2af36549 100644
--- a/src/ballistica/input/device/joystick.cc
+++ b/src/ballistica/input/device/joystick.cc
@@ -288,7 +288,7 @@ auto Joystick::GetButtonName(int index) -> std::string {
Joystick::~Joystick() {
if (!InLogicThread()) {
- Log("Error: Joystick dying in wrong thread.");
+ Log(LogLevel::kError, "Joystick dying in wrong thread.");
}
// Kill our child if need be.
@@ -301,7 +301,7 @@ Joystick::~Joystick() {
// Send a message back to the main thread to close this SDL Joystick.
// HMMM - can we just have the main thread close the joystick immediately
// before informing us its dead?.. i don't think we actually use it at all
- // here in the game thread..
+ // here in the logic thread..
if (sdl_joystick_) {
#if BA_ENABLE_SDL_JOYSTICKS
assert(g_app_flavor);
@@ -310,7 +310,8 @@ Joystick::~Joystick() {
[joystick] { SDL_JoystickClose(joystick); });
sdl_joystick_ = nullptr;
#else
- Log("sdl_joystick_ set in non-sdl-joystick build destructor.");
+ Log(LogLevel::kError,
+ "sdl_joystick_ set in non-sdl-joystick build destructor.");
#endif // BA_ENABLE_SDL_JOYSTICKS
}
}
@@ -692,8 +693,9 @@ void Joystick::HandleSDLEvent(const SDL_Event* e) {
hat_held_ = true;
break;
default:
- BA_LOG_ONCE("Error: Invalid hat value: "
- + std::to_string(static_cast(e->jhat.value)));
+ BA_LOG_ONCE(LogLevel::kError,
+ "Invalid hat value: "
+ + std::to_string(static_cast(e->jhat.value)));
break;
}
}
diff --git a/src/ballistica/input/device/touch_input.cc b/src/ballistica/input/device/touch_input.cc
index 6ecb4dc7..a76bfa0c 100644
--- a/src/ballistica/input/device/touch_input.cc
+++ b/src/ballistica/input/device/touch_input.cc
@@ -840,7 +840,8 @@ void TouchInput::UpdateMapping() {
} else if (touch_movement_type == "joystick") {
movement_control_type_ = TouchInput::MovementControlType::kJoystick;
} else {
- Log("Error: Invalid touch-movement-type: " + touch_movement_type);
+ Log(LogLevel::kError,
+ "Invalid touch-movement-type: " + touch_movement_type);
movement_control_type_ = TouchInput::MovementControlType::kSwipe;
}
std::string touch_action_type =
@@ -850,7 +851,7 @@ void TouchInput::UpdateMapping() {
} else if (touch_action_type == "buttons") {
action_control_type_ = TouchInput::ActionControlType::kButtons;
} else {
- Log("Error: Invalid touch-action-type: " + touch_action_type);
+ Log(LogLevel::kError, "Invalid touch-action-type: " + touch_action_type);
action_control_type_ = TouchInput::ActionControlType::kSwipe;
}
diff --git a/src/ballistica/input/input.cc b/src/ballistica/input/input.cc
index 4cbfd4ed..1137276b 100644
--- a/src/ballistica/input/input.cc
+++ b/src/ballistica/input/input.cc
@@ -20,7 +20,7 @@
namespace ballistica {
-// Though it seems strange, input is actually owned by the game thread, not the
+// Though it seems strange, input is actually owned by the logic thread, not the
// app thread. This keeps things simple for game logic interacting with input
// stuff (controller names, counts, etc) but means we need to be prudent about
// properly passing stuff between the game and app thread as needed.
@@ -327,7 +327,8 @@ void Input::PushCreateKeyboardInputDevices() {
void Input::CreateKeyboardInputDevices() {
assert(InLogicThread());
if (keyboard_input_ != nullptr || keyboard_input_2_ != nullptr) {
- Log("Error: CreateKeyboardInputDevices called with existing kbs.");
+ Log(LogLevel::kError,
+ "CreateKeyboardInputDevices called with existing kbs.");
return;
}
keyboard_input_ = Object::NewDeferred(nullptr);
@@ -343,7 +344,8 @@ void Input::PushDestroyKeyboardInputDevices() {
void Input::DestroyKeyboardInputDevices() {
assert(InLogicThread());
if (keyboard_input_ == nullptr || keyboard_input_2_ == nullptr) {
- Log("Error: DestroyKeyboardInputDevices called with null kb(s).");
+ Log(LogLevel::kError,
+ "DestroyKeyboardInputDevices called with null kb(s).");
return;
}
RemoveInputDevice(keyboard_input_, false);
@@ -812,7 +814,8 @@ void Input::UpdateEnabledControllerSubsystems() {
ignore_mfi_controllers_ = false;
ignore_sdl_controllers_ = false;
} else {
- BA_LOG_ONCE("Invalid mac-controller-subsystem value: '" + sys + "'");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Invalid mac-controller-subsystem value: '" + sys + "'");
}
}
}
@@ -842,7 +845,8 @@ void Input::Update() {
// If input has been locked an excessively long amount of time, unlock it.
if (input_lock_count_temp_) {
if (real_time - last_input_temp_lock_time_ > 10000) {
- Log("Error: Input has been temp-locked for 10 seconds; unlocking.");
+ Log(LogLevel::kError,
+ "Input has been temp-locked for 10 seconds; unlocking.");
input_lock_count_temp_ = 0;
PrintLockLabels();
input_lock_temp_labels_.clear();
@@ -938,8 +942,9 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) {
input_lock_count_temp_--;
input_unlock_temp_labels_.push_back(label);
if (input_lock_count_temp_ < 0) {
- Log("WARNING: temp input unlock at time " + std::to_string(GetRealTime())
- + " with no active lock: '" + label + "'");
+ Log(LogLevel::kWarning, "temp input unlock at time "
+ + std::to_string(GetRealTime())
+ + " with no active lock: '" + label + "'");
// This is to be expected since we can reset this to 0.
input_lock_count_temp_ = 0;
}
@@ -991,7 +996,7 @@ void Input::PrintLockLabels() {
s += "\n " + std::to_string(num++) + ": " + recent_input_locks_unlock;
}
- Log(s);
+ Log(LogLevel::kError, s);
}
void Input::ProcessStressTesting(int player_count) {
@@ -1653,7 +1658,8 @@ void Input::HandleTouchEvent(const TouchEvent& e) {
// overall multitouch gesture, it should always be winding up as our
// single_touch_.
if (e.type == TouchEvent::Type::kDown && single_touch_ != nullptr) {
- BA_LOG_ONCE("Got touch labeled first but will not be our single.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Got touch labeled first but will not be our single.");
}
// Also: if the OS tells us that this is the end of an overall multi-touch
@@ -1661,7 +1667,8 @@ void Input::HandleTouchEvent(const TouchEvent& e) {
if ((e.type == TouchEvent::Type::kUp
|| e.type == TouchEvent::Type::kCanceled)
&& single_touch_ != nullptr && single_touch_ != e.touch) {
- BA_LOG_ONCE("Last touch coming up is not single touch!");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Last touch coming up is not single touch!");
}
}
@@ -1792,8 +1799,9 @@ const char* GetScancodeName(SDL_Scancode scancode) {
const char* name;
if (static_cast(scancode) < SDL_SCANCODE_UNKNOWN
|| scancode >= SDL_NUM_SCANCODES) {
- BA_LOG_ONCE("GetScancodeName passed invalid scancode "
- + std::to_string(static_cast(scancode)));
+ BA_LOG_ONCE(LogLevel::kError,
+ "GetScancodeName passed invalid scancode "
+ + std::to_string(static_cast(scancode)));
return "";
}
diff --git a/src/ballistica/input/input.h b/src/ballistica/input/input.h
index 6c02956e..8f1d7838 100644
--- a/src/ballistica/input/input.h
+++ b/src/ballistica/input/input.h
@@ -14,17 +14,17 @@
namespace ballistica {
/// Class for managing input.
-/// Should only be used in the game thread unless otherwise specified.
+/// Should only be used in the logic thread unless otherwise specified.
class Input {
public:
Input();
- // Add an input device. Must be called from the game thread; otherwise use
+ // Add an input device. Must be called from the logic thread; otherwise use
// PushAddInputDeviceCall.
auto AddInputDevice(InputDevice* input, bool standard_message) -> void;
// Removes a previously-added input-device. Must be called from the
- // game thread; otherwise use PushRemoveInputDeviceCall.
+ // logic thread; otherwise use PushRemoveInputDeviceCall.
auto RemoveInputDevice(InputDevice* input, bool standard_message) -> void;
// Given a device name and persistent identifier for it, returns a device or
diff --git a/src/ballistica/input/remote_app.cc b/src/ballistica/input/remote_app.cc
index 2bac63ec..43c3549a 100644
--- a/src/ballistica/input/remote_app.cc
+++ b/src/ballistica/input/remote_app.cc
@@ -75,9 +75,9 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
}
case BA_PACKET_REMOTE_ID_REQUEST: {
if (amt < 5 || amt > 127) {
- BA_LOG_ONCE(
- "Error: received invalid BA_PACKET_REMOTE_ID_REQUEST of length "
- + std::to_string(amt));
+ BA_LOG_ONCE(LogLevel::kError,
+ "Received invalid BA_PACKET_REMOTE_ID_REQUEST of length "
+ + std::to_string(amt));
break;
}
@@ -212,7 +212,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
// Each state is 2 bytes. So make sure our length adds up.
if (amt != 4 + state_count * 3) {
- BA_LOG_ONCE("Error: Invalid state packet");
+ BA_LOG_ONCE(LogLevel::kError, "Invalid state packet");
return;
}
RemoteAppClient* client = clients_ + joystick_id;
diff --git a/src/ballistica/internal/app_internal.h b/src/ballistica/internal/app_internal.h
index 076c246f..6421c66d 100644
--- a/src/ballistica/internal/app_internal.h
+++ b/src/ballistica/internal/app_internal.h
@@ -41,9 +41,9 @@ class AppInternal {
bool user_initiated) -> void = 0;
virtual auto GetPublicV1AccountID() -> std::string = 0;
virtual auto OnLogicThreadPause() -> void = 0;
- virtual auto DirectSendLogs(const std::string& prefix,
- const std::string& suffix, bool instant,
- int* result = nullptr) -> void = 0;
+ virtual auto DirectSendV1CloudLogs(const std::string& prefix,
+ const std::string& suffix, bool instant,
+ int* result = nullptr) -> void = 0;
virtual auto ClientInfoQuery(const std::string& val1, const std::string& val2,
const std::string& val3, int build_number)
-> void = 0;
diff --git a/src/ballistica/logic/connection/connection.cc b/src/ballistica/logic/connection/connection.cc
index 654602ba..ec901c52 100644
--- a/src/ballistica/logic/connection/connection.cc
+++ b/src/ballistica/logic/connection/connection.cc
@@ -149,7 +149,8 @@ void Connection::HandleGamePacketCompressed(const std::vector& data) {
try {
data_decompressed = g_utils->huffman()->decompress(data);
} catch (const std::exception& e) {
- Log(std::string("EXC in huffman decompression for packet: ") + e.what());
+ Log(LogLevel::kError,
+ std::string("Error in huffman decompression for packet: ") + e.what());
// Hmmm i guess lets just ignore this packet and keep on trucking?.. or
// should we kill the connection?
@@ -168,7 +169,8 @@ void Connection::HandleGamePacket(const std::vector& data) {
switch (data[0]) {
case BA_GAMEPACKET_KEEPALIVE: {
if (data.size() != 4) {
- BA_LOG_ONCE("Error: got invalid BA_GAMEPACKET_KEEPALIVE packet.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Error: got invalid BA_GAMEPACKET_KEEPALIVE packet.");
return;
}
millisecs_t real_time = GetRealTime();
@@ -181,7 +183,7 @@ void Connection::HandleGamePacket(const std::vector& data) {
// Expect 1 byte type, 2 byte num, 3 byte acks, at least 1 byte payload.
if (data.size() < 7) {
- Log("Error: Got invalid BA_PACKET_STATE packet.");
+ Log(LogLevel::kError, "Got invalid BA_PACKET_STATE packet.");
return;
}
uint16_t num;
@@ -212,7 +214,7 @@ void Connection::HandleGamePacket(const std::vector& data) {
// Expect 1 byte type, 2 byte num, 2 byte unreliable-num, 3 byte acks,
// at least 1 byte payload.
if (data.size() < 9) {
- Log("Error: Got invalid BA_PACKET_STATE_UNRELIABLE packet.");
+ Log(LogLevel::kError, "Got invalid BA_PACKET_STATE_UNRELIABLE packet.");
return;
}
uint16_t num, num_unreliable;
@@ -233,8 +235,8 @@ void Connection::HandleGamePacket(const std::vector& data) {
}
default:
- Log("Connection got unknown packet type: "
- + std::to_string(static_cast(data[0])));
+ Log(LogLevel::kError, "Connection got unknown packet type: "
+ + std::to_string(static_cast(data[0])));
break;
}
}
@@ -316,8 +318,9 @@ void Connection::SendReliableMessage(const std::vector& data) {
void Connection::SendUnreliableMessage(const std::vector& data) {
// For now we just silently drop anything bigger than our max packet size.
if (data.size() + 8 > kMaxPacketSize) {
- BA_LOG_ONCE("Error: Dropping outgoing unreliable packet of size "
- + std::to_string(data.size()) + ".");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Error: Dropping outgoing unreliable packet of size "
+ + std::to_string(data.size()) + ".");
return;
}
@@ -428,7 +431,7 @@ void Connection::HandleMessagePacket(const std::vector& buffer) {
multipart_buffer_.resize(old_size + (buffer.size() - 1));
memcpy(&(multipart_buffer_[old_size]), &(buffer[1]), buffer.size() - 1);
} else {
- Log("got invalid BA_MESSAGE_MULTIPART");
+ Log(LogLevel::kError, "got invalid BA_MESSAGE_MULTIPART");
}
if (buffer[0] == BA_MESSAGE_MULTIPART_END) {
HandleMessagePacket(multipart_buffer_);
@@ -468,10 +471,11 @@ void Connection::SendGamePacket(const std::vector& data) {
if (!can_send && data[0] != BA_GAMEPACKET_HANDSHAKE
&& data[0] != BA_GAMEPACKET_HANDSHAKE_RESPONSE) {
if (explicit_bool(false)) {
- BA_LOG_ONCE("SendGamePacket() called before can_communicate set ("
- + g_platform->DemangleCXXSymbol(typeid(*this).name())
- + " ptype " + std::to_string(static_cast(data[0]))
- + ")");
+ BA_LOG_ONCE(LogLevel::kError,
+ "SendGamePacket() called before can_communicate set ("
+ + g_platform->DemangleCXXSymbol(typeid(*this).name())
+ + " ptype " + std::to_string(static_cast(data[0]))
+ + ")");
}
return;
}
diff --git a/src/ballistica/logic/connection/connection_set.cc b/src/ballistica/logic/connection/connection_set.cc
index 1748a3b9..23797bf6 100644
--- a/src/ballistica/logic/connection/connection_set.cc
+++ b/src/ballistica/logic/connection/connection_set.cc
@@ -26,7 +26,8 @@ void ConnectionSet::RegisterClientController(ClientControllerInterface* c) {
// This shouldn't happen, but if there's already a controller registered,
// detach all clients from it.
if (client_controller_) {
- Log("RegisterClientController() called "
+ Log(LogLevel::kError,
+ "RegisterClientController() called "
"but already have a controller; bad.");
for (auto&& i : connections_to_clients_) {
assert(i.second.exists());
@@ -206,7 +207,8 @@ auto ConnectionSet::GetConnectionsToClients()
if (connections_to_client.second.exists()) {
connections.push_back(connections_to_client.second.get());
} else {
- Log("HAVE NONEXISTENT CONNECTION_TO_CLIENT IN LIST; UNEXPECTED");
+ Log(LogLevel::kError,
+ "HAVE NONEXISTENT CONNECTION_TO_CLIENT IN LIST; UNEXPECTED");
}
}
return connections;
@@ -218,6 +220,7 @@ void ConnectionSet::PushUDPConnectionPacketCall(
// these are unreliable messages so its ok to just drop them.
if (!g_logic->thread()->CheckPushSafety()) {
BA_LOG_ONCE(
+ LogLevel::kError,
"Ignoring excessive udp-connection input packets; (could this be a "
"flood attack?).");
return;
@@ -280,7 +283,8 @@ void ConnectionSet::SendScreenMessageToAll(const std::string& s, float r,
auto ConnectionSet::PrepareForLaunchHostSession() -> void {
// If for some reason we're still attached to a host, kill the connection.
if (connection_to_host_.exists()) {
- Log("Had host-connection during LaunchHostSession(); shouldn't happen.");
+ Log(LogLevel::kError,
+ "Had host-connection during LaunchHostSession(); shouldn't happen.");
connection_to_host_->RequestDisconnect();
connection_to_host_.Clear();
has_connection_to_host_ = false;
@@ -322,8 +326,8 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool {
return false;
}
if (client_id > 255) {
- Log("DisconnectClient got client_id > 255 (" + std::to_string(client_id)
- + ")");
+ Log(LogLevel::kError, "DisconnectClient got client_id > 255 ("
+ + std::to_string(client_id) + ")");
} else {
std::vector msg_out(2);
msg_out[0] = BA_MESSAGE_KICK_VOTE;
@@ -404,7 +408,8 @@ auto ConnectionSet::UnregisterClientController(ClientControllerInterface* c)
// This shouldn't happen.
if (client_controller_ != c) {
- Log("UnregisterClientController() called with a non-registered "
+ Log(LogLevel::kError,
+ "UnregisterClientController() called with a non-registered "
"controller");
return;
}
@@ -668,7 +673,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in,
msg_out[0] = BA_PACKET_CLIENT_DENY;
msg_out[1] = request_id;
g_network_writer->PushSendToCall(msg_out, addr);
- Log("All client slots full; really?..");
+ Log(LogLevel::kError, "All client slots full; really?..");
break;
}
connection_to_client = Object::New(
@@ -710,8 +715,9 @@ auto ConnectionSet::VerifyClientAddr(uint8_t client_id, const SockAddr& addr)
if (addr == connection_to_client_udp->addr()) {
return true;
}
- BA_LOG_ONCE("VerifyClientAddr() found mismatch for client "
- + std::to_string(client_id) + ".");
+ BA_LOG_ONCE(LogLevel::kError,
+ "VerifyClientAddr() found mismatch for client "
+ + std::to_string(client_id) + ".");
return false;
}
@@ -722,8 +728,9 @@ void ConnectionSet::SetClientInfoFromMasterServer(
const std::string& client_token, PyObject* info_obj) {
// NOLINTNEXTLINE (python doing bitwise math on signed int)
if (!PyDict_Check(info_obj)) {
- Log("got non-dict for master-server client info for token " + client_token
- + ": " + Python::ObjToString(info_obj));
+ Log(LogLevel::kError,
+ "got non-dict for master-server client info for token " + client_token
+ + ": " + Python::ObjToString(info_obj));
return;
}
for (ConnectionToClient* client : GetConnectionsToClients()) {
diff --git a/src/ballistica/logic/connection/connection_to_client.cc b/src/ballistica/logic/connection/connection_to_client.cc
index 402a7593..10d52a4c 100644
--- a/src/ballistica/logic/connection/connection_to_client.cc
+++ b/src/ballistica/logic/connection/connection_to_client.cc
@@ -129,14 +129,14 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) {
}
if (data.empty()) {
- Log("Error: ConnectionToClient got data size 0.");
+ Log(LogLevel::kError, "ConnectionToClient got data size 0.");
return;
}
switch (data[0]) {
case BA_GAMEPACKET_HANDSHAKE_RESPONSE: {
// We sent the client a handshake and they're responding.
if (data.size() < 3) {
- Log("got invalid BA_GAMEPACKET_HANDSHAKE_RESPONSE");
+ Log(LogLevel::kError, "got invalid BA_GAMEPACKET_HANDSHAKE_RESPONSE");
return;
}
@@ -322,7 +322,7 @@ void ConnectionToClient::SendScreenMessage(const std::string& s, float r,
void ConnectionToClient::HandleMessagePacket(
const std::vector& buffer) {
if (buffer.empty()) {
- Log("Error: Got invalid HandleMessagePacket.");
+ Log(LogLevel::kError, "Got invalid HandleMessagePacket.");
return;
}
@@ -369,7 +369,7 @@ void ConnectionToClient::HandleMessagePacket(
if (b) {
build_number_ = b->valueint;
} else {
- Log("no buildnumber in clientinfo msg");
+ Log(LogLevel::kError, "no buildnumber in clientinfo msg");
}
// Grab their token (we use this to ask the
@@ -378,7 +378,7 @@ void ConnectionToClient::HandleMessagePacket(
if (t) {
token_ = t->valuestring;
} else {
- Log("no token in clientinfo msg");
+ Log(LogLevel::kError, "no token in clientinfo msg");
}
// Newer clients also pass a peer-hash, which
@@ -397,8 +397,10 @@ void ConnectionToClient::HandleMessagePacket(
}
cJSON_Delete(info);
} else {
- Log("got invalid json in clientinfo message: '"
- + std::string(reinterpret_cast(&(buffer[1]))) + "'");
+ Log(LogLevel::kError,
+ "got invalid json in clientinfo message: '"
+ + std::string(reinterpret_cast(&(buffer[1])))
+ + "'");
}
}
got_client_info_ = true;
@@ -435,7 +437,8 @@ void ConnectionToClient::HandleMessagePacket(
// we support for game streams vs client-connections. We could disallow
// connections to/from these older peers while still allowing old replays
// to play back.
- BA_LOG_ONCE("Received old pre-json player profiles msg; ignoring.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Received old pre-json player profiles msg; ignoring.");
break;
}
@@ -462,7 +465,8 @@ void ConnectionToClient::HandleMessagePacket(
// spamming before we can verify their identities)
if (g_logic->require_client_authentication()
&& !got_info_from_master_server_) {
- Log("Ignoring chat message from peer with no client info.");
+ Log(LogLevel::kError,
+ "Ignoring chat message from peer with no client info.");
SendScreenMessage(R"({"r":"loadingTryAgainText"})", 1, 0, 0);
} else if (last_chat_times_.size() >= 5) {
chat_block_time_ = now + next_chat_block_seconds_ * 1000;
@@ -556,7 +560,7 @@ void ConnectionToClient::HandleMessagePacket(
GetClientInputDevice(buffer[1])) {
int count = static_cast((buffer.size() - 2) / 5);
if ((buffer.size() - 2) % 5 != 0) {
- Log("Error: invalid player-input-commands packet");
+ Log(LogLevel::kError, "Error: invalid player-input-commands packet");
break;
}
int index = 2;
@@ -574,7 +578,7 @@ void ConnectionToClient::HandleMessagePacket(
case BA_MESSAGE_REMOVE_REMOTE_PLAYER: {
last_remove_player_time_ = GetRealTime();
if (buffer.size() != 2) {
- Log("Error: invalid remove-remote-player packet");
+ Log(LogLevel::kError, "Error: invalid remove-remote-player packet");
break;
}
if (ClientInputDevice* cid = GetClientInputDevice(buffer[1])) {
@@ -589,7 +593,7 @@ void ConnectionToClient::HandleMessagePacket(
case BA_MESSAGE_REQUEST_REMOTE_PLAYER: {
if (buffer.size() != 2) {
- Log("Error: invalid remote-player-request packet");
+ Log(LogLevel::kError, "Error: invalid remote-player-request packet");
break;
}
@@ -628,16 +632,18 @@ void ConnectionToClient::HandleMessagePacket(
} else {
// Either timed out or have info; let the request go through.
if (still_waiting) {
- Log("Allowing player-request without client\'s master-server "
+ Log(LogLevel::kError,
+ "Allowing player-request without client\'s master-server "
"info (build "
- + std::to_string(build_number_) + ")");
+ + std::to_string(build_number_) + ")");
}
hs->RequestPlayer(cid);
}
}
}
} else {
- Log("Error: ConnectionToClient got remote player"
+ Log(LogLevel::kError,
+ "ConnectionToClient got remote player"
" request but have no host session");
}
break;
@@ -650,8 +656,9 @@ void ConnectionToClient::HandleMessagePacket(
if (multipart_buffer_size() > 50000) {
// Its not actually unknown but shhh don't tell the hackers...
SendScreenMessage(R"({"r":"errorUnknownText"})", 1, 0, 0);
- Log("Client data limit exceeded by '" + peer_spec().GetShortName()
- + "'; kicking.");
+ Log(LogLevel::kError, "Client data limit exceeded by '"
+ + peer_spec().GetShortName()
+ + "'; kicking.");
g_logic->BanPlayer(peer_spec(), 1000 * 60);
Error("");
return;
@@ -738,8 +745,8 @@ void ConnectionToClient::HandleMasterServerClientInfo(PyObject* info_obj) {
"{\"t\":[\"serverResponses\","
"\"Your account was rejected. Are you signed in?\"]}",
1, 0, 0);
- Log("Master server found no valid account for '"
- + peer_spec().GetShortName() + "'; kicking.");
+ Log(LogLevel::kError, "Master server found no valid account for '"
+ + peer_spec().GetShortName() + "'; kicking.");
// Not benning anymore. People were exploiting this by impersonating
// other players using their public ids to get them banned from
diff --git a/src/ballistica/logic/connection/connection_to_client_udp.cc b/src/ballistica/logic/connection/connection_to_client_udp.cc
index 09a65a34..4dbbf839 100644
--- a/src/ballistica/logic/connection/connection_to_client_udp.cc
+++ b/src/ballistica/logic/connection/connection_to_client_udp.cc
@@ -66,7 +66,7 @@ void ConnectionToClientUDP::HandleGamePacket(
void ConnectionToClientUDP::Die() {
if (did_die_) {
- Log("Error: Posting multiple die messages; probably not good.");
+ Log(LogLevel::kError, "Posting multiple die messages; probably not good.");
return;
}
// this will actually clear the object..
diff --git a/src/ballistica/logic/connection/connection_to_host.cc b/src/ballistica/logic/connection/connection_to_host.cc
index baede471..e1500402 100644
--- a/src/ballistica/logic/connection/connection_to_host.cc
+++ b/src/ballistica/logic/connection/connection_to_host.cc
@@ -219,7 +219,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) {
PyObject* profiles = g_python->GetRawConfigValue("Player Profiles");
PythonRef empty_dict;
if (!profiles) {
- Log("No profiles found; sending empty list to host");
+ Log(LogLevel::kError,
+ "No profiles found; sending empty list to host");
empty_dict.Steal(PyDict_New());
profiles = empty_dict.get();
}
@@ -231,7 +232,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) {
PythonRef results =
g_python->obj(Python::ObjID::kJsonDumpsCall).Call(args, keywds);
if (!results.exists()) {
- Log("Error getting json dump of local profiles");
+ Log(LogLevel::kError,
+ "Error getting json dump of local profiles");
} else {
try {
// Pull the string as utf8 and send.
@@ -241,13 +243,15 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) {
memcpy(&(msg[1]), &s[0], s.size());
SendReliableMessage(msg);
} catch (const std::exception& e) {
- Log(std::string("exc sending player profiles to host: ")
- + e.what());
+ Log(LogLevel::kError,
+ std::string("exc sending player profiles to host: ")
+ + e.what());
}
}
}
} else {
- Log("Connected to old protocol; can't send player profiles");
+ Log(LogLevel::kError,
+ "Connected to old protocol; can't send player profiles");
}
}
break;
@@ -274,7 +278,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
assert(InLogicThread());
if (buffer.empty()) {
- Log("Error: got invalid HandleMessagePacket");
+ Log(LogLevel::kError, "Got invalid HandleMessagePacket");
return;
}
@@ -298,7 +302,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
if (b) {
build_number_ = b->valueint;
} else {
- Log("no buildnumber in hostinfo msg");
+ Log(LogLevel::kError, "no buildnumber in hostinfo msg");
}
// Party name.
cJSON* n = cJSON_GetObjectItem(info, "n");
@@ -307,7 +311,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
}
cJSON_Delete(info);
} else {
- Log("got invalid json in hostinfo message");
+ Log(LogLevel::kError, "got invalid json in hostinfo message");
}
}
got_host_info_ = true;
@@ -399,7 +403,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
case BA_MESSAGE_ATTACH_REMOTE_PLAYER_2: {
// New-style packet which includes a 32-bit player_id.
if (buffer.size() != 6) {
- Log("Error: invalid attach-remote-player-2 msg");
+ Log(LogLevel::kError, "Invalid attach-remote-player-2 msg");
break;
}
@@ -426,7 +430,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
// public servers.
// TODO(ericf): can remove this once back-compat-protocol > 29.
if (buffer.size() != 3) {
- Log("Error: Invalid attach-remote-player msg.");
+ Log(LogLevel::kError, "Invalid attach-remote-player msg.");
break;
}
@@ -447,7 +451,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) {
case BA_MESSAGE_DETACH_REMOTE_PLAYER: {
if (buffer.size() != 2) {
- Log("Error: Invalid detach-remote-player msg");
+ Log(LogLevel::kError, "Invalid detach-remote-player msg");
break;
}
InputDevice* input_device = g_input->GetInputDevice(buffer[1]);
diff --git a/src/ballistica/logic/connection/connection_to_host_udp.cc b/src/ballistica/logic/connection/connection_to_host_udp.cc
index c77a8a8d..8e332355 100644
--- a/src/ballistica/logic/connection/connection_to_host_udp.cc
+++ b/src/ballistica/logic/connection/connection_to_host_udp.cc
@@ -110,14 +110,15 @@ void ConnectionToHostUDP::Update() {
// departure before doing this when possible.
void ConnectionToHostUDP::Die() {
if (did_die_) {
- Log("Error: posting multiple die messages; probably not good.");
+ Log(LogLevel::kError, "Posting multiple die messages; probably not good.");
return;
}
if (g_logic->connections()->connection_to_host() == this) {
g_logic->connections()->PushDisconnectedFromHostCall();
did_die_ = true;
} else {
- Log("Error: Running update for non-current host-connection; shouldn't "
+ Log(LogLevel::kError,
+ "Running update for non-current host-connection; shouldn't "
"happen.");
}
}
diff --git a/src/ballistica/logic/host_activity.cc b/src/ballistica/logic/host_activity.cc
index 9685cadd..be77634f 100644
--- a/src/ballistica/logic/host_activity.cc
+++ b/src/ballistica/logic/host_activity.cc
@@ -98,14 +98,14 @@ HostActivity::~HostActivity() {
if (g_buildconfig.debug_build()) {
PruneDeadRefs(&python_calls_);
if (python_calls_.size() > 1) {
- std::string s = "WARNING: " + std::to_string(python_calls_.size())
+ std::string s = std::to_string(python_calls_.size())
+ " live PythonContextCalls at shutdown for "
+ "HostActivity" + " (1 call is expected):";
int count = 1;
for (auto& python_call : python_calls_)
s += "\n " + std::to_string(count++) + ": "
+ (*python_call).GetObjectDescription();
- Log(s);
+ Log(LogLevel::kWarning, s);
}
}
}
@@ -153,15 +153,16 @@ void HostActivity::RegisterCall(PythonContextCall* call) {
// If we're shutting down, just kill the call immediately.
// (we turn all of our calls to no-ops as we shut down)
if (shutting_down_) {
- Log("WARNING: adding call to expired activity; call will not function: "
- + call->GetObjectDescription());
+ Log(LogLevel::kWarning,
+ "Adding call to expired activity; call will not function: "
+ + call->GetObjectDescription());
call->MarkDead();
}
}
void HostActivity::start() {
if (_started) {
- Log("Error: Start called twice for activity.");
+ Log(LogLevel::kError, "Start called twice for activity.");
}
_started = true;
}
@@ -253,7 +254,8 @@ void HostActivity::HandleOutOfBoundsNodes() {
// Make sure someone's handling our out-of-bounds messages.
out_of_bounds_in_a_row_++;
if (out_of_bounds_in_a_row_ > 100) {
- Log("Warning: 100 consecutive out-of-bounds messages sent."
+ Log(LogLevel::kWarning,
+ "100 consecutive out-of-bounds messages sent."
" They are probably not being handled properly");
int j = 0;
for (auto&& i : scene()->out_of_bounds_nodes()) {
@@ -265,9 +267,10 @@ void HostActivity::HandleOutOfBoundsNodes() {
if (delegate) {
dstr = PythonRef(delegate, PythonRef::kAcquire).Str();
}
- Log(" node #" + std::to_string(j) + ": type='" + n->type()->name()
- + "' addr=" + Utils::PtrToString(i.get()) + " name='" + n->label()
- + "' delegate=" + dstr);
+ Log(LogLevel::kWarning,
+ " node #" + std::to_string(j) + ": type='" + n->type()->name()
+ + "' addr=" + Utils::PtrToString(i.get()) + " name='"
+ + n->label() + "' delegate=" + dstr);
}
}
out_of_bounds_in_a_row_ = 0;
diff --git a/src/ballistica/logic/logic.cc b/src/ballistica/logic/logic.cc
index 795951ac..a791d3f0 100644
--- a/src/ballistica/logic/logic.cc
+++ b/src/ballistica/logic/logic.cc
@@ -373,7 +373,8 @@ void Logic::PruneSessions() {
try {
i.Clear();
} catch (const std::exception& e) {
- Log("Exception killing Session: " + std::string(e.what()));
+ Log(LogLevel::kError,
+ "Exception killing Session: " + std::string(e.what()));
}
have_dead_session = true;
}
@@ -663,8 +664,8 @@ void Logic::Update() {
// Complain when our full update takes longer than 1/60th second.
if (duration > (1000 / 60)) {
- Log("Game update took too long (" + std::to_string(duration) + " ms).",
- true, false);
+ Log(LogLevel::kInfo,
+ "Logic update took too long (" + std::to_string(duration) + " ms).");
// Limit these if we want (not doing so for now).
next_long_update_report_time_ = real_time;
@@ -686,8 +687,9 @@ void Logic::Reset() {
// If all is well our sessions should all be dead.
if (g_app->session_count != 0) {
- Log("Error: session-count is non-zero ("
- + std::to_string(g_app->session_count) + ") on Logic::Reset.");
+ Log(LogLevel::kError, "Session-count is non-zero ("
+ + std::to_string(g_app->session_count)
+ + ") on Logic::Reset.");
}
// Note: we don't clear real-time timers anymore. Should we?..
@@ -754,7 +756,7 @@ void Logic::PushBackButtonCall(InputDevice* input_device) {
void Logic::PushStringEditSetCall(const std::string& value) {
thread()->PushCall([value] {
if (!g_ui) {
- Log("Error: No ui on StringEditSetEvent.");
+ Log(LogLevel::kError, "No ui on StringEditSetEvent.");
return;
}
#if BA_OSTYPE_ANDROID
@@ -771,7 +773,7 @@ void Logic::PushStringEditSetCall(const std::string& value) {
void Logic::PushStringEditCancelCall() {
thread()->PushCall([] {
if (!g_ui) {
- Log("Error: No ui in PushStringEditCancelCall.");
+ Log(LogLevel::kError, "No ui in PushStringEditCancelCall.");
return;
}
});
@@ -994,7 +996,7 @@ void Logic::PushAskUserForTelnetAccessCall() {
}
void Logic::PushPythonCall(const Object::Ref& call) {
- // Since we're mucking with refs, need to limit to game thread.
+ // Since we're mucking with refs, need to limit to logic thread.
BA_PRECONDITION(InLogicThread());
BA_PRECONDITION(call->object_strong_ref_count() > 0);
thread()->PushCall([call] {
@@ -1005,7 +1007,7 @@ void Logic::PushPythonCall(const Object::Ref& call) {
void Logic::PushPythonCallArgs(const Object::Ref& call,
const PythonRef& args) {
- // Since we're mucking with refs, need to limit to game thread.
+ // Since we're mucking with refs, need to limit to logic thread.
BA_PRECONDITION(InLogicThread());
BA_PRECONDITION(call->object_strong_ref_count() > 0);
thread()->PushCall([call, args] {
@@ -1015,7 +1017,7 @@ void Logic::PushPythonCallArgs(const Object::Ref& call,
}
void Logic::PushPythonWeakCall(const Object::WeakRef& call) {
- // Since we're mucking with refs, need to limit to game thread.
+ // Since we're mucking with refs, need to limit to logic thread.
BA_PRECONDITION(InLogicThread());
// Even though we only hold a weak ref, we expect a valid strong-reffed
@@ -1032,7 +1034,7 @@ void Logic::PushPythonWeakCall(const Object::WeakRef& call) {
void Logic::PushPythonWeakCallArgs(
const Object::WeakRef& call, const PythonRef& args) {
- // Since we're mucking with refs, need to limit to game thread.
+ // Since we're mucking with refs, need to limit to logic thread.
BA_PRECONDITION(InLogicThread());
// Even though we only hold a weak ref, we expect a valid strong-reffed
@@ -1181,7 +1183,7 @@ void Logic::PushConfirmQuitCall() {
thread()->PushCall([this] {
assert(InLogicThread());
if (HeadlessMode()) {
- Log("PushConfirmQuitCall() unhandled on headless.");
+ Log(LogLevel::kError, "PushConfirmQuitCall() unhandled on headless.");
} else {
// If input is locked, just quit immediately.. a confirm screen wouldn't
// work anyway
@@ -1233,7 +1235,7 @@ void Logic::Draw() {
HostActivity* ha = GetForegroundContext().GetHostActivity();
if (ha) {
int64_t step = ha->scene()->stepnum();
- Log(std::to_string(step - last_step));
+ Log(LogLevel::kInfo, std::to_string(step - last_step));
last_step = step;
}
}
@@ -1278,7 +1280,8 @@ void Logic::ApplyConfig() {
} else if (texqualstr == "Low") {
texture_quality_requested = TextureQuality::kLow;
} else {
- Log("Invalid texture quality: '" + texqualstr + "'; defaulting to low.");
+ Log(LogLevel::kError,
+ "Invalid texture quality: '" + texqualstr + "'; defaulting to low.");
texture_quality_requested = TextureQuality::kLow;
}
@@ -1298,8 +1301,8 @@ void Logic::ApplyConfig() {
} else if (gqualstr == "Low") {
graphics_quality_requested = GraphicsQuality::kLow;
} else {
- Log("Error: Invalid graphics quality: '" + gqualstr
- + "'; defaulting to auto.");
+ Log(LogLevel::kError,
+ "Invalid graphics quality: '" + gqualstr + "'; defaulting to auto.");
graphics_quality_requested = GraphicsQuality::kAuto;
}
@@ -1365,7 +1368,7 @@ void Logic::ApplyConfig() {
} else {
do_v_sync = false;
auto_v_sync = false;
- Log("Error: Invalid 'Vertical Sync' value: '" + v_sync + "'");
+ Log(LogLevel::kError, "Invalid 'Vertical Sync' value: '" + v_sync + "'");
}
g_graphics_server->PushSetVSyncCall(do_v_sync, auto_v_sync);
@@ -1505,7 +1508,7 @@ auto Logic::RemovePlayer(Player* player) -> void {
if (HostSession* host_session = player->GetHostSession()) {
host_session->RemovePlayer(player);
} else {
- Log("Got RemovePlayer call but have no host_session");
+ Log(LogLevel::kError, "Got RemovePlayer call but have no host_session");
}
}
@@ -1526,7 +1529,8 @@ void Logic::SetRealTimerLength(int timer_id, millisecs_t length) {
if (t) {
t->SetLength(length);
} else {
- Log("Error: Logic::SetRealTimerLength() called on nonexistent timer.");
+ Log(LogLevel::kError,
+ "Logic::SetRealTimerLength() called on nonexistent timer.");
}
}
@@ -1577,8 +1581,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string {
if (!printed) {
printed = true;
char* c = cJSON_Print(obj);
- BA_LOG_ONCE("found long key 'resource' in raw lstr json: "
- + std::string(c));
+ BA_LOG_ONCE(
+ LogLevel::kError,
+ "found long key 'resource' in raw lstr json: " + std::string(c));
free(c);
}
}
@@ -1596,8 +1601,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string {
if (!printed) {
printed = true;
char* c = cJSON_Print(obj);
- BA_LOG_ONCE("found long key 'fallback' in raw lstr json: "
- + std::string(c));
+ BA_LOG_ONCE(
+ LogLevel::kError,
+ "found long key 'fallback' in raw lstr json: " + std::string(c));
free(c);
}
}
@@ -1620,8 +1626,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string {
if (!printed) {
printed = true;
char* c = cJSON_Print(obj);
- BA_LOG_ONCE("found long key 'translate' in raw lstr json: "
- + std::string(c));
+ BA_LOG_ONCE(
+ LogLevel::kError,
+ "found long key 'translate' in raw lstr json: " + std::string(c));
free(c);
}
}
@@ -1658,8 +1665,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string {
if (!printed) {
printed = true;
char* c = cJSON_Print(obj);
- BA_LOG_ONCE("found long key 'value' in raw lstr json: "
- + std::string(c));
+ BA_LOG_ONCE(
+ LogLevel::kError,
+ "found long key 'value' in raw lstr json: " + std::string(c));
free(c);
}
}
@@ -1689,8 +1697,8 @@ auto DoCompileResourceString(cJSON* obj) -> std::string {
if (!printed) {
printed = true;
char* c = cJSON_Print(obj);
- BA_LOG_ONCE("found long key 'subs' in raw lstr json: "
- + std::string(c));
+ BA_LOG_ONCE(LogLevel::kError, "found long key 'subs' in raw lstr json: "
+ + std::string(c));
free(c);
}
}
@@ -1761,8 +1769,8 @@ auto Logic::CompileResourceString(const std::string& s, const std::string& loc,
cJSON* root = cJSON_Parse(s.c_str());
if (root == nullptr) {
- Log("CompileResourceString failed (loc " + loc + "); invalid json: '" + s
- + "'");
+ Log(LogLevel::kError, "CompileResourceString failed (loc " + loc
+ + "); invalid json: '" + s + "'");
*valid = false;
return "";
}
@@ -1771,8 +1779,8 @@ auto Logic::CompileResourceString(const std::string& s, const std::string& loc,
result = DoCompileResourceString(root);
*valid = true;
} catch (const std::exception& e) {
- Log("CompileResourceString failed (loc " + loc
- + "): " + std::string(e.what()) + "; str='" + s + "'");
+ Log(LogLevel::kError, "CompileResourceString failed (loc " + loc + "): "
+ + std::string(e.what()) + "; str='" + s + "'");
result = "";
*valid = false;
}
diff --git a/src/ballistica/logic/logic.h b/src/ballistica/logic/logic.h
index 7fcd4163..57799970 100644
--- a/src/ballistica/logic/logic.h
+++ b/src/ballistica/logic/logic.h
@@ -44,7 +44,7 @@ class Logic {
/// Push a generic 'menu press' event, optionally associated with an
/// input device (nullptr to specify none). Note: caller must ensure
- /// a RemoveInputDevice() call does not arrive at the game thread
+ /// a RemoveInputDevice() call does not arrive at the logic thread
/// before this one.
auto PushMainMenuPressCall(InputDevice* device) -> void;
@@ -71,12 +71,13 @@ class Logic {
auto PushMediaPruneCall(int level) -> void;
auto PushAskUserForTelnetAccessCall() -> void;
- // Push Python call and keep it alive; must be called from game thread.
+ // Push Python call and keep it alive; must be called from logic thread.
auto PushPythonCall(const Object::Ref& call) -> void;
auto PushPythonCallArgs(const Object::Ref& call,
const PythonRef& args) -> void;
- // Push Python call without keeping it alive; must be called from game thread.
+ // Push Python call without keeping it alive; must be called from logic
+ // thread.
auto PushPythonWeakCall(const Object::WeakRef& call)
-> void;
auto PushPythonWeakCallArgs(const Object::WeakRef& call,
diff --git a/src/ballistica/logic/player_spec.cc b/src/ballistica/logic/player_spec.cc
index 71b6f515..8f8f28c9 100644
--- a/src/ballistica/logic/player_spec.cc
+++ b/src/ballistica/logic/player_spec.cc
@@ -33,7 +33,7 @@ PlayerSpec::PlayerSpec(const std::string& s) {
cJSON_Delete(root_obj);
}
if (!success) {
- Log("Error creating PlayerSpec from string: '" + s + "'");
+ Log(LogLevel::kError, "Error creating PlayerSpec from string: '" + s + "'");
name_ = "";
short_name_ = "";
account_type_ = V1AccountType::kInvalid;
@@ -95,7 +95,7 @@ auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec {
}
if (spec.name_.size() > 100) {
// FIXME should perhaps clamp this in unicode space
- Log("account name size too long: '" + spec.name_ + "'");
+ Log(LogLevel::kError, "account name size too long: '" + spec.name_ + "'");
spec.name_.resize(100);
spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgaps3");
}
@@ -107,7 +107,8 @@ auto PlayerSpec::GetDummyPlayerSpec(const std::string& name) -> PlayerSpec {
spec.name_ = Utils::GetValidUTF8(name.c_str(), "bsgdps1");
if (spec.name_.size() > 100) {
// FIXME should perhaps clamp this in unicode space
- Log("dummy player spec name too long: '" + spec.name_ + "'");
+ Log(LogLevel::kError,
+ "dummy player spec name too long: '" + spec.name_ + "'");
spec.name_.resize(100);
spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgdps2");
}
diff --git a/src/ballistica/logic/session/client_session.cc b/src/ballistica/logic/session/client_session.cc
index d9b72a57..73e5b67a 100644
--- a/src/ballistica/logic/session/client_session.cc
+++ b/src/ballistica/logic/session/client_session.cc
@@ -196,10 +196,12 @@ void ClientSession::Update(int time_advance) {
if (g_buildconfig.debug_build()) {
if (current_cmd_ptr_ != nullptr) {
if (current_cmd_ptr_ != &(current_cmd_[0]) + current_cmd_.size()) {
- Log("SIZE ERROR FOR CMD "
- + std::to_string(static_cast(current_cmd_[0]))
- + " expected " + std::to_string(current_cmd_.size()) + " got "
- + std::to_string(current_cmd_ptr_ - &(current_cmd_[0])));
+ Log(LogLevel::kError,
+ "SIZE ERROR FOR CMD "
+ + std::to_string(static_cast(current_cmd_[0]))
+ + " expected " + std::to_string(current_cmd_.size())
+ + " got "
+ + std::to_string(current_cmd_ptr_ - &(current_cmd_[0])));
}
}
assert(current_cmd_ptr_ == current_cmd_.data() + current_cmd_.size());
@@ -953,7 +955,7 @@ auto ClientSession::GetCollideModel(int id) const -> CollideModel* {
}
void ClientSession::Error(const std::string& description) {
- Log("ERROR: client session error: " + description);
+ Log(LogLevel::kError, "Client session error: " + description);
End();
}
diff --git a/src/ballistica/logic/session/host_session.cc b/src/ballistica/logic/session/host_session.cc
index 6dbbe3a1..caa5b7dc 100644
--- a/src/ballistica/logic/session/host_session.cc
+++ b/src/ballistica/logic/session/host_session.cc
@@ -242,7 +242,8 @@ void HostSession::RequestPlayer(InputDevice* device) {
// Ignore if we have no Python session obj.
if (!GetSessionPyObj()) {
- Log("Error: HostSession::RequestPlayer() called w/no session_py_obj_.");
+ Log(LogLevel::kError,
+ "HostSession::RequestPlayer() called w/no session_py_obj_.");
return;
}
@@ -325,11 +326,13 @@ void HostSession::IssuePlayerLeft(Player* player) {
BA_LOG_PYTHON_TRACE_ONCE("missing player on IssuePlayerLeft");
}
} else {
- Log("WARNING: HostSession: IssuePlayerLeft caled with no "
+ Log(LogLevel::kWarning,
+ "HostSession: IssuePlayerLeft caled with no "
"session_py_obj_");
}
} catch (const std::exception& e) {
- Log(std::string("Error calling on_player_leave(): ") + e.what());
+ Log(LogLevel::kError,
+ std::string("Error calling on_player_leave(): ") + e.what());
}
}
@@ -347,7 +350,8 @@ void HostSession::SetForegroundHostActivity(HostActivity* a) {
assert(InLogicThread());
if (shutting_down_) {
- Log("WARNING: SetForegroundHostActivity called during session shutdown; "
+ Log(LogLevel::kWarning,
+ "SetForegroundHostActivity called during session shutdown; "
"ignoring.");
return;
}
@@ -603,7 +607,7 @@ HostSession::~HostSession() {
if (g_buildconfig.debug_build()) {
PruneDeadRefs(&python_calls_);
if (python_calls_.size() > 1) {
- std::string s = "WARNING: " + std::to_string(python_calls_.size())
+ std::string s = std::to_string(python_calls_.size())
+ " live PythonContextCalls at shutdown for "
+ "HostSession" + " (1 call is expected):";
int count = 1;
@@ -611,11 +615,12 @@ HostSession::~HostSession() {
s += ("\n " + std::to_string(count++) + ": "
+ i->GetObjectDescription());
}
- Log(s);
+ Log(LogLevel::kWarning, s);
}
}
} catch (const std::exception& e) {
- Log("Exception in HostSession destructor: " + std::string(e.what()));
+ Log(LogLevel::kError,
+ "Exception in HostSession destructor: " + std::string(e.what()));
}
}
@@ -626,8 +631,9 @@ void HostSession::RegisterCall(PythonContextCall* call) {
// If we're shutting down, just kill the call immediately.
// (we turn all of our calls to no-ops as we shut down).
if (shutting_down_) {
- Log("WARNING: adding call to expired session; call will not function: "
- + call->GetObjectDescription());
+ Log(LogLevel::kWarning,
+ "Adding call to expired session; call will not function: "
+ + call->GetObjectDescription());
call->MarkDead();
}
}
diff --git a/src/ballistica/logic/session/net_client_session.cc b/src/ballistica/logic/session/net_client_session.cc
index 795bd1ee..a1f6b7b0 100644
--- a/src/ballistica/logic/session/net_client_session.cc
+++ b/src/ballistica/logic/session/net_client_session.cc
@@ -13,7 +13,8 @@ namespace ballistica {
NetClientSession::NetClientSession() {
// Sanity check: we should only ever be writing one replay at once.
if (g_app->replay_open) {
- Log("ERROR: g_replay_open true at netclient start; shouldn't happen.");
+ Log(LogLevel::kError,
+ "g_replay_open true at netclient start; shouldn't happen.");
}
assert(g_assets_server);
g_assets_server->PushBeginWriteReplayCall();
@@ -25,7 +26,8 @@ NetClientSession::~NetClientSession() {
if (writing_replay_) {
// Sanity check: we should only ever be writing one replay at once.
if (!g_app->replay_open) {
- Log("ERROR: g_replay_open false at net-client close; shouldn't happen.");
+ Log(LogLevel::kError,
+ "g_replay_open false at net-client close; shouldn't happen.");
}
g_app->replay_open = false;
assert(g_assets_server);
diff --git a/src/ballistica/logic/session/replay_client_session.cc b/src/ballistica/logic/session/replay_client_session.cc
index c93fb45f..d422cb4d 100644
--- a/src/ballistica/logic/session/replay_client_session.cc
+++ b/src/ballistica/logic/session/replay_client_session.cc
@@ -45,14 +45,16 @@ void ReplayClientSession::OnClientConnected(ConnectionToClient* c) {
// sanity check - abort if its on either of our lists already
for (ConnectionToClient* i : connections_to_clients_) {
if (i == c) {
- Log("Error: ReplayClientSession::OnClientConnected()"
+ Log(LogLevel::kError,
+ "ReplayClientSession::OnClientConnected()"
" got duplicate connection");
return;
}
}
for (ConnectionToClient* i : connections_to_clients_ignored_) {
if (i == c) {
- Log("Error: ReplayClientSession::OnClientConnected()"
+ Log(LogLevel::kError,
+ "ReplayClientSession::OnClientConnected()"
" got duplicate connection");
return;
}
@@ -113,7 +115,8 @@ void ReplayClientSession::OnClientDisconnected(ConnectionToClient* c) {
return;
}
}
- Log("Error: ReplayClientSession::OnClientDisconnected()"
+ Log(LogLevel::kError,
+ "ReplayClientSession::OnClientDisconnected()"
" called for connection not on lists");
}
diff --git a/src/ballistica/logic/session/session.cc b/src/ballistica/logic/session/session.cc
index facb0460..a785e291 100644
--- a/src/ballistica/logic/session/session.cc
+++ b/src/ballistica/logic/session/session.cc
@@ -31,7 +31,8 @@ void Session::GraphicsQualityChanged(GraphicsQuality q) {}
void Session::DebugSpeedMultChanged() {}
void Session::DumpFullState(SceneStream* out) {
- Log("Session::DumpFullState() being called; shouldn't happen.");
+ Log(LogLevel::kError,
+ "Session::DumpFullState() being called; shouldn't happen.");
}
} // namespace ballistica
diff --git a/src/ballistica/logic/v1_account.cc b/src/ballistica/logic/v1_account.cc
index 23ccd39b..399211cc 100644
--- a/src/ballistica/logic/v1_account.cc
+++ b/src/ballistica/logic/v1_account.cc
@@ -150,7 +150,7 @@ void V1Account::SetLogin(V1AccountType account_type, V1LoginState login_state,
{
std::scoped_lock lock(mutex_);
- // We call out to Python so need to be in game thread.
+ // We call out to Python so need to be in logic thread.
assert(InLogicThread());
if (login_state_ != login_state || g_app->account_type != account_type
|| login_id_ != login_id || login_name_ != login_name) {
diff --git a/src/ballistica/networking/network_reader.cc b/src/ballistica/networking/network_reader.cc
index 15b62d2a..837dc644 100644
--- a/src/ballistica/networking/network_reader.cc
+++ b/src/ballistica/networking/network_reader.cc
@@ -43,7 +43,7 @@ auto NetworkReader::Pause() -> void {
if (port4_ != -1) {
PokeSelf();
} else {
- Log("Error: NetworkReader port is -1 on pause");
+ Log(LogLevel::kError, "NetworkReader port is -1 on pause");
}
}
@@ -63,8 +63,8 @@ void NetworkReader::Resume() {
void NetworkReader::PokeSelf() {
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
- Log("ERROR: unable to create sleep ping socket; errno "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Unable to create sleep ping socket; errno "
+ + g_platform->GetSocketErrorString());
} else {
struct sockaddr_in serv_addr {};
memset(&serv_addr, 0, sizeof(serv_addr));
@@ -73,8 +73,8 @@ void NetworkReader::PokeSelf() {
serv_addr.sin_port = 0; // any
int bresult = ::bind(sd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (bresult == 1) {
- Log("ERROR: unable to bind sleep socket: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError,
+ "Unable to bind sleep socket: " + g_platform->GetSocketErrorString());
} else {
struct sockaddr_in t_addr {};
memset(&t_addr, 0, sizeof(t_addr));
@@ -85,8 +85,8 @@ void NetworkReader::PokeSelf() {
ssize_t sresult =
sendto(sd, b, 1, 0, (struct sockaddr*)(&t_addr), sizeof(t_addr));
if (sresult == -1) {
- Log("Error on sleep self-sendto: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Error on sleep self-sendto: "
+ + g_platform->GetSocketErrorString());
}
}
g_platform->CloseSocket(sd);
@@ -144,7 +144,7 @@ static auto HandleGameQuery(const char* buffer, size_t size,
BA_PRECONDITION_FATAL(!usid.empty());
if (usid.size() > 100) {
- Log("had to truncate session-id; shouldn't happen");
+ Log(LogLevel::kError, "had to truncate session-id; shouldn't happen");
usid.resize(100);
}
if (usid.empty()) {
@@ -170,8 +170,8 @@ static auto HandleGameQuery(const char* buffer, size_t size,
g_network_writer->PushSendToCall(msg_buffer, SockAddr(*from));
} else {
- Log("Error: Got invalid game-query packet of len " + std::to_string(size)
- + "; expected 5.");
+ Log(LogLevel::kError, "Got invalid game-query packet of len "
+ + std::to_string(size) + "; expected 5.");
}
}
@@ -232,7 +232,8 @@ auto NetworkReader::RunThread() -> int {
// Aint no thang.
} else {
// Let's complain for anything else though.
- Log("Error on select: " + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError,
+ "Error on select: " + g_platform->GetSocketErrorString());
}
} else {
// Wait for any data on either of our sockets.
@@ -244,7 +245,8 @@ auto NetworkReader::RunThread() -> int {
recvfrom(sd, buffer, sizeof(buffer), 0,
reinterpret_cast(&from), &from_size);
if (rresult == 0) {
- Log("ERROR: NetworkReader Recv got length 0; this shouldn't "
+ Log(LogLevel::kError,
+ "NetworkReader Recv got length 0; this shouldn't "
"happen");
} else if (rresult == -1) {
// This needs to be locked during any sd changes/writes.
@@ -354,7 +356,7 @@ auto NetworkReader::RunThread() -> int {
case BA_PACKET_CLIENT_GAMEPACKET_COMPRESSED:
case BA_PACKET_HOST_GAMEPACKET_COMPRESSED: {
// These messages are associated with udp host/client
- // connections.. pass them to the game thread to wrangle.
+ // connections.. pass them to the logic thread to wrangle.
std::vector msg_buffer(rresult2);
memcpy(&(msg_buffer[0]), buffer, rresult2);
g_logic->connections()->PushUDPConnectionPacketCall(
@@ -395,8 +397,8 @@ auto NetworkReader::OpenSockets() -> void {
sd4_ = socket(AF_INET, SOCK_DGRAM, 0);
if (sd4_ < 0) {
- Log("ERROR: Unable to open host socket; errno "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Unable to open host socket; errno "
+ + g_platform->GetSocketErrorString());
} else {
g_platform->SetSocketNonBlocking(sd4_);
@@ -413,9 +415,8 @@ auto NetworkReader::OpenSockets() -> void {
// If we're headless then we abort here; we're useless if we don't get
// the port we wanted.
if (HeadlessMode()) {
- Log("FATAL ERROR: unable to bind to requested udp port "
- + std::to_string(port4_) + " (ipv4)");
- exit(1);
+ FatalError("Unable to bind to requested udp port "
+ + std::to_string(port4_) + " (ipv4)");
}
// Primary ipv4 bind failed; try on any port as a backup.
@@ -450,8 +451,8 @@ auto NetworkReader::OpenSockets() -> void {
// available everywhere (win XP, etc) so let's do this for now.
sd6_ = socket(AF_INET6, SOCK_DGRAM, 0);
if (sd6_ < 0) {
- Log("ERROR: Unable to open ipv6 socket: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError,
+ "Unable to open ipv6 socket: " + g_platform->GetSocketErrorString());
} else {
// Since we're explicitly creating both a v4 and v6 socket, tell the v6
// to *not* do both itself (not sure if this is necessary; on mac it
@@ -460,7 +461,7 @@ auto NetworkReader::OpenSockets() -> void {
if (setsockopt(sd6_, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast(&on), sizeof(on))
== -1) {
- Log("error setting socket as ipv6-only");
+ Log(LogLevel::kError, "Error setting socket as ipv6-only");
}
g_platform->SetSocketNonBlocking(sd6_);
@@ -473,9 +474,8 @@ auto NetworkReader::OpenSockets() -> void {
if (result != 0) {
if (HeadlessMode()) {
- Log("FATAL ERROR: unable to bind to requested udp port "
- + std::to_string(port6_) + " (ipv6)");
- exit(1);
+ FatalError("Unable to bind to requested udp port "
+ + std::to_string(port6_) + " (ipv6)");
}
// Primary ipv6 bind failed; try backup.
@@ -508,9 +508,9 @@ auto NetworkReader::OpenSockets() -> void {
+ std::to_string(initial_requested_port)
+ "; some network functionality may fail.",
{1, 0.5f, 0});
- Log("Unable to bind udp port " + std::to_string(initial_requested_port)
- + "; some network functionality may fail.",
- true, false);
+ Log(LogLevel::kWarning, "Unable to bind udp port "
+ + std::to_string(initial_requested_port)
+ + "; some network functionality may fail.");
}
}
diff --git a/src/ballistica/networking/network_writer.cc b/src/ballistica/networking/network_writer.cc
index 82679ce5..3050e8b5 100644
--- a/src/ballistica/networking/network_writer.cc
+++ b/src/ballistica/networking/network_writer.cc
@@ -22,7 +22,8 @@ void NetworkWriter::PushSendToCall(const std::vector& msg,
// Avoid buffer-full errors if something is causing us to write too often;
// these are unreliable messages so its ok to just drop them.
if (!thread()->CheckPushSafety()) {
- BA_LOG_ONCE("Excessive send-to calls in net-write-module.");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Excessive send-to calls in net-write-module.");
return;
}
thread()->PushCall([this, msg, addr] {
diff --git a/src/ballistica/networking/networking.cc b/src/ballistica/networking/networking.cc
index 3aaca894..7af90cf3 100644
--- a/src/ballistica/networking/networking.cc
+++ b/src/ballistica/networking/networking.cc
@@ -32,14 +32,14 @@ void Networking::HostScanCycle() {
scan_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
if (scan_socket_ == -1) {
- Log("Error opening scan socket: " + g_platform->GetSocketErrorString()
- + ".");
+ Log(LogLevel::kError, "Error opening scan socket: "
+ + g_platform->GetSocketErrorString() + ".");
return;
}
// Since this guy lives in the game-thread we need it to not block.
if (!g_platform->SetSocketNonBlocking(scan_socket_)) {
- Log("Error setting socket non-blocking.");
+ Log(LogLevel::kError, "Error setting socket non-blocking.");
g_platform->CloseSocket(scan_socket_);
scan_socket_ = -1;
return;
@@ -54,7 +54,8 @@ void Networking::HostScanCycle() {
int result =
::bind(scan_socket_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (result == 1) {
- Log("Error binding socket: " + g_platform->GetSocketErrorString() + ".");
+ Log(LogLevel::kError,
+ "Error binding socket: " + g_platform->GetSocketErrorString() + ".");
g_platform->CloseSocket(scan_socket_);
scan_socket_ = -1;
return;
@@ -66,8 +67,8 @@ void Networking::HostScanCycle() {
sizeof(op_val));
if (result != 0) {
- Log("Error enabling broadcast for scan-socket: "
- + g_platform->GetSocketErrorString() + ".");
+ Log(LogLevel::kError, "Error enabling broadcast for scan-socket: "
+ + g_platform->GetSocketErrorString() + ".");
g_platform->CloseSocket(scan_socket_);
scan_socket_ = -1;
return;
@@ -99,8 +100,8 @@ void Networking::HostScanCycle() {
case ENETUNREACH:
break;
default:
- Log("Error on scanSocket sendto: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Error on scanSocket sendto: "
+ + g_platform->GetSocketErrorString());
}
}
}
@@ -122,7 +123,8 @@ void Networking::HostScanCycle() {
case EWOULDBLOCK:
break;
default:
- Log("Error: recvfrom error: " + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError,
+ "Error: recvfrom error: " + g_platform->GetSocketErrorString());
break;
}
break;
@@ -179,10 +181,12 @@ void Networking::HostScanCycle() {
PruneScanResults();
}
} else {
- Log("Error: Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet");
+ Log(LogLevel::kError,
+ "Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet");
}
} else {
- Log("Error: Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet");
+ Log(LogLevel::kError,
+ "Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet");
}
}
}
@@ -228,7 +232,8 @@ void Networking::EndHostScanning() {
void Networking::Pause() {
if (!running_) {
- Log("Networking::pause() called with running_ already false");
+ Log(LogLevel::kError,
+ "Networking::pause() called with running_ already false");
}
running_ = false;
@@ -238,7 +243,8 @@ void Networking::Pause() {
void Networking::Resume() {
if (running_) {
- Log("Networking::resume() called with running_ already true");
+ Log(LogLevel::kError,
+ "Networking::resume() called with running_ already true");
}
running_ = true;
}
diff --git a/src/ballistica/networking/networking.h b/src/ballistica/networking/networking.h
index 76f6766e..6554d5c5 100644
--- a/src/ballistica/networking/networking.h
+++ b/src/ballistica/networking/networking.h
@@ -112,7 +112,7 @@ namespace ballistica {
#define HUFFMAN_TRAINING_MODE 0
#endif
-// Bits used by the game thread for network communication.
+// Bits used by the logic thread for network communication.
class Networking {
public:
// Send a message to an address. This may block for a brief moment, so it can
diff --git a/src/ballistica/networking/telnet_server.cc b/src/ballistica/networking/telnet_server.cc
index 91c430c1..7a75d1f9 100644
--- a/src/ballistica/networking/telnet_server.cc
+++ b/src/ballistica/networking/telnet_server.cc
@@ -69,7 +69,8 @@ auto TelnetServer::RunThread() -> int {
sd_ = socket(AF_INET, SOCK_STREAM, 0);
if (sd_ < 0) {
- Log("Error: Unable to open host socket; errno " + std::to_string(errno));
+ Log(LogLevel::kError,
+ "Unable to open host socket; errno " + std::to_string(errno));
return 1;
}
@@ -78,7 +79,7 @@ auto TelnetServer::RunThread() -> int {
int status =
setsockopt(sd_, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
if (-1 == status) {
- Log("Error setting SO_REUSEADDR on telnet server");
+ Log(LogLevel::kError, "Error setting SO_REUSEADDR on telnet server");
}
// Bind to local server port.
@@ -221,7 +222,7 @@ void TelnetServer::PushPrint(const std::string& s) {
}
void TelnetServer::Print(const std::string& s) {
- // Currently we make the assumption that *only* the game thread writes to our
+ // Currently we make the assumption that *only* the logic thread writes to our
// socket.
assert(InLogicThread());
if (client_sd_ != -1) {
diff --git a/src/ballistica/platform/apple/platform_apple.h b/src/ballistica/platform/apple/platform_apple.h
index 90055735..f9a31860 100644
--- a/src/ballistica/platform/apple/platform_apple.h
+++ b/src/ballistica/platform/apple/platform_apple.h
@@ -25,7 +25,8 @@ class PlatformApple : public Platform {
auto DoHasTouchScreen() -> bool override;
auto GetUIScale() -> UIScale override;
auto IsRunningOnDesktop() -> bool override;
- auto HandleLog(const std::string& msg) -> void override;
+ auto DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) -> void override;
auto SetupDataDirectory() -> void override;
auto GetTextBoundsAndWidth(const std::string& text, Rect* r, float* width)
-> void override;
diff --git a/src/ballistica/platform/linux/platform_linux.cc b/src/ballistica/platform/linux/platform_linux.cc
index de9f1ed9..58af4402 100644
--- a/src/ballistica/platform/linux/platform_linux.cc
+++ b/src/ballistica/platform/linux/platform_linux.cc
@@ -63,8 +63,8 @@ void PlatformLinux::OpenFileExternally(const std::string& path) {
std::string cmd = std::string("xdg-open \"") + path + "\"";
int result = system(cmd.c_str());
if (result != 0) {
- Log("Error: Got return value " + std::to_string(result)
- + " on xdg-open cmd '" + cmd + "'");
+ Log(LogLevel::kError, "Got return value " + std::to_string(result)
+ + " on xdg-open cmd '" + cmd + "'");
}
}
@@ -72,8 +72,8 @@ void PlatformLinux::OpenDirExternally(const std::string& path) {
std::string cmd = std::string("xdg-open \"") + path + "\"";
int result = system(cmd.c_str());
if (result != 0) {
- Log("Error: Got return value " + std::to_string(result)
- + " on xdg-open cmd '" + cmd + "'");
+ Log(LogLevel::kError, "Got return value " + std::to_string(result)
+ + " on xdg-open cmd '" + cmd + "'");
}
}
diff --git a/src/ballistica/platform/platform.cc b/src/ballistica/platform/platform.cc
index 8ce6ada9..0fa607e7 100644
--- a/src/ballistica/platform/platform.cc
+++ b/src/ballistica/platform/platform.cc
@@ -169,10 +169,12 @@ auto Platform::GetLegacyDeviceUUID() -> const std::string& {
legacy_device_uuid_ += val;
if (FILE* f2 = FOpen(path.c_str(), "wb")) {
size_t result = fwrite(val.c_str(), val.size(), 1, f2);
- if (result != 1) Log("unable to write bsuuid file.");
+ if (result != 1)
+ Log(LogLevel::kError, "unable to write bsuuid file.");
fclose(f2);
} else {
- Log("unable to open bsuuid file for writing: '" + path + "'");
+ Log(LogLevel::kError,
+ "unable to open bsuuid file for writing: '" + path + "'");
}
}
}
@@ -182,7 +184,7 @@ auto Platform::GetLegacyDeviceUUID() -> const std::string& {
}
auto Platform::GetDeviceV1AccountUUIDPrefix() -> std::string {
- Log("GetDeviceV1AccountUUIDPrefix() unimplemented");
+ Log(LogLevel::kError, "GetDeviceV1AccountUUIDPrefix() unimplemented");
return "u";
}
@@ -261,10 +263,11 @@ void Platform::SetLowLevelConfigValue(const char* key, int value) {
FILE* f = FOpen(path.c_str(), "w");
if (f) {
size_t result = fwrite(out.c_str(), out.size(), 1, f);
- if (result != 1) Log("unable to write low level config file.");
+ if (result != 1)
+ Log(LogLevel::kError, "unable to write low level config file.");
fclose(f);
} else {
- Log("unable to open low level config file for writing.");
+ Log(LogLevel::kError, "unable to open low level config file for writing.");
}
}
@@ -306,11 +309,10 @@ auto Platform::GetAppPythonDirectory() -> std::string {
// Fall back to our default if that doesn't exist.
if (FilePathExists(app_python_dir_)) {
using_custom_app_python_dir_ = true;
- Log("Using custom app Python path: '"
- + (GetUserPythonDirectory() + BA_DIRSLASH + "sys" + BA_DIRSLASH
- + kAppVersion)
- + "'.",
- true, false);
+ Log(LogLevel::kInfo, "Using custom app Python path: '"
+ + (GetUserPythonDirectory() + BA_DIRSLASH + "sys"
+ + BA_DIRSLASH + kAppVersion)
+ + "'.");
} else {
// Going with relative paths for cleaner tracebacks...
@@ -484,7 +486,8 @@ auto Platform::GetLocale() -> std::string {
return lang;
} else {
if (!g_buildconfig.headless_build()) {
- BA_LOG_ONCE("No LANG value available; defaulting to en_US");
+ BA_LOG_ONCE(LogLevel::kError,
+ "No LANG value available; defaulting to en_US");
}
return "en_US";
}
@@ -588,7 +591,7 @@ static void HandleArgs(int argc, char** argv) {
exit(-1);
}
} else {
- Log("ERROR: expected arg after -cfgdir");
+ Log(LogLevel::kError, "Expected arg after -cfgdir.");
exit(-1);
}
}
@@ -672,7 +675,8 @@ auto Platform::GetUIScale() -> UIScale {
return UIScale::kLarge;
}
-void Platform::HandleLog(const std::string& msg) {
+void Platform::DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) {
// Do nothing by default.
}
@@ -848,13 +852,13 @@ auto Platform::ConvertIncomingLeaderboardScore(
void Platform::GetFriendScores(const std::string& game,
const std::string& game_version, void* data) {
// As a default, just fail gracefully.
- Log("FIXME: GetFriendScores unimplemented");
+ Log(LogLevel::kError, "FIXME: GetFriendScores unimplemented");
g_logic->PushFriendScoreSetCall(FriendScoreSet(false, data));
}
void Platform::SubmitScore(const std::string& game, const std::string& version,
int64_t score) {
- Log("FIXME: SubmitScore() unimplemented");
+ Log(LogLevel::kError, "FIXME: SubmitScore() unimplemented");
}
void Platform::ReportAchievement(const std::string& achievement) {}
@@ -866,13 +870,13 @@ auto Platform::HaveLeaderboard(const std::string& game,
void Platform::EditText(const std::string& title, const std::string& value,
int max_chars) {
- Log("FIXME: EditText() unimplemented");
+ Log(LogLevel::kError, "FIXME: EditText() unimplemented");
}
void Platform::ShowOnlineScoreUI(const std::string& show,
const std::string& game,
const std::string& game_version) {
- Log("FIXME: ShowOnlineScoreUI() unimplemented");
+ Log(LogLevel::kError, "FIXME: ShowOnlineScoreUI() unimplemented");
}
void Platform::Purchase(const std::string& item) {
@@ -880,7 +884,9 @@ void Platform::Purchase(const std::string& item) {
g_python->PushObjCall(Python::ObjID::kUnavailableMessageCall);
}
-void Platform::RestorePurchases() { Log("RestorePurchases() unimplemented"); }
+void Platform::RestorePurchases() {
+ Log(LogLevel::kError, "RestorePurchases() unimplemented");
+}
void Platform::AndroidSetResString(const std::string& res) {
throw Exception();
@@ -889,11 +895,11 @@ void Platform::AndroidSetResString(const std::string& res) {
void Platform::ApplyConfig() {}
void Platform::AndroidSynthesizeBackPress() {
- Log("AndroidSynthesizeBackPress() unimplemented");
+ Log(LogLevel::kError, "AndroidSynthesizeBackPress() unimplemented");
}
void Platform::AndroidQuitActivity() {
- Log("AndroidQuitActivity() unimplemented");
+ Log(LogLevel::kError, "AndroidQuitActivity() unimplemented");
}
auto Platform::GetDeviceV1AccountID() -> std::string {
@@ -920,7 +926,8 @@ auto Platform::DemangleCXXSymbol(const std::string& s) -> std::string {
abi::__cxa_demangle(s.c_str(), nullptr, nullptr, &demangle_status);
if (demangled_name != nullptr) {
if (demangle_status != 0) {
- BA_LOG_ONCE("__cxa_demangle got buffer but non-zero status; unexpected");
+ BA_LOG_ONCE(LogLevel::kError,
+ "__cxa_demangle got buffer but non-zero status; unexpected");
}
std::string retval = demangled_name;
free(static_cast(demangled_name));
@@ -938,7 +945,7 @@ auto Platform::NewAutoReleasePool() -> void* { throw Exception(); }
void Platform::DrainAutoReleasePool(void* pool) { throw Exception(); }
void Platform::OpenURL(const std::string& url) {
- // Can't open URLs in VR - just tell the game thread to show the url.
+ // Can't open URLs in VR - just tell the logic thread to show the url.
if (IsVRMode()) {
g_logic->PushShowURLCall(url);
return;
@@ -949,16 +956,18 @@ void Platform::OpenURL(const std::string& url) {
}
void Platform::DoOpenURL(const std::string& url) {
- Log("DoOpenURL unimplemented on this platform.");
+ Log(LogLevel::kError, "DoOpenURL unimplemented on this platform.");
}
-void Platform::ResetAchievements() { Log("ResetAchievements() unimplemented"); }
+void Platform::ResetAchievements() {
+ Log(LogLevel::kError, "ResetAchievements() unimplemented");
+}
void Platform::GameCenterLogin() { throw Exception(); }
void Platform::PurchaseAck(const std::string& purchase,
const std::string& order_id) {
- Log("PurchaseAck() unimplemented");
+ Log(LogLevel::kError, "PurchaseAck() unimplemented");
}
void Platform::RunEvents() {}
@@ -969,17 +978,18 @@ void Platform::OnAppPause() {}
void Platform::OnAppResume() {}
void Platform::MusicPlayerPlay(PyObject* target) {
- Log("MusicPlayerPlay() unimplemented on this platform");
+ Log(LogLevel::kError, "MusicPlayerPlay() unimplemented on this platform");
}
void Platform::MusicPlayerStop() {
- Log("MusicPlayerStop() unimplemented on this platform");
+ Log(LogLevel::kError, "MusicPlayerStop() unimplemented on this platform");
}
void Platform::MusicPlayerShutdown() {
- Log("MusicPlayerShutdown() unimplemented on this platform");
+ Log(LogLevel::kError, "MusicPlayerShutdown() unimplemented on this platform");
}
void Platform::MusicPlayerSetVolume(float volume) {
- Log("MusicPlayerSetVolume() unimplemented on this platform");
+ Log(LogLevel::kError,
+ "MusicPlayerSetVolume() unimplemented on this platform");
}
auto Platform::IsOSPlayingMusic() -> bool { return false; }
@@ -987,7 +997,7 @@ auto Platform::IsOSPlayingMusic() -> bool { return false; }
void Platform::AndroidShowAppInvite(const std::string& title,
const std::string& message,
const std::string& code) {
- Log("AndroidShowAppInvite() unimplemented");
+ Log(LogLevel::kError, "AndroidShowAppInvite() unimplemented");
}
void Platform::IncrementAnalyticsCount(const std::string& name, int increment) {
@@ -1006,11 +1016,11 @@ void Platform::SubmitAnalyticsCounts() {}
void Platform::SetPlatformMiscReadVals(const std::string& vals) {}
void Platform::AndroidRefreshFile(const std::string& file) {
- Log("AndroidRefreshFile() unimplemented");
+ Log(LogLevel::kError, "AndroidRefreshFile() unimplemented");
}
void Platform::ShowAd(const std::string& purpose) {
- Log("ShowAd() unimplemented");
+ Log(LogLevel::kError, "ShowAd() unimplemented");
}
auto Platform::GetHasAds() -> bool { return false; }
@@ -1021,17 +1031,19 @@ auto Platform::GetHasVideoAds() -> bool {
}
void Platform::SignInV1(const std::string& account_type) {
- Log("SignInV1() unimplemented");
+ Log(LogLevel::kError, "SignInV1() unimplemented");
}
void Platform::V1LoginDidChange() {
// Default is no-op.
}
-void Platform::SignOutV1() { Log("SignOutV1() unimplemented"); }
+void Platform::SignOutV1() {
+ Log(LogLevel::kError, "SignOutV1() unimplemented");
+}
void Platform::AndroidShowWifiSettings() {
- Log("AndroidShowWifiSettings() unimplemented");
+ Log(LogLevel::kError, "AndroidShowWifiSettings() unimplemented");
}
void Platform::SetHardwareCursorVisible(bool visible) {
@@ -1044,40 +1056,40 @@ void Platform::SetHardwareCursorVisible(bool visible) {
auto Platform::QuitApp() -> void { exit(g_app->return_value); }
auto Platform::OpenFileExternally(const std::string& path) -> void {
- Log("OpenFileExternally() unimplemented");
+ Log(LogLevel::kError, "OpenFileExternally() unimplemented");
}
auto Platform::OpenDirExternally(const std::string& path) -> void {
- Log("OpenDirExternally() unimplemented");
+ Log(LogLevel::kError, "OpenDirExternally() unimplemented");
}
auto Platform::MacMusicAppInit() -> void {
- Log("MacMusicAppInit() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppInit() unimplemented");
}
auto Platform::MacMusicAppGetVolume() -> int {
- Log("MacMusicAppGetVolume() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppGetVolume() unimplemented");
return 0;
}
auto Platform::MacMusicAppSetVolume(int volume) -> void {
- Log("MacMusicAppSetVolume() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppSetVolume() unimplemented");
}
auto Platform::MacMusicAppGetLibrarySource() -> void {
- Log("MacMusicAppGetLibrarySource() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppGetLibrarySource() unimplemented");
}
auto Platform::MacMusicAppStop() -> void {
- Log("MacMusicAppStop() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppStop() unimplemented");
}
auto Platform::MacMusicAppPlayPlaylist(const std::string& playlist) -> bool {
- Log("MacMusicAppPlayPlaylist() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppPlayPlaylist() unimplemented");
return false;
}
auto Platform::MacMusicAppGetPlaylists() -> std::list {
- Log("MacMusicAppGetPlaylists() unimplemented");
+ Log(LogLevel::kError, "MacMusicAppGetPlaylists() unimplemented");
return {};
}
@@ -1181,8 +1193,8 @@ auto Platform::SetSocketNonBlocking(int sd) -> bool {
#else
int result = fcntl(sd, F_SETFL, O_NONBLOCK);
if (result != 0) {
- Log("Error setting non-blocking socket: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Error setting non-blocking socket: "
+ + g_platform->GetSocketErrorString());
return false;
}
return true;
@@ -1280,7 +1292,7 @@ static void HandleSIGINT(int s) {
if (g_logic) {
g_logic->PushInterruptSignalCall();
} else {
- Log("SigInt handler called before g_logic exists.");
+ Log(LogLevel::kError, "SigInt handler called before g_logic exists.");
}
}
#endif
diff --git a/src/ballistica/platform/platform.h b/src/ballistica/platform/platform.h
index e1ced644..77932c2b 100644
--- a/src/ballistica/platform/platform.h
+++ b/src/ballistica/platform/platform.h
@@ -126,10 +126,10 @@ class Platform {
#pragma mark PRINTING/LOGGING --------------------------------------------------
- // Send a message to the default platform handler.
- // IMPORTANT: No Object::Refs should be created or destroyed within this call,
- // or deadlock can occur.
- virtual auto HandleLog(const std::string& msg) -> void;
+ /// Display a message to any default log for the platform (android log, etc.)
+ /// Note that this can be called from any thread.
+ virtual auto DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) -> void;
#pragma mark ENVIRONMENT -------------------------------------------------------
@@ -427,11 +427,10 @@ class Platform {
-> void;
/// Print a log message to be included in crash logs or other debug
- /// mechanisms. Standard log messages (at least with to_server=true) get
- /// send here as well. It can be useful to call this directly to report
- /// extra details that may help in debugging, as these calls are not
- /// considered 'noteworthy' or presented to the user as standard Log()
- /// calls are.
+ /// mechanisms (example: Crashlytics). V1-cloud-log messages get forwarded
+ /// to here as well. It can be useful to call this directly to report extra
+ /// details that may help in debugging, as these calls are not considered
+ /// 'noteworthy' or presented to the user as standard Log() calls are.
virtual auto HandleDebugLog(const std::string& msg) -> void;
static auto DebugLog(const std::string& msg) -> void {
diff --git a/src/ballistica/platform/sdl/sdl_app.cc b/src/ballistica/platform/sdl/sdl_app.cc
index 24801293..3f28f087 100644
--- a/src/ballistica/platform/sdl/sdl_app.cc
+++ b/src/ballistica/platform/sdl/sdl_app.cc
@@ -43,8 +43,8 @@ void SDLApp::HandleSDLEvent(const SDL_Event& event) {
g_input->PushJoystickEvent(event, js);
}
} else {
- Log("Error: Unable to get SDL Joystick for event type "
- + std::to_string(event.type));
+ Log(LogLevel::kError, "Unable to get SDL Joystick for event type "
+ + std::to_string(event.type));
}
break;
}
@@ -245,8 +245,8 @@ auto FilterSDLEvent(const SDL_Event* event) -> int {
return true; // sdl should keep this.
}
} catch (const std::exception& e) {
- BA_LOG_ONCE(std::string("Exception in inline SDL-Event handling: ")
- + e.what());
+ BA_LOG_ONCE(LogLevel::kError,
+ std::string("Error in inline SDL-Event handling: ") + e.what());
throw;
}
}
@@ -369,7 +369,7 @@ void SDLApp::DoSwap() {
if (g_buildconfig.debug_build()) {
millisecs_t diff = GetRealTime() - swap_start_time_;
if (diff > 5) {
- Log("WARNING: Swap handling delay of " + std::to_string(diff));
+ Log(LogLevel::kWarning, "Swap handling delay of " + std::to_string(diff));
}
}
@@ -514,14 +514,15 @@ void SDLApp::SDLJoystickConnected(int device_index) {
// We add all existing inputs when bootstrapping is complete; we should
// never be getting these before that happens.
if (g_input == nullptr || g_app_flavor == nullptr || !IsBootstrapped()) {
- Log("Unexpected SDLJoystickConnected early in boot sequence.");
+ Log(LogLevel::kError,
+ "Unexpected SDLJoystickConnected early in boot sequence.");
return;
}
// Create the joystick here in the main thread and then pass it over to the
- // game thread to be added to the game.
+ // logic thread to be added to the game.
if (g_buildconfig.ostype_ios_tvos()) {
- BA_LOG_ONCE("WTF GOT SDL-JOY-CONNECTED ON IOS");
+ BA_LOG_ONCE(LogLevel::kError, "WTF GOT SDL-JOY-CONNECTED ON IOS");
} else {
auto* j = Object::NewDeferred(device_index);
if (g_buildconfig.sdl2_build() && g_buildconfig.enable_sdl_joysticks()) {
@@ -566,9 +567,9 @@ void SDLApp::RemoveSDLInputDevice(int index) {
if (static_cast_check_fit(sdl_joysticks_.size()) > index) {
sdl_joysticks_[index] = nullptr;
} else {
- Log("Error: Invalid index on RemoveSDLInputDevice: size is "
- + std::to_string(sdl_joysticks_.size()) + "; index is "
- + std::to_string(index));
+ Log(LogLevel::kError, "Invalid index on RemoveSDLInputDevice: size is "
+ + std::to_string(sdl_joysticks_.size())
+ + "; index is " + std::to_string(index));
}
g_input->PushRemoveInputDeviceCall(j, true);
}
diff --git a/src/ballistica/platform/stdio_console.cc b/src/ballistica/platform/stdio_console.cc
index 9e7036aa..0ef6f190 100644
--- a/src/ballistica/platform/stdio_console.cc
+++ b/src/ballistica/platform/stdio_console.cc
@@ -77,8 +77,8 @@ auto StdioConsole::OnAppStart() -> void {
}
}
} else {
- Log("StdioConsole got non-eof error reading stdin: "
- + std::to_string(ferror(stdin)));
+ Log(LogLevel::kError, "StdioConsole got non-eof error reading stdin: "
+ + std::to_string(ferror(stdin)));
}
break;
}
diff --git a/src/ballistica/platform/windows/platform_windows.cc b/src/ballistica/platform/windows/platform_windows.cc
index 9269f3ad..81376aa1 100644
--- a/src/ballistica/platform/windows/platform_windows.cc
+++ b/src/ballistica/platform/windows/platform_windows.cc
@@ -111,7 +111,7 @@ BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
if (g_logic) {
g_logic->PushInterruptSignalCall();
} else {
- Log("SigInt handler called before g_logic exists.");
+ Log(LogLevel::kError, "SigInt handler called before g_logic exists.");
}
return TRUE;
@@ -123,7 +123,7 @@ BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
void PlatformWindows::SetupInterruptHandling() {
// Set up Ctrl-C handling.
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
- Log("Error on SetConsoleCtrlHandler()");
+ Log(LogLevel::kError, "Error on SetConsoleCtrlHandler()");
}
}
@@ -719,10 +719,11 @@ std::string PlatformWindows::DoGetDeviceName() {
bool PlatformWindows::DoHasTouchScreen() { return false; }
-void PlatformWindows::HandleLog(const std::string& msg) {
+void PlatformWindows::DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) {
// if (have_stdin_stdout_) {
// // On headless builds we use default handler (simple stdout).
- // return Platform::HandleLog(msg);
+ // return Platform::DisplayLog(msg);
// }
// Also spit this out as a debug-string for when running from msvc.
@@ -822,7 +823,8 @@ void PlatformWindows::DoOpenURL(const std::string& url) {
// This should return > 32 on success.
if (r <= 32) {
- Log("Error " + std::to_string(r) + " opening URL '" + url + "'");
+ Log(LogLevel::kError,
+ "Error " + std::to_string(r) + " opening URL '" + url + "'");
}
}
@@ -831,8 +833,8 @@ void PlatformWindows::OpenFileExternally(const std::string& path) {
ShellExecute(nullptr, _T("open"), _T("notepad.exe"),
utf8_decode(path).c_str(), nullptr, SW_SHOWNORMAL));
if (r <= 32) {
- Log("Error " + std::to_string(r) + " on open_file_externally for '" + path
- + "'");
+ Log(LogLevel::kError, "Error " + std::to_string(r)
+ + " on open_file_externally for '" + path + "'");
}
}
@@ -841,8 +843,8 @@ void PlatformWindows::OpenDirExternally(const std::string& path) {
ShellExecute(nullptr, _T("open"), _T("explorer.exe"),
utf8_decode(path).c_str(), nullptr, SW_SHOWNORMAL));
if (r <= 32) {
- Log("Error " + std::to_string(r) + " on open_dir_externally for '" + path
- + "'");
+ Log(LogLevel::kError, "Error " + std::to_string(r)
+ + " on open_dir_externally for '" + path + "'");
}
}
@@ -873,15 +875,15 @@ std::vector PlatformWindows::GetBroadcastAddrs() {
pIPAddrTable = static_cast(MALLOC(dwSize));
}
if (pIPAddrTable == nullptr) {
- Log("Error: Memory allocation failed for GetIpAddrTable\n");
+ Log(LogLevel::kError, "Memory allocation failed for GetIpAddrTable\n");
err = true;
}
if (!err) {
// Make a second call to GetIpAddrTable to get the actual data we want
if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) {
- Log("Error: GetIpAddrTable failed with error "
- + std::to_string(dwRetVal));
+ Log(LogLevel::kError,
+ "GetIpAddrTable failed with error " + std::to_string(dwRetVal));
err = true;
}
}
@@ -916,8 +918,8 @@ bool PlatformWindows::SetSocketNonBlocking(int sd) {
unsigned long dataval = 1; // NOLINT (func signature wants long)
int result = ioctlsocket(sd, FIONBIO, &dataval);
if (result != 0) {
- Log("Error setting non-blocking socket: "
- + g_platform->GetSocketErrorString());
+ Log(LogLevel::kError, "Error setting non-blocking socket: "
+ + g_platform->GetSocketErrorString());
return false;
}
return true;
diff --git a/src/ballistica/platform/windows/platform_windows.h b/src/ballistica/platform/windows/platform_windows.h
index ba7566a5..7857aca8 100644
--- a/src/ballistica/platform/windows/platform_windows.h
+++ b/src/ballistica/platform/windows/platform_windows.h
@@ -32,7 +32,8 @@ class PlatformWindows : public Platform {
auto GetLocale() -> std::string override;
auto DoGetDeviceName() -> std::string override;
auto DoHasTouchScreen() -> bool override;
- void HandleLog(const std::string& msg) override;
+ void DisplayLog(const std::string& name, LogLevel level,
+ const std::string& msg) override;
void SetupDataDirectory() override;
void SetEnv(const std::string& name, const std::string& value) override;
auto GetIsStdinATerminal() -> bool override;
diff --git a/src/ballistica/python/class/python_class_activity_data.cc b/src/ballistica/python/class/python_class_activity_data.cc
index b08927f9..14bd3ffc 100644
--- a/src/ballistica/python/class/python_class_activity_data.cc
+++ b/src/ballistica/python/class/python_class_activity_data.cc
@@ -69,7 +69,7 @@ auto PythonClassActivityData::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
self->host_activity_ = new Object::WeakRef();
@@ -81,7 +81,7 @@ auto PythonClassActivityData::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassActivityData::tp_dealloc(PythonClassActivityData* self) {
BA_PYTHON_TRY;
- // These have to be destructed in the game thread; send them along to
+ // These have to be destructed in the logic thread; send them along to
// it if need be; otherwise do it immediately.
if (!InLogicThread()) {
Object::WeakRef* h = self->host_activity_;
diff --git a/src/ballistica/python/class/python_class_collide_model.cc b/src/ballistica/python/class/python_class_collide_model.cc
index 6616aadc..086c3b35 100644
--- a/src/ballistica/python/class/python_class_collide_model.cc
+++ b/src/ballistica/python/class/python_class_collide_model.cc
@@ -71,7 +71,7 @@ auto PythonClassCollideModel::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
@@ -101,7 +101,7 @@ void PythonClassCollideModel::Delete(Object::Ref* ref) {
void PythonClassCollideModel::tp_dealloc(PythonClassCollideModel* self) {
BA_PYTHON_TRY;
- // these have to be deleted in the game thread - send the ptr along if need
+ // these have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately
if (!InLogicThread()) {
Object::Ref* c = self->collide_model_;
diff --git a/src/ballistica/python/class/python_class_context.cc b/src/ballistica/python/class/python_class_context.cc
index 2b4c52ab..21915540 100644
--- a/src/ballistica/python/class/python_class_context.cc
+++ b/src/ballistica/python/class/python_class_context.cc
@@ -136,7 +136,7 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
@@ -147,7 +147,8 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args,
if (source == "ui") {
cs = Context(g_logic->GetUIContextTarget());
} else if (source == "UI") {
- BA_LOG_ONCE("'UI' context-target option is deprecated; please use 'ui'");
+ BA_LOG_ONCE(LogLevel::kError,
+ "'UI' context-target option is deprecated; please use 'ui'");
Python::PrintStackTrace();
cs = Context(g_logic->GetUIContextTarget());
} else if (source == "current") {
@@ -183,7 +184,7 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassContext::tp_dealloc(PythonClassContext* self) {
BA_PYTHON_TRY;
- // Contexts have to be deleted in the game thread;
+ // Contexts have to be deleted in the logic thread;
// ship them to it for deletion if need be; otherwise do it immediately.
if (!InLogicThread()) {
Context* c = self->context_;
diff --git a/src/ballistica/python/class/python_class_context_call.cc b/src/ballistica/python/class/python_class_context_call.cc
index 2aa433dc..81cfd2cb 100644
--- a/src/ballistica/python/class/python_class_context_call.cc
+++ b/src/ballistica/python/class/python_class_context_call.cc
@@ -103,7 +103,7 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
self->context_call_ = new Object::Ref(
@@ -115,7 +115,7 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassContextCall::tp_dealloc(PythonClassContextCall* self) {
BA_PYTHON_TRY;
- // these have to be deleted in the game thread - send the ptr along if need
+ // these have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately
if (!InLogicThread()) {
Object::Ref* c = self->context_call_;
diff --git a/src/ballistica/python/class/python_class_data.cc b/src/ballistica/python/class/python_class_data.cc
index 4f6584dc..eaebd5f9 100644
--- a/src/ballistica/python/class/python_class_data.cc
+++ b/src/ballistica/python/class/python_class_data.cc
@@ -68,7 +68,7 @@ auto PythonClassData::tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
@@ -98,7 +98,7 @@ void PythonClassData::Delete(Object::Ref* ref) {
void PythonClassData::tp_dealloc(PythonClassData* self) {
BA_PYTHON_TRY;
- // these have to be deleted in the game thread - send the ptr along if need
+ // these have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately
if (!InLogicThread()) {
Object::Ref* s = self->data_;
diff --git a/src/ballistica/python/class/python_class_input_device.cc b/src/ballistica/python/class/python_class_input_device.cc
index d9b6ecd6..8f262fe3 100644
--- a/src/ballistica/python/class/python_class_input_device.cc
+++ b/src/ballistica/python/class/python_class_input_device.cc
@@ -126,7 +126,7 @@ auto PythonClassInputDevice::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
self->input_device_ = new Object::WeakRef();
@@ -137,7 +137,7 @@ auto PythonClassInputDevice::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassInputDevice::tp_dealloc(PythonClassInputDevice* self) {
BA_PYTHON_TRY;
- // These have to be destructed in the game thread - send them along to it if
+ // These have to be destructed in the logic thread - send them along to it if
// need be.
// FIXME: Technically the main thread has a pointer to a dead PyObject
// until the delete goes through; could that ever be a problem?
@@ -391,7 +391,8 @@ auto PythonClassInputDevice::GetButtonName(PythonClassInputDevice* self,
PythonRef results =
g_python->obj(Python::ObjID::kLstrFromJsonCall).Call(args2);
if (!results.exists()) {
- Log("Error creating Lstr from raw button name: '" + bname + "'");
+ Log(LogLevel::kError,
+ "Error creating Lstr from raw button name: '" + bname + "'");
PythonRef args3(Py_BuildValue("(s)", "?"), PythonRef::kSteal);
results = g_python->obj(Python::ObjID::kLstrFromJsonCall).Call(args3);
}
diff --git a/src/ballistica/python/class/python_class_material.cc b/src/ballistica/python/class/python_class_material.cc
index c1a049ad..a1c6cfe1 100644
--- a/src/ballistica/python/class/python_class_material.cc
+++ b/src/ballistica/python/class/python_class_material.cc
@@ -92,7 +92,7 @@ auto PythonClassMaterial::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
PyObject* name_obj = Py_None;
@@ -144,7 +144,7 @@ void PythonClassMaterial::Delete(Object::Ref* m) {
void PythonClassMaterial::tp_dealloc(PythonClassMaterial* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread - push a call if
+ // These have to be deleted in the logic thread - push a call if
// need be.. otherwise do it immediately.
if (!InLogicThread()) {
Object::Ref* ptr = self->material_;
diff --git a/src/ballistica/python/class/python_class_model.cc b/src/ballistica/python/class/python_class_model.cc
index 7e0d637d..6da13d4e 100644
--- a/src/ballistica/python/class/python_class_model.cc
+++ b/src/ballistica/python/class/python_class_model.cc
@@ -69,7 +69,7 @@ auto PythonClassModel::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
if (!s_create_empty_) {
@@ -99,7 +99,7 @@ void PythonClassModel::Delete(Object::Ref* ref) {
void PythonClassModel::tp_dealloc(PythonClassModel* self) {
BA_PYTHON_TRY;
- // these have to be deleted in the game thread - send the ptr along if need
+ // these have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately
if (!InLogicThread()) {
Object::Ref* m = self->model_;
diff --git a/src/ballistica/python/class/python_class_node.cc b/src/ballistica/python/class/python_class_node.cc
index ef4b67ed..e8a31a32 100644
--- a/src/ballistica/python/class/python_class_node.cc
+++ b/src/ballistica/python/class/python_class_node.cc
@@ -87,7 +87,7 @@ auto PythonClassNode::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
// Clion incorrectly things s_create_empty will always be false.
@@ -110,7 +110,7 @@ auto PythonClassNode::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassNode::tp_dealloc(PythonClassNode* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread; send the ptr along if need
+ // These have to be deleted in the logic thread; send the ptr along if need
// be; otherwise do it immediately.
if (!InLogicThread()) {
Object::WeakRef* n = self->node_;
diff --git a/src/ballistica/python/class/python_class_session_data.cc b/src/ballistica/python/class/python_class_session_data.cc
index db534145..1dc4c396 100644
--- a/src/ballistica/python/class/python_class_session_data.cc
+++ b/src/ballistica/python/class/python_class_session_data.cc
@@ -66,7 +66,7 @@ auto PythonClassSessionData::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
self->session_ = new Object::WeakRef();
@@ -77,7 +77,7 @@ auto PythonClassSessionData::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassSessionData::tp_dealloc(PythonClassSessionData* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread;
+ // These have to be deleted in the logic thread;
// ...send the ptr along if need be.
// FIXME: technically the main thread has a pointer to a dead PyObject
// until the delete goes through; could that ever be a problem?
diff --git a/src/ballistica/python/class/python_class_session_player.cc b/src/ballistica/python/class/python_class_session_player.cc
index 9e92af8f..66f6f36d 100644
--- a/src/ballistica/python/class/python_class_session_player.cc
+++ b/src/ballistica/python/class/python_class_session_player.cc
@@ -157,7 +157,7 @@ auto PythonClassSessionPlayer::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
@@ -184,7 +184,7 @@ auto PythonClassSessionPlayer::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassSessionPlayer::tp_dealloc(PythonClassSessionPlayer* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread - send the ptr along if need
+ // These have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately.
if (!InLogicThread()) {
Object::WeakRef* p = self->player_;
@@ -258,8 +258,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self,
throw Exception(PyExcType::kSessionPlayerNotFound);
}
if (!p->has_py_data()) {
- BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s)
- + "' without data set.");
+ BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '"
+ + std::string(s)
+ + "' without data set.");
}
PyObject* obj = p->GetPyCharacter();
Py_INCREF(obj);
@@ -270,8 +271,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self,
throw Exception(PyExcType::kSessionPlayerNotFound);
}
if (!p->has_py_data()) {
- BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s)
- + "' without data set.");
+ BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '"
+ + std::string(s)
+ + "' without data set.");
}
PyObject* obj = p->GetPyColor();
Py_INCREF(obj);
@@ -282,8 +284,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self,
throw Exception(PyExcType::kSessionPlayerNotFound);
}
if (!p->has_py_data()) {
- BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s)
- + "' without data set.");
+ BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '"
+ + std::string(s)
+ + "' without data set.");
}
PyObject* obj = p->GetPyHighlight();
Py_INCREF(obj);
@@ -294,8 +297,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self,
throw Exception(PyExcType::kSessionPlayerNotFound);
}
if (!p->has_py_data()) {
- BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s)
- + "' without data set.");
+ BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '"
+ + std::string(s)
+ + "' without data set.");
}
PyObject* obj = p->GetPyActivityPlayer();
Py_INCREF(obj);
diff --git a/src/ballistica/python/class/python_class_sound.cc b/src/ballistica/python/class/python_class_sound.cc
index 59491749..6a76d646 100644
--- a/src/ballistica/python/class/python_class_sound.cc
+++ b/src/ballistica/python/class/python_class_sound.cc
@@ -68,7 +68,7 @@ auto PythonClassSound::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
if (!s_create_empty_) {
@@ -98,7 +98,7 @@ void PythonClassSound::Delete(Object::Ref* ref) {
void PythonClassSound::tp_dealloc(PythonClassSound* self) {
BA_PYTHON_TRY;
- // these have to be deleted in the game thread - send the ptr along if need
+ // these have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately
if (!InLogicThread()) {
Object::Ref* s = self->sound_;
diff --git a/src/ballistica/python/class/python_class_texture.cc b/src/ballistica/python/class/python_class_texture.cc
index 33d13cd4..adb4b4b5 100644
--- a/src/ballistica/python/class/python_class_texture.cc
+++ b/src/ballistica/python/class/python_class_texture.cc
@@ -63,7 +63,7 @@ auto PythonClassTexture::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
if (!s_create_empty_) {
@@ -91,7 +91,7 @@ void PythonClassTexture::Delete(Object::Ref* ref) {
void PythonClassTexture::tp_dealloc(PythonClassTexture* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread - send the ptr along if need
+ // These have to be deleted in the logic thread - send the ptr along if need
// be; otherwise do it immediately.
if (!InLogicThread()) {
Object::Ref* t = self->texture_;
diff --git a/src/ballistica/python/class/python_class_timer.cc b/src/ballistica/python/class/python_class_timer.cc
index ac553819..1895f793 100644
--- a/src/ballistica/python/class/python_class_timer.cc
+++ b/src/ballistica/python/class/python_class_timer.cc
@@ -76,7 +76,7 @@ auto PythonClassTimer::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
@@ -161,7 +161,7 @@ void PythonClassTimer::DoDelete(bool have_timer, TimeType time_type,
void PythonClassTimer::tp_dealloc(PythonClassTimer* self) {
BA_PYTHON_TRY;
- // These have to be deleted in the game thread.
+ // These have to be deleted in the logic thread.
if (!InLogicThread()) {
auto a0 = self->have_timer_;
auto a1 = self->time_type_;
diff --git a/src/ballistica/python/class/python_class_widget.cc b/src/ballistica/python/class/python_class_widget.cc
index 8ebf2b39..684fa4c1 100644
--- a/src/ballistica/python/class/python_class_widget.cc
+++ b/src/ballistica/python/class/python_class_widget.cc
@@ -78,7 +78,7 @@ auto PythonClassWidget::tp_new(PyTypeObject* type, PyObject* args,
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
- + " objects must only be created in the game thread (current is ("
+ + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}
self->widget_ = new Object::WeakRef();
@@ -89,7 +89,7 @@ auto PythonClassWidget::tp_new(PyTypeObject* type, PyObject* args,
void PythonClassWidget::tp_dealloc(PythonClassWidget* self) {
BA_PYTHON_TRY;
- // these have to be destructed in the game thread - send them along to it if
+ // these have to be destructed in the logic thread - send them along to it if
// need be
if (!InLogicThread()) {
Object::WeakRef* w = self->widget_;
@@ -222,7 +222,7 @@ auto PythonClassWidget::Delete(PythonClassWidget* self, PyObject* args,
if (p) {
p->DeleteWidget(w);
} else {
- Log("Error: Can't delete widget: no parent.");
+ Log(LogLevel::kError, "Can't delete widget: no parent.");
}
}
Py_RETURN_NONE;
diff --git a/src/ballistica/python/methods/python_methods_app.cc b/src/ballistica/python/methods/python_methods_app.cc
index d8a7fc6f..44f2e45b 100644
--- a/src/ballistica/python/methods/python_methods_app.cc
+++ b/src/ballistica/python/methods/python_methods_app.cc
@@ -197,7 +197,7 @@ auto PyGetForegroundHostSession(PyObject* self, PyObject* args,
return nullptr;
}
- // Note: we return None if not in the game thread.
+ // Note: we return None if not in the logic thread.
HostSession* s = InLogicThread()
? g_logic->GetForegroundContext().GetHostSession()
: nullptr;
@@ -259,7 +259,7 @@ auto PyGetActivity(PyObject* self, PyObject* args, PyObject* keywds)
return nullptr;
}
- // Fail gracefully if called from outside the game thread.
+ // Fail gracefully if called from outside the logic thread.
if (!InLogicThread()) {
Py_RETURN_NONE;
}
@@ -292,7 +292,7 @@ auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
// The from-other-thread case is basically a different call.
if (from_other_thread) {
- // Warn the user not to use this from the game thread since it doesnt
+ // Warn the user not to use this from the logic thread since it doesnt
// save/restore context.
if (!suppress_warning && InLogicThread()) {
g_python->IssueCallInLogicThreadWarning(call_obj);
@@ -301,7 +301,7 @@ auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
// This gets called from other python threads so we can't construct
// Objects and things here or we'll trip our thread-checks. Instead we
// just increment the python object's refcount and pass it along raw;
- // the game thread decrements it on the other end.
+ // the logic thread decrements it on the other end.
Py_INCREF(call_obj);
g_logic->PushPythonRawCallable(call_obj);
} else {
@@ -461,7 +461,7 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds)
return nullptr;
}
if (log) {
- Log(message);
+ Log(LogLevel::kInfo, message);
}
// Transient messages get sent to clients as high-level messages instead of
@@ -552,7 +552,7 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds)
tint_color.x, tint_color.y, tint_color.z, tint2_color.x,
tint2_color.y, tint2_color.z);
} else {
- Log("Error: unhandled screenmessage output_stream case");
+ Log(LogLevel::kError, "Unhandled screenmessage output_stream case.");
}
}
@@ -581,7 +581,7 @@ auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
if (g_buildconfig.ostype_ios_tvos()) {
// This should never be called on iOS
- Log("Error: Quit called.");
+ Log(LogLevel::kError, "Quit called.");
}
bool handled = false;
@@ -628,7 +628,7 @@ auto PyBless(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
auto PyApplyConfig(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY;
- // Hmm; python runs in the game thread; technically we could just run
+ // Hmm; python runs in the logic thread; technically we could just run
// ApplyConfig() immediately (though pushing is probably safer).
g_logic->PushApplyConfigCall();
Py_RETURN_NONE;
@@ -807,42 +807,53 @@ auto PySetStressTesting(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_CATCH;
}
-auto PyPrintStdout(PyObject* self, PyObject* args) -> PyObject* {
+auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds)
+ -> PyObject* {
BA_PYTHON_TRY;
- const char* s;
- if (!PyArg_ParseTuple(args, "s", &s)) {
+ static const char* kwlist[] = {"name", "level", "message", nullptr};
+ const char* name;
+ const char* levelstr;
+ const char* message;
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss",
+ const_cast(kwlist), &name, &levelstr,
+ &message)) {
return nullptr;
}
- Logging::PrintStdout(s);
+
+ // Calc LogLevel enum val from their string val.
+ LogLevel level;
+ if (levelstr == std::string("DEBUG")) {
+ level = LogLevel::kDebug;
+ } else if (levelstr == std::string("INFO")) {
+ level = LogLevel::kInfo;
+ } else if (levelstr == std::string("WARNING")) {
+ level = LogLevel::kWarning;
+ } else if (levelstr == std::string("ERROR")) {
+ level = LogLevel::kError;
+ } else if (levelstr == std::string("CRITICAL")) {
+ level = LogLevel::kCritical;
+ } else {
+ // Assume we should avoid Log() calls here since it could infinite loop.
+ fprintf(stderr, "Invalid log level to display_log(): %s\n", levelstr);
+ level = LogLevel::kInfo;
+ }
+ Logging::DisplayLog(name, level, message);
+
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
-auto PyPrintStderr(PyObject* self, PyObject* args) -> PyObject* {
+auto PyV1CloudLog(PyObject* self, PyObject* args, PyObject* keywds)
+ -> PyObject* {
BA_PYTHON_TRY;
- const char* s;
- if (!PyArg_ParseTuple(args, "s", &s)) {
+ const char* message;
+ static const char* kwlist[] = {"message", nullptr};
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "s",
+ const_cast(kwlist), &message)) {
return nullptr;
}
- Logging::PrintStderr(s);
- Py_RETURN_NONE;
- BA_PYTHON_CATCH;
-}
+ Logging::V1CloudLog(message);
-auto PyLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
- BA_PYTHON_TRY;
- static const char* kwlist[] = {"message", "to_stdout", "to_server", nullptr};
- int to_server = 1;
- int to_stdout = 1;
- std::string message;
- PyObject* message_obj;
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|pp",
- const_cast(kwlist), &message_obj,
- &to_stdout, &to_server)) {
- return nullptr;
- }
- message = Python::GetPyString(message_obj);
- Log(message, static_cast(to_stdout), static_cast(to_server));
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
@@ -900,39 +911,22 @@ auto PythonMethodsApp::GetMethods() -> std::vector {
"(for helping with the transition from milliseconds-based time calls\n"
"to seconds-based ones)"},
- {"log", (PyCFunction)PyLog, METH_VARARGS | METH_KEYWORDS,
- "log(message: str, to_stdout: bool = True,\n"
- " to_server: bool = True) -> None\n"
- "\n"
- "Category: **General Utility Functions**\n"
- "\n"
- "Log a message. This goes to the default logging mechanism depending\n"
- "on the platform (stdout on mac, android log on android, etc).\n"
- "\n"
- "Log messages also go to the in-game console unless 'to_console'\n"
- "is False. They are also sent to the master-server for use in "
- "analyzing\n"
- "issues unless to_server is False.\n"
- "\n"
- "Python's standard print() is wired to call this (with default "
- "values)\n"
- "so in most cases you can just use that."},
-
- {"print_stdout", PyPrintStdout, METH_VARARGS,
- "print_stdout(message: str) -> None\n"
- "\n"
- "(internal)"
- "\n"
- "Print to system stdout.\n"
- "Also forwards to the internal console, etc."},
-
- {"print_stderr", PyPrintStderr, METH_VARARGS,
- "print_stderr(message: str) -> None\n"
+ {"display_log", (PyCFunction)PyDisplayLog, METH_VARARGS | METH_KEYWORDS,
+ "display_log(name: str, level: str, message: str) -> None\n"
"\n"
"(internal)\n"
"\n"
- "Print to system stderr.\n"
- "Also forwards to the internal console, etc."},
+ "Sends a log message to the in-game console and any per-platform\n"
+ "log destinations (Android log, etc.). This generally is not called\n"
+ "directly and should instead be fed Python logging output."},
+
+ {"v1_cloud_log", (PyCFunction)PyV1CloudLog,
+ METH_VARARGS | METH_KEYWORDS,
+ "v1_cloud_log(message: str) -> None\n"
+ "\n"
+ "(internal)\n"
+ "\n"
+ "Push messages to the old v1 cloud log."},
{"set_stress_testing", PySetStressTesting, METH_VARARGS,
"set_stress_testing(testing: bool, player_count: int) -> None\n"
@@ -1120,13 +1114,13 @@ auto PythonMethodsApp::GetMethods() -> std::vector {
"This can be handy for calls that are disallowed from within other\n"
"callbacks, etc.\n"
"\n"
- "This call expects to be used in the game thread, and will "
+ "This call expects to be used in the logic thread, and will "
"automatically\n"
"save and restore the ba.Context to behave seamlessly.\n"
"\n"
- "If you want to push a call from outside of the game thread,\n"
+ "If you want to push a call from outside of the logic thread,\n"
"however, you can pass 'from_other_thread' as True. In this case\n"
- "the call will always run in the UI context on the game thread."},
+ "the call will always run in the UI context on the logic thread."},
{"getactivity", (PyCFunction)PyGetActivity,
METH_VARARGS | METH_KEYWORDS,
diff --git a/src/ballistica/python/methods/python_methods_gameplay.cc b/src/ballistica/python/methods/python_methods_gameplay.cc
index 4ba23115..fdc0a550 100644
--- a/src/ballistica/python/methods/python_methods_gameplay.cc
+++ b/src/ballistica/python/methods/python_methods_gameplay.cc
@@ -57,7 +57,7 @@ auto PyPrintNodes(PyObject* self, PyObject* args) -> PyObject* {
snprintf(buffer, sizeof(buffer), "#%d: type: %-14s desc: %s", count,
i->type()->name().c_str(), i->label().c_str());
s += buffer;
- Log(buffer);
+ Log(LogLevel::kInfo, buffer);
count++;
}
Py_RETURN_NONE;
@@ -381,7 +381,7 @@ auto PyGetForegroundHostActivity(PyObject* self, PyObject* args,
return nullptr;
}
- // Note: we return None if not in the game thread.
+ // Note: we return None if not in the logic thread.
HostActivity* h = InLogicThread()
? g_logic->GetForegroundContext().GetHostActivity()
: nullptr;
diff --git a/src/ballistica/python/methods/python_methods_networking.cc b/src/ballistica/python/methods/python_methods_networking.cc
index 27eb5a2d..6c5a4d6e 100644
--- a/src/ballistica/python/methods/python_methods_networking.cc
+++ b/src/ballistica/python/methods/python_methods_networking.cc
@@ -324,8 +324,8 @@ auto PySetMasterServerSource(PyObject* self, PyObject* args) -> PyObject* {
int source;
if (!PyArg_ParseTuple(args, "i", &source)) return nullptr;
if (source != 0 && source != 1) {
- BA_LOG_ONCE("Error: Invalid server source: " + std::to_string(source)
- + ".");
+ BA_LOG_ONCE(LogLevel::kError,
+ "Invalid server source: " + std::to_string(source) + ".");
source = 1;
}
g_app->master_server_source = source;
diff --git a/src/ballistica/python/methods/python_methods_system.cc b/src/ballistica/python/methods/python_methods_system.cc
index 7b593c8f..1cb80409 100644
--- a/src/ballistica/python/methods/python_methods_system.cc
+++ b/src/ballistica/python/methods/python_methods_system.cc
@@ -79,7 +79,7 @@ auto PySetUpSigInt(PyObject* self) -> PyObject* {
if (g_app_flavor) {
g_platform->SetupInterruptHandling();
} else {
- Log("SigInt handler called before g_app_flavor exists.");
+ Log(LogLevel::kError, "SigInt handler called before g_app_flavor exists.");
}
Py_RETURN_NONE;
BA_PYTHON_CATCH;
@@ -354,7 +354,7 @@ auto PyPrintContext(PyObject* self, PyObject* args, PyObject* keywds)
const_cast(kwlist))) {
return nullptr;
}
- Python::LogContextAuto();
+ Python::PrintContextAuto();
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
@@ -521,19 +521,20 @@ auto PyGetVolatileDataDirectory(PyObject* self, PyObject* args) -> PyObject* {
auto PyIsLogFull(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY;
- if (g_app->log_full) {
+ if (g_app->v1_cloud_log_full) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
BA_PYTHON_CATCH;
}
-auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
+auto PyGetV1CloudLog(PyObject* self, PyObject* args, PyObject* keywds)
+ -> PyObject* {
BA_PYTHON_TRY;
std::string log_fin;
{
- std::scoped_lock lock(g_app->log_mutex);
- log_fin = g_app->log;
+ std::scoped_lock lock(g_app->v1_cloud_log_mutex);
+ log_fin = g_app->v1_cloud_log;
}
// we want to use something with error handling here since the last
// bit of this string could be truncated utf8 chars..
@@ -545,8 +546,8 @@ auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
auto PyMarkLogSent(PyObject* self, PyObject* args, PyObject* keywds)
-> PyObject* {
BA_PYTHON_TRY;
- // this way we won't try to send it at shutdown time and whatnot
- g_app->put_log = true;
+ // This way we won't try to send it at shutdown time and whatnot
+ g_app->did_put_v1_cloud_log = true;
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
@@ -902,8 +903,9 @@ auto PythonMethodsSystem::GetMethods() -> std::vector {
"\n"
"(internal)"},
- {"getlog", (PyCFunction)PyGetLog, METH_VARARGS | METH_KEYWORDS,
- "getlog() -> str\n"
+ {"get_v1_cloud_log", (PyCFunction)PyGetV1CloudLog,
+ METH_VARARGS | METH_KEYWORDS,
+ "get_v1_cloud_log() -> str\n"
"\n"
"(internal)"},
@@ -912,8 +914,8 @@ auto PythonMethodsSystem::GetMethods() -> std::vector {
"\n"
"(internal)"},
- {"get_log_file_path", PyGetLogFilePath, METH_VARARGS,
- "get_log_file_path() -> str\n"
+ {"get_v1_cloud_log_file_path", PyGetLogFilePath, METH_VARARGS,
+ "get_v1_cloud_log_file_path() -> str\n"
"\n"
"(internal)\n"
"\n"
@@ -1055,7 +1057,7 @@ auto PythonMethodsSystem::GetMethods() -> std::vector {
"\n"
"(internal)\n"
"\n"
- "Returns whether or not the current thread is the game thread."},
+ "Returns whether or not the current thread is the logic thread."},
{"request_permission", (PyCFunction)PyRequestPermission,
METH_VARARGS | METH_KEYWORDS,
diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc
index ae8d285f..3be6a998 100644
--- a/src/ballistica/python/python.cc
+++ b/src/ballistica/python/python.cc
@@ -68,6 +68,46 @@ namespace ballistica {
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#pragma ide diagnostic ignored "RedundantCast"
+auto Python::LoggingCall(LogLevel loglevel, const std::string& msg) -> void {
+ // If we've not yet captured our Python logging calls, stash this call away.
+ // We'll submit all accumulated entries after we bootstrap python.
+ if (!objexists(ObjID::kLoggingCriticalCall)) {
+ std::scoped_lock lock(early_log_lock_);
+ early_logs_.emplace_back(std::make_pair(loglevel, msg));
+ return;
+ }
+
+ // Ok; seems we've got Python calls. Run the right one for our log level.
+ ObjID logcallobj;
+ switch (loglevel) {
+ case LogLevel::kDebug:
+ logcallobj = Python::ObjID::kLoggingDebugCall;
+ break;
+ case LogLevel::kInfo:
+ logcallobj = Python::ObjID::kLoggingInfoCall;
+ break;
+ case LogLevel::kWarning:
+ logcallobj = Python::ObjID::kLoggingWarningCall;
+ break;
+ case LogLevel::kError:
+ logcallobj = Python::ObjID::kLoggingErrorCall;
+ break;
+ case LogLevel::kCritical:
+ logcallobj = Python::ObjID::kLoggingCriticalCall;
+ break;
+ default:
+ logcallobj = Python::ObjID::kLoggingInfoCall;
+ fprintf(stderr, "Unexpected LogLevel %d\n", static_cast(loglevel));
+ break;
+ }
+
+ // Make sure we're good to go from any thread.
+ ScopedInterpreterLock lock;
+
+ PythonRef args(Py_BuildValue("(s)", msg.c_str()), PythonRef::kSteal);
+ obj(logcallobj).Call(args);
+}
+
void Python::SetPythonException(const Exception& exc) {
PyExcType exctype{exc.python_type()};
const char* description{GetShortExceptionDescription(exc)};
@@ -130,7 +170,8 @@ void Python::PrintStackTrace() {
if (g_python->objexists(objid)) {
g_python->obj(objid).Call();
} else {
- Log("Warning: Python::PrintStackTrace() called before bootstrap complete; "
+ Log(LogLevel::kWarning,
+ "Python::PrintStackTrace() called before bootstrap complete; "
"not printing.");
}
}
@@ -1042,6 +1083,16 @@ auto Python::InitBallisticaPython() -> void {
// Import and grab all the Python stuff we use from C++.
#include "ballistica/generated/python_embedded/binding.inc"
+ // If we've got any early log calls, it is now safe to push them along
+ // to Python.
+ assert(objexists(ObjID::kLoggingCriticalCall));
+ {
+ std::scoped_lock lock(early_log_lock_);
+ for (auto&& entry : early_logs_) {
+ LoggingCall(entry.first, "DEFERRED-EARLY-LOG: " + entry.second);
+ }
+ early_logs_.clear();
+ }
g_app_internal->PythonPostInit();
// Alright I guess let's pull ba in to main, since pretty
@@ -1187,14 +1238,15 @@ auto Python::GetResource(const char* key, const char* fallback_resource,
try {
return GetPyString(results.get());
} catch (const std::exception&) {
- Log("GetResource failed for '" + std::string(key) + "'");
+ Log(LogLevel::kError,
+ "GetResource failed for '" + std::string(key) + "'");
// Hmm; I guess let's just return the key to help identify/fix the
// issue?..
return std::string("";
}
} else {
- Log("GetResource failed for '" + std::string(key) + "'");
+ Log(LogLevel::kError, "GetResource failed for '" + std::string(key) + "'");
}
// Hmm; I guess let's just return the key to help identify/fix the issue?..
@@ -1212,11 +1264,13 @@ auto Python::GetTranslation(const char* category, const char* s)
try {
return GetPyString(results.get());
} catch (const std::exception&) {
- Log("GetTranslation failed for '" + std::string(category) + "'");
+ Log(LogLevel::kError,
+ "GetTranslation failed for '" + std::string(category) + "'");
return "";
}
} else {
- Log("GetTranslation failed for category '" + std::string(category) + "'");
+ Log(LogLevel::kError,
+ "GetTranslation failed for category '" + std::string(category) + "'");
}
return "";
}
@@ -1228,7 +1282,7 @@ void Python::RunDeepLink(const std::string& url) {
PythonRef args(Py_BuildValue("(s)", url.c_str()), PythonRef::kSteal);
obj(ObjID::kDeepLinkCall).Call(args);
} else {
- Log("Error on deep-link call");
+ Log(LogLevel::kError, "Error on deep-link call");
}
}
@@ -1253,7 +1307,7 @@ void Python::ShowURL(const std::string& url) {
PythonRef args(Py_BuildValue("(s)", url.c_str()), PythonRef::kSteal);
obj(ObjID::kShowURLWindowCall).Call(args);
} else {
- Log("Error: ShowURLWindowCall nonexistent.");
+ Log(LogLevel::kError, "ShowURLWindowCall nonexistent.");
}
}
@@ -1296,7 +1350,8 @@ auto Python::FilterChatMessage(std::string* message, int client_id) -> bool {
try {
*message = Python::GetPyString(result.get());
} catch (const std::exception& e) {
- Log("Error getting string from chat filter: " + std::string(e.what()));
+ Log(LogLevel::kError,
+ "Error getting string from chat filter: " + std::string(e.what()));
}
return true;
}
@@ -1781,9 +1836,9 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* {
attr_vals.emplace_back(
t->GetAttribute(std::string(PyUnicode_AsUTF8(key))), value);
} catch (const std::exception&) {
- Log("ERROR: Attr not found on initial attr set: '"
- + std::string(PyUnicode_AsUTF8(key)) + "' on " + type + " node '"
- + name + "'");
+ Log(LogLevel::kError, "Attr not found on initial attr set: '"
+ + std::string(PyUnicode_AsUTF8(key)) + "' on "
+ + type + " node '" + name + "'");
}
}
@@ -1793,8 +1848,9 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* {
try {
SetNodeAttr(node, i.first->name().c_str(), i.second);
} catch (const std::exception& e) {
- Log("ERROR: exception in initial attr set for attr '" + i.first->name()
- + "' on " + type + " node '" + name + "':" + e.what());
+ Log(LogLevel::kError, "Exception in initial attr set for attr '"
+ + i.first->name() + "' on " + type + " node '"
+ + name + "':" + e.what());
}
}
}
@@ -1807,10 +1863,12 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* {
if (PythonClassNode::Check(owner_obj)) {
Node* owner_node = GetPyNode(owner_obj, true);
if (owner_node == nullptr) {
- Log("ERROR: empty node-ref passed for 'owner'; pass None if you want "
+ Log(LogLevel::kError,
+ "Empty node-ref passed for 'owner'; pass None if you want "
"no owner.");
} else if (owner_node->scene() != node->scene()) {
- Log("ERROR: owner node is from a different scene; ignoring.");
+ Log(LogLevel::kError,
+ "Owner node is from a different scene; ignoring.");
} else {
owner_node->AddDependentNode(node);
}
@@ -1830,8 +1888,9 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* {
}
node->OnCreate();
} catch (const std::exception& e) {
- Log("ERROR: exception in OnCreate() for node "
- + ballistica::ObjToString(node) + "':" + e.what());
+ Log(LogLevel::kError, "Exception in OnCreate() for node "
+ + ballistica::ObjToString(node)
+ + "':" + e.what());
}
return node;
@@ -2039,10 +2098,11 @@ auto Python::GetNodeAttr(Node* node, const char* attr_name) -> PyObject* {
}
void Python::IssueCallInLogicThreadWarning(PyObject* call_obj) {
- Log("WARNING: ba.pushcall() called from the game thread with "
+ Log(LogLevel::kWarning,
+ "ba.pushcall() called from the logic thread with "
"from_other_thread set to true (call "
- + ObjToString(call_obj) + " at " + GetPythonFileLocation()
- + "). That arg should only be used from other threads.");
+ + ObjToString(call_obj) + " at " + GetPythonFileLocation()
+ + "). That arg should only be used from other threads.");
}
void Python::LaunchStringEdit(TextWidget* w) {
@@ -2253,41 +2313,41 @@ auto Python::GetContextBaseString() -> std::string {
return s;
}
-void Python::LogContextForCallableLabel(const char* label) {
+void Python::PrintContextForCallableLabel(const char* label) {
assert(InLogicThread());
assert(label);
std::string s = std::string(" root call: ") + label;
s += g_python->GetContextBaseString();
- Log(s);
+ PySys_WriteStderr("%s\n", s.c_str());
}
-void Python::LogContextNonLogicThread() {
+void Python::PrintContextNonLogicThread() {
std::string s =
- std::string(" root call: ");
- Log(s);
+ std::string(" root call: ");
+ PySys_WriteStderr("%s\n", s.c_str());
}
-void Python::LogContextEmpty() {
+void Python::PrintContextEmpty() {
assert(InLogicThread());
std::string s = std::string(" root call: ");
s += g_python->GetContextBaseString();
- Log(s);
+ PySys_WriteStderr("%s\n", s.c_str());
}
-void Python::LogContextAuto() {
+void Python::PrintContextAuto() {
// Lets print whatever context info is available.
// FIXME: If we have recursive calls this may not print
// the context we'd expect; we'd need a unified stack.
if (!InLogicThread()) {
- LogContextNonLogicThread();
+ PrintContextNonLogicThread();
} else if (const char* label = ScopedCallLabel::current_label()) {
- LogContextForCallableLabel(label);
+ PrintContextForCallableLabel(label);
} else if (PythonCommand* cmd = PythonCommand::current_command()) {
- cmd->LogContext();
+ cmd->PrintContext();
} else if (PythonContextCall* call = PythonContextCall::current_call()) {
- call->LogContext();
+ call->PrintContext();
} else {
- LogContextEmpty();
+ PrintContextEmpty();
}
}
@@ -2304,8 +2364,8 @@ void Python::AcquireGIL() {
if (debug_timing) {
auto duration{Platform::GetCurrentMilliseconds() - startms};
if (duration > (1000 / 120)) {
- Log("GIL acquire took too long (" + std::to_string(duration) + " ms).",
- true, false);
+ Log(LogLevel::kInfo,
+ "GIL acquire took too long (" + std::to_string(duration) + " ms).");
}
}
}
@@ -2491,7 +2551,8 @@ auto Python::GetRawConfigValue(const char* name, float default_value) -> float {
try {
return GetPyFloat(value);
} catch (const std::exception&) {
- Log("expected a float for config value '" + std::string(name) + "'");
+ Log(LogLevel::kError,
+ "expected a float for config value '" + std::string(name) + "'");
return default_value;
}
}
@@ -2511,7 +2572,8 @@ auto Python::GetRawConfigValue(const char* name,
}
return GetPyFloat(value);
} catch (const std::exception&) {
- Log("expected a float for config value '" + std::string(name) + "'");
+ Log(LogLevel::kError,
+ "expected a float for config value '" + std::string(name) + "'");
return default_value;
}
}
@@ -2526,7 +2588,8 @@ auto Python::GetRawConfigValue(const char* name, int default_value) -> int {
try {
return static_cast_check_fit(GetPyInt64(value));
} catch (const std::exception&) {
- Log("Expected an int value for config value '" + std::string(name) + "'.");
+ Log(LogLevel::kError,
+ "Expected an int value for config value '" + std::string(name) + "'.");
return default_value;
}
}
@@ -2541,7 +2604,8 @@ auto Python::GetRawConfigValue(const char* name, bool default_value) -> bool {
try {
return GetPyBool(value);
} catch (const std::exception&) {
- Log("Expected a bool value for config value '" + std::string(name) + "'.");
+ Log(LogLevel::kError,
+ "Expected a bool value for config value '" + std::string(name) + "'.");
return default_value;
}
}
@@ -2564,7 +2628,7 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) {
if (length >= 200.0) {
static bool warned = false;
if (!warned) {
- Log("Warning: time value "
+ Log(LogLevel::kWarning, "Time value "
+std::to_string(length)+" passed as seconds;"
" did you mean milliseconds?"
" (if so, pass suppress_format_warning=True to stop this warning)");
@@ -2578,7 +2642,7 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) {
if (length < 1.0 && length > 0.0000001) {
static bool warned = false;
if (!warned) {
- Log("Warning: time value "
+ Log(LogLevel::kWarning, "Time value "
+ std::to_string(length) + " passed as milliseconds;"
" did you mean seconds?"
" (if so, pass suppress_format_warning=True to stop this warning)");
@@ -2589,8 +2653,9 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) {
} else {
static bool warned = false;
if (!warned) {
- BA_LOG_ONCE("TimeFormatCheck got timeformat value: '"
- + std::to_string(static_cast(time_format)) + "'");
+ BA_LOG_ONCE(LogLevel::kError,
+ "TimeFormatCheck got timeformat value: '"
+ + std::to_string(static_cast(time_format)) + "'");
warned = true;
}
}
diff --git a/src/ballistica/python/python.h b/src/ballistica/python/python.h
index bf451032..a48e847b 100644
--- a/src/ballistica/python/python.h
+++ b/src/ballistica/python/python.h
@@ -5,6 +5,7 @@
#include
#include