major logging system revamp

This commit is contained in:
Eric 2022-09-14 19:11:34 -07:00
parent 9319287fcd
commit cfe42b39e6
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
153 changed files with 1622 additions and 1460 deletions

View File

@ -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"
}

View File

@ -64,9 +64,11 @@
<w>aiomain</w>
<w>alarmsound</w>
<w>alibaba</w>
<w>allerrors</w>
<w>allpaths</w>
<w>allsettings</w>
<w>allteams</w>
<w>allwarnings</w>
<w>aman</w>
<w>amazonaws</w>
<w>aname</w>
@ -2384,6 +2386,7 @@
<w>startscan</w>
<w>startsplits</w>
<w>starttime</w>
<w>startupmsg</w>
<w>statestr</w>
<w>statictest</w>
<w>statictestfiles</w>
@ -2412,6 +2415,7 @@
<w>stot</w>
<w>strftime</w>
<w>stringified</w>
<w>stringifying</w>
<w>stringprep</w>
<w>stringptr</w>
<w>strippable</w>

View File

@ -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.

View File

@ -1 +1 @@
126683827977798484003262787310231621875
76251027805752156826413428926087661089

View File

@ -1 +1 @@
222094078620857897443553282652634355523
139020022013133168311319486434408589898

View File

@ -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:

View File

@ -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',

View File

@ -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.

View File

@ -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', '<NULL_BYTE>'),
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.

View File

@ -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.')

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -41,7 +41,9 @@
<w>airborn</w>
<w>alext</w>
<w>alibaba</w>
<w>allerrors</w>
<w>allocs</w>
<w>allwarnings</w>
<w>alot</w>
<w>alphaimg</w>
<w>alphapixels</w>
@ -1243,6 +1245,7 @@
<w>startpos</w>
<w>startsplits</w>
<w>starttime</w>
<w>startupmsg</w>
<w>startx</w>
<w>starty</w>
<w>statestr</w>
@ -1262,6 +1265,7 @@
<w>strdup</w>
<w>stringi</w>
<w>stringified</w>
<w>stringifying</w>
<w>strlen</w>
<w>strs</w>
<w>strtof</w>

View File

@ -32,10 +32,10 @@ class App {
std::vector<Thread*> 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{};

View File

@ -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:

View File

@ -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<int>(preload_time),
static_cast_check_fit<int>(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<int>(preload_time),
static_cast_check_fit<int>(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<int>(preload_time),
static_cast_check_fit<int>(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<int>(preload_time),
static_cast_check_fit<int>(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<int>(preload_time),
static_cast_check_fit<int>(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<int>(total_preload_time),
static_cast<int>(total_load_time));
Log(buffer, true, false);
Log(LogLevel::kInfo, buffer);
}
void Assets::MarkAllAssetsForLoad() {
@ -810,8 +810,8 @@ auto Assets::RunPendingLoadList(std::vector<Object::Ref<T>*>* 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<AssetComponentData>* 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;

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -56,8 +56,8 @@ static auto LoadOgg(const char* file_name, std::vector<char>* 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<char>* 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<char>* 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!!!!
}

View File

@ -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)
+ ";");
}
}

View File

@ -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<SoundData>* 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);

View File

@ -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<SoundData>* c) -> void;

View File

@ -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));

View File

@ -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;
}

View File

@ -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);

View File

@ -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<unsigned int>(
Platform::GetCurrentMilliseconds())); // NOLINT
session_id = std::to_string(static_cast<uint32_t>(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 + "'");
}
}

View File

@ -102,7 +102,7 @@ const float kGameStepSeconds =
(static_cast<float>(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.

View File

@ -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 {

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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<max log size reached>\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<max log size reached>\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);
}
}
}

View File

@ -5,23 +5,33 @@
#include <string>
#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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -43,26 +43,54 @@ auto Thread::RunLogicThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunLogicThreadP(void* data) -> void* {
static_cast<Thread*>(data)->ThreadMain();
return nullptr;
}
auto Thread::RunAudioThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunAudioThreadP(void* data) -> void* {
static_cast<Thread*>(data)->ThreadMain();
return nullptr;
}
auto Thread::RunBGDynamicThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunBGDynamicThreadP(void* data) -> void* {
static_cast<Thread*>(data)->ThreadMain();
return nullptr;
}
auto Thread::RunNetworkWriteThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunNetworkWriteThreadP(void* data) -> void* {
static_cast<Thread*>(data)->ThreadMain();
return nullptr;
}
auto Thread::RunStdInputThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunStdInputThreadP(void* data) -> void* {
static_cast<Thread*>(data)->ThreadMain();
return nullptr;
}
auto Thread::RunAssetsThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain();
}
auto Thread::RunAssetsThreadP(void* data) -> void* {
static_cast<Thread*>(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<std::string, int> 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();
}
}

View File

@ -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<ThreadMessage>* messages) -> void;
@ -159,7 +165,7 @@ class Thread {
std::list<std::pair<Runnable*, bool*>> runnables_;
std::list<Runnable*> pause_callbacks_;
std::list<Runnable*> resume_callbacks_;
std::thread* thread_{};
// std::thread* thread_{};
std::condition_variable thread_message_cv_;
std::mutex thread_message_mutex_;
std::list<ThreadMessage> thread_messages_;

View File

@ -424,6 +424,8 @@ enum class PyExcType {
kWidgetNotFound
};
enum class LogLevel { kDebug, kInfo, kWarning, kError, kCritical };
enum class SystemTextureID {
kUIAtlas,
kButtonSquare,

View File

@ -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();

View File

@ -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 {

View File

@ -539,12 +539,12 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot(
auto* ibuf = Object::NewDeferred<MeshIndexBuffer16>(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<MeshBufferVertexSprite>(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<int>(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<MeshIndexBuffer16>(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<MeshBufferVertexSprite>(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<MeshIndexBuffer16>(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<MeshBufferVertexSprite>(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<MeshIndexBuffer16>(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<MeshBufferVertexSmokeFull>(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<MeshIndexBuffer16>(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<MeshBufferVertexSimpleFull>(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);

View File

@ -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();

View File

@ -213,8 +213,9 @@ void MaterialComponent::Restore(const char** buffer, ClientSession* cs) {
action = Object::New<NodeModMaterialAction>();
break;
default:
Log("Error: Invalid material action: '"
+ std::to_string(static_cast<int>(type)) + "'.");
Log(LogLevel::kError, "Invalid material action: '"
+ std::to_string(static_cast<int>(type))
+ "'.");
throw Exception();
}
action->Restore(buffer, cs);

View File

@ -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.");
}
}

View File

@ -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++) {

View File

@ -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.");
}
}
}

View File

@ -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<decltype(testnode)>() + "' debug_full is '"
+ static_type_name<decltype(testnode)>(true) + "'");
Log(LogLevel::kError,
"static_type_name check; name is '"
+ static_type_name<decltype(testnode)>() + "' debug_full is '"
+ static_type_name<decltype(testnode)>(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 {

View File

@ -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) {

View File

@ -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(); }

View File

@ -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

View File

@ -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<uint32_t>(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_;
}

View File

@ -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++;

View File

@ -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();

View File

@ -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;
}
});

View File

@ -32,7 +32,7 @@ class GraphicsServer {
const std::vector<Object::Ref<AssetComponentData>*>& 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;

View File

@ -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

View File

@ -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<MeshData*>::iterator iterator_;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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<unsigned char>(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;
}

View File

@ -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:

View File

@ -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<int>(e->jhat.value)));
BA_LOG_ONCE(LogLevel::kError,
"Invalid hat value: "
+ std::to_string(static_cast<int>(e->jhat.value)));
break;
}
}

View File

@ -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;
}

View File

@ -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<KeyboardInput>(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<int>(scancode) < SDL_SCANCODE_UNKNOWN
|| scancode >= SDL_NUM_SCANCODES) {
BA_LOG_ONCE("GetScancodeName passed invalid scancode "
+ std::to_string(static_cast<int>(scancode)));
BA_LOG_ONCE(LogLevel::kError,
"GetScancodeName passed invalid scancode "
+ std::to_string(static_cast<int>(scancode)));
return "";
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -149,7 +149,8 @@ void Connection::HandleGamePacketCompressed(const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& data) {
}
default:
Log("Connection got unknown packet type: "
+ std::to_string(static_cast<int>(data[0])));
Log(LogLevel::kError, "Connection got unknown packet type: "
+ std::to_string(static_cast<int>(data[0])));
break;
}
}
@ -316,8 +318,9 @@ void Connection::SendReliableMessage(const std::vector<uint8_t>& data) {
void Connection::SendUnreliableMessage(const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<int>(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<int>(data[0]))
+ ")");
}
return;
}

View File

@ -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<uint8_t> 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<uint8_t>& 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<ConnectionToClientUDP>(
@ -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()) {

View File

@ -129,14 +129,14 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& 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<uint8_t>& 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<const char*>(&(buffer[1]))) + "'");
Log(LogLevel::kError,
"got invalid json in clientinfo message: '"
+ std::string(reinterpret_cast<const char*>(&(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<int>((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

View File

@ -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..

View File

@ -219,7 +219,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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]);

View File

@ -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.");
}
}

View File

@ -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;

View File

@ -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<PythonContextCall>& 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<PythonContextCall>& call) {
void Logic::PushPythonCallArgs(const Object::Ref<PythonContextCall>& 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<PythonContextCall>& call,
}
void Logic::PushPythonWeakCall(const Object::WeakRef<PythonContextCall>& 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<PythonContextCall>& call) {
void Logic::PushPythonWeakCallArgs(
const Object::WeakRef<PythonContextCall>& 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 = "<error>";
*valid = false;
}

View File

@ -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<PythonContextCall>& call) -> void;
auto PushPythonCallArgs(const Object::Ref<PythonContextCall>& 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<PythonContextCall>& call)
-> void;
auto PushPythonWeakCallArgs(const Object::WeakRef<PythonContextCall>& call,

View File

@ -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_ = "<error>";
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");
}

View File

@ -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<int>(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<int>(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();
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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");
}

View File

@ -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

View File

@ -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) {

View File

@ -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<sockaddr*>(&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<uint8_t> 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<char*>(&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.");
}
}

View File

@ -22,7 +22,8 @@ void NetworkWriter::PushSendToCall(const std::vector<uint8_t>& 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] {

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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 + "'");
}
}

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