diff --git a/.efrocachemap b/.efrocachemap
index 9dfcc088..7e7c9904 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -420,32 +420,32 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/51/eb/0a567253cc08c94c5d315a64d9af",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/bc/8f/a9c51a09c418136e386b7fdf21c7",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/02/e5/84916e123f47ccf11ddda380d699",
- "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/0a/8c/4b3344df186e2f26d959bd886c72",
+ "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/b8/e5/db9d86f120c6f2788f68320453e4",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/0f/e1/94378b32c786d5365a7810a15d73",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/55/8c/8d0a0585e434b94865ae4befc090",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/f6/21/951b7ff02b0ad14b1f0ac55763c4",
"assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/ef/c2/a607f318b815f025a20ab92f0a7b",
"assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/66/bf/6e98398016da261296b8c306560e",
- "assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/82/61/8319e81bc3fed77e8319a2fd6988",
+ "assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/87/84/9f3d39610453b3bf350698a23316",
"assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/46/e4da3c1d2b0ebf916df55c608b28",
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/97/90/39ba65c2ad714429aec82ea1ae3e",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/99/2a/bdcfa0932cf73e5cf63fd8113b1b",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/4c/c7/0184b8178869d1a3827a1bfcd5bb",
- "assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/00/70/fabda1781ddbb540cd1c2a6278db",
+ "assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/6c/81/fad9858b8904190be7686ee245f8",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/b6/e0/37dd30b686f475733ccc4b3cab49",
"assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/20/3f/198dcc5cfed5789042e1595bd048",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/03/6a/4db89c5bf1ced8eb5a5615a4ae64",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/8c/8e/67de1d9997a66299c8881d44ab22",
- "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/c2/f5/e7549f5179c22c6da97fafffc058",
+ "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/08/3b/68cea4d16f7020d932829af85323",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/2d/e5/3737c6c3979cf381321c5472bea5",
"assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/75/70/e33e6ee95830052e8f36cd2135f7",
"assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/c7/16/e31ce16d1b4150c271401669f24f",
- "assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/d7/8b/acdfb39196be7856f8bad77eb6a0",
+ "assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/07/37/ab65ccee3a555bd40e9661860c58",
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/02/ab/e310f81582b6dc2ae93348d45166",
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/d5/fe/422745cdbe51ccb4f2ced6f5554a",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/26/41/f1246ab56c6b7853f605c3a95889",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/82/12/57bf144e12be229a9b70da9c45cb",
- "assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/e8/25/a304de7a79195cb37ce9340e4194",
+ "assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/b2/46/89ae228342f20ca4937ee254197b",
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e6/59/af13a5d296da5935699bec902ed7",
"assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/9f/a6/a2c9d7f3f90a2320aa45ccfd65cd",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/87/5d/d36a8a2e9cb0f02731a3fd7af000",
@@ -454,7 +454,7 @@
"assets/build/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/74/3d/c3d40a1e5ee1edf82555da05eda9",
"assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/0a/4f/90fcd63bd12a7648b2a1e9b01586",
"assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/87/20/259904441097b886b841d7c4d09a",
- "assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/71/59/356404b8db683d8c946a40ca02e5",
+ "assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/e2/e1/b815d9f2e9b2c3a4daddaf728225",
"assets/build/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/0b/24/3cc2b5a6ebe4bca1e01b40f8ed09",
"assets/build/ba_data/data/maps/big_g.json": "https://files.ballistica.net/cache/ba1/47/0a/a617cc85d927b576c4e6fc1091ed",
"assets/build/ba_data/data/maps/bridgit.json": "https://files.ballistica.net/cache/ba1/03/4b/57ee9b42854b26f23f81bd8c58ef",
@@ -3971,50 +3971,50 @@
"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/58/fd/d72fbca459b185e8afbb06809ed6",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ee/26/7d95e9e9690eaa1b865463014f98",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/74/1d/fc9e33e565475daaac80da5252f0",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/53/08/51bcc266af4f781b51f901f1035c",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7c/5a/8073a7469267775f911146e5a63b",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a4/0b/4b8d1d1860e07d6c310491737038",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7c/a0/c05d501b5285b420df878daee007",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/80/92/11a44a4395f70cf709ac82283cf0",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/09/aa/968d1f5d5f8e263cb798948ecf12",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9c/7b/ac1a200be0f37078af0991faca3b",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0f/41/50fb52935e99d22064a846ba0228",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/96/d9/744aaf4cafdab3604c3ade120566",
- "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/fd/da/d960a375186f00e31bfe525c4ddf",
- "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/62/27/808783deef688eae80758fd366bb",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/08/13/8d736b50fc456c0f4bf28932cdba",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b2/7a/f146b4c46af2d879f9d2001f5549",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/de/5f/fc9a0bead56b3aebbe7ce5fe3e76",
- "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a0/f1/1ff103dd68124320676dd5bb55b5",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/60/e1/0a32c6a13ef0584f9fd05c63a14c",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/ac/69a19cd8529e3be4c41c6b9dc41b",
- "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/0e/f9/e5c30b3c21cd0f56bf5c2273152b",
- "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f6/a3/ddeb4ac188dcd75180229af77c7e",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/80/32/eaa12833c0c1058e8de23ba61ef0",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/1a/7e/78c2ca66e9fe29c94e837846b833",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/05/31/c89531314b9ad630e191a5821a0c",
- "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a6/4f/6ed44d4194bfe50d1fa634823b6d",
- "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/c7/948c131d58594bfc9358c76fc9cc",
- "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/94/57/cd3580bf48aa736523a81bf132b9",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6b/80/d71d814dbb7fd2c7062524d74ac3",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f4/fa/7084c793e3a0de5ada3a4c5ef242",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/88/f9/076ea39266eaaf256f75bbed1af3",
- "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/11/cc/7a587313f931221d486c0af4868b",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d4/75/16ff39f4507a3d7b5c063eccea2e",
- "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9a/50/1221d18faf9363305f5d8c370a0a",
- "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a7/ea/840e778dc33042219f7c7068ef20",
- "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bc/ab/e803c965fa448e268afc49555ab7",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/44/24/cf847c839553d5ed959550581ac1",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/08/e0/90f957a27cba0a9aac1b5269cc95",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0f/85/bee2da67697275c634cd88d5cf4d",
- "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/08/0c/9396b12f9653cd7587f6daf5f58d",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/40/12/4f8ac59e0d505e26d672840ec894",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/11/25/0c493ecada2cd87fc84bf7877f3e",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/57/93/363cdc3317d902c9c1f92bd63696",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/6c/9f/2293738027a75bcd83f62ddd9810",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/28/1f/780afd766603475f611e6e6804d7",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/da/1c/a841b30ce31b3b41078948b24fe1",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/77/24/b769aadc90eb68c2183cdad3f719",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/64/22/c1e56da255ca05406a7c45cf22b5",
- "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c5/18/29d9fe8e483ce222d3263336f7e6",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5a/cd/ef7c51e344560a70d4ca092b656f",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/1f/c527e0722286b8c9dd8cf8bbb1e8",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/1a/e1/a1a8034e2e1fade16ace9c907ff5",
+ "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/19/07/095195bc15b265b89492f8641b28",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fa/a8/8f0f464da6fbaf48cd32a52e57a7",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/86/e6/54166b647cb27688365f18188864",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/fc/0b/02397618cdb66f16f20ed42b56f0",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/cb/bf/86fd8654910cac65cf3417fe44c4",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9a/49/c975de8bfc3393550b36d3aceee2",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2f/13/71e11f74c7d9f4b7aae29fd698fa",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/02/89/46e6a952d95f6d2777f59761a0a7",
+ "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/71/8f/05b55549929a9f9bbeb87044affe",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/06/28/f87dd138acb909d269630fbc822d",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/43/13/780f59f3e669d350a5454052b8ec",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/36/4c/60982c040f3d99115533defa8424",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fa/ce/979941714e5818f53e7b432999e5",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e8/7a/53b3337ca506f115d3abb2ed2178",
+ "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/14/fc/47d215d72a1f92884b4bb933d174",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b9/56/8d0c5c7c3e88053b91ca3347d81c",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/17/666666b770534df7bf22bd8b339a",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/16/f2/f3ddad586518c3cf691f549c935a",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ec/44/0371116fbbdec59df047cd704739",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/da/99/a26510b1c727ba3c21059b6e527c",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0d/aa/d66c7ed8e090061c4223ffda8691",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/95/04/32ec2d08c22a60c1094df98f7cde",
+ "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6e/ad/651d3b89a8b3b1adbadffa242453",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/2c/9005fac95dba13211491a4a174b1",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/57/bc/4baf0c1c3ae705b13351d77c2321",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a6/3c/13037c954372776021d4e02cd976",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/03/18/548ae6eb8fa4c8e022c549e95408",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/c2/d1/075f751f5d75b98934fcacf49e30",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/23/3a/0f2194973ba501270391c8914832",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/41/23/56af29e4b57c39805c3d1be25d42",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/94/b8/6163d06d7374cde403b51b1a8cdc",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/56/8c/2d149890a539907d8fbfb682038d",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/a1/e5/f47f6a080bd269d919bbda6eadb8",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/18/b2/9c4875a5a057670e2348dc59103b",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/cc/08/576b8a703cbacbb58fd5283479f3",
+ "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/b3/15/7c6d580b3482870b5b058858624c",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/9d/7e/0a5125aa421e722c59d22b8beb19"
}
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/want-to-discuss-something-or-ask-a-question-.md b/.github/ISSUE_TEMPLATE/want-to-discuss-something-or-ask-a-question-.md
deleted file mode 100644
index bdcda739..00000000
--- a/.github/ISSUE_TEMPLATE/want-to-discuss-something-or-ask-a-question-.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: Want to discuss something or ask a question?
-about: GitHub Discussions are waiting for you
-title: Discussion
-labels: ''
-assignees: ''
-
----
-
-We're using [GitHub Discussions](https://github.com/efroemling/ballistica/discussions)! Asking and answering questions is much more convenient there than in Issues.
diff --git a/.gitignore b/.gitignore
index 3355e9a3..2768560b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,9 @@ _fulltest_buildfile_*
ballistica_files/
**/.#*
+# Environment files
+/venv
+
# Project/tool files
config/localconfig.json
config/.*
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index f2e1cf3f..b0d3e764 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -1159,6 +1159,7 @@
ioprep
ioprepped
ioprepping
+ ioreg
ipaddress
ipos
iprof
@@ -1203,6 +1204,7 @@
keepalives
keepaway
keeprefs
+ kerploople
keyanntype
keyfilt
keyint
@@ -2202,6 +2204,7 @@
srcdata
srcdir
srcfolder
+ srcid
srcjson
srcname
srcnode
@@ -2246,6 +2249,7 @@
storedhash
storeitemui
storename
+ stot
strftime
stringified
stringprep
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e4864d7..76efc354 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
-### 1.6.9 (20480, 2022-02-21)
+### 1.6.10 (20503, 2022-03-07)
+- Added `_ba.get_client_public_device_uuid` function which returns a semi-permanent device id for a connected client running 1.6.10 or newer. Can be useful to combat spam attacks or other mischief.
+- Fixed an issue with `make update` not properly rewriting Visual Studio project files to account for new/deleted source files.
+- Removed various bits of code associated with the (no-longer-functional) Google Play Games multiplayer connections.
+
+### 1.6.9 (20486, 2022-02-22)
- Upgraded Android Python to 3.9.10
- Fixed an issue with SSL in Android builds that was preventing communication with the master-server in 1.6.8
- Added a new network-diagnostics tool at 'Settings->Advanced->Network Testing'. Can be used to diagnose issues talking to master-servers/etc. (especially useful now that SSL can factor in)
diff --git a/README.md b/README.md
index 65c96357..65fe61ee 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@

-The Ballistica project is the foundation for the next generation of [BombSquad](http://bombsquadgame.com). It debuted with version 1.5 of the game and lays the foundation for some of the big changes coming in 2.0.
+The Ballistica project is the foundation for the next generation of [BombSquad](https://www.froemling.net/apps/bombsquad). It debuted with version 1.5 of the game and lays the foundation for some of the big changes coming in 2.0.
[Head to the project wiki to get started](https://github.com/efroemling/ballistica/wiki), or learn more about the project below.
diff --git a/assets/src/ba_data/python/._ba_sources_hash b/assets/src/ba_data/python/._ba_sources_hash
index 7e426813..873c68f4 100644
--- a/assets/src/ba_data/python/._ba_sources_hash
+++ b/assets/src/ba_data/python/._ba_sources_hash
@@ -1 +1 @@
-135697331021140808219546877225423515533
\ No newline at end of file
+135697331021140808219546877225423515533
diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py
index 02c6d161..f199c10c 100644
--- a/assets/src/ba_data/python/_ba.py
+++ b/assets/src/ba_data/python/_ba.py
@@ -1099,11 +1099,6 @@ def _app() -> ba.App:
return ba.App()
-def accept_party_invitation(invite_id: str) -> None:
- """(internal)"""
- return None
-
-
def add_clean_frame_callback(call: Callable) -> None:
"""(internal)
@@ -1658,6 +1653,22 @@ def get_chat_messages() -> list[str]:
return ['blah', 'blah2']
+def get_client_public_device_uuid(client_id: int) -> Optional[str]:
+ """get_client_public_device_uuid(client_id: int) -> Optional[str]
+
+ (internal)
+
+ Category: General Utility Functions
+
+ Return a public device UUID for a client. If the client does not
+ exist or is running a version older than 1.6.10, returns None.
+ Public device UUID uniquely identifies the device the client is
+ using in a semi-permanent way. The UUID value will change
+ periodically with updates to the game or operating system.
+ """
+ return ''
+
+
def get_collision_info(*args: Any) -> Any:
"""Return collision related values
@@ -1726,11 +1737,6 @@ def get_game_roster() -> list[dict[str, Any]]:
return [{'foo': 'bar'}]
-def get_google_play_party_client_count() -> int:
- """(internal)"""
- return int()
-
-
def get_idle_time() -> int:
"""(internal)
@@ -2234,13 +2240,6 @@ def increment_analytics_counts_raw(name: str, increment: int = 1) -> None:
return None
-def invite_players() -> None:
- """(internal)
- Category: General Utility Functions
- """
- return None
-
-
def is_blessed() -> bool:
"""(internal)"""
return bool()
@@ -2936,14 +2935,6 @@ def show_app_invite(title: Union[str, ba.Lstr], message: Union[str, ba.Lstr],
return None
-def show_invites_ui() -> None:
- """(internal)
-
- Category: **General Utility Functions**
- """
- return None
-
-
def show_online_score_ui(show: str = 'general',
game: str = None,
game_version: str = None) -> None:
@@ -2975,22 +2966,6 @@ def sign_out() -> None:
return None
-def start_listening_for_wii_remotes() -> None:
- """(internal)
-
- Start listening for connections from wii remotes.
- """
- return None
-
-
-def stop_listening_for_wii_remotes() -> None:
- """(internal)
-
- Stop listening for connections from wii remotes.
- """
- return None
-
-
def submit_analytics_counts() -> None:
"""(internal)"""
return None
diff --git a/assets/src/ba_data/python/ba/_hooks.py b/assets/src/ba_data/python/ba/_hooks.py
index 11dd8235..11ddae36 100644
--- a/assets/src/ba_data/python/ba/_hooks.py
+++ b/assets/src/ba_data/python/ba/_hooks.py
@@ -352,3 +352,13 @@ def get_player_icon(sessionplayer: ba.SessionPlayer) -> dict[str, Any]:
'tint_color': info['tint_color'],
'tint2_color': info['tint2_color']
}
+
+
+def hash_strings(inputs: list[str]) -> str:
+ """Hash provided strings into a short output string."""
+ import hashlib
+ sha = hashlib.sha1()
+ for inp in inputs:
+ sha.update(inp.encode())
+
+ return sha.hexdigest()
diff --git a/assets/src/ba_data/python/ba/_language.py b/assets/src/ba_data/python/ba/_language.py
index 6d431ced..805cf287 100644
--- a/assets/src/ba_data/python/ba/_language.py
+++ b/assets/src/ba_data/python/ba/_language.py
@@ -374,7 +374,7 @@ class Lstr:
currently-active language.
To see available resource keys, look at any of the bs_language_*.py files
- in the game or the translations pages at bombsquadgame.com/translate.
+ in the game or the translations pages at legacy.ballistica.net/translate.
##### Examples
EXAMPLE 1: specify a string from a resource path
diff --git a/assets/src/ba_data/python/ba/_net.py b/assets/src/ba_data/python/ba/_net.py
index 2a47e70d..d427ccce 100644
--- a/assets/src/ba_data/python/ba/_net.py
+++ b/assets/src/ba_data/python/ba/_net.py
@@ -32,6 +32,7 @@ class NetworkSubsystem:
# For debugging.
self.v1_test_log: str = ''
+ self.v1_ctest_results: dict[int, str] = {}
def get_ip_address_type(addr: str) -> socket.AddressFamily:
@@ -117,15 +118,14 @@ class MasterServerCallThread(threading.Thread):
if self._request_type == 'get':
response = urllib.request.urlopen(
urllib.request.Request(
- (_ba.get_master_server_address(internal=True) + '/' +
+ (_ba.get_master_server_address() + '/' +
self._request + '?' + parse.urlencode(self._data)),
None, {'User-Agent': _ba.app.user_agent_string}),
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
elif self._request_type == 'post':
response = urllib.request.urlopen(
urllib.request.Request(
- _ba.get_master_server_address(internal=True) + '/' +
- self._request,
+ _ba.get_master_server_address() + '/' + self._request,
parse.urlencode(self._data).encode(),
{'User-Agent': _ba.app.user_agent_string}),
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
diff --git a/assets/src/ba_data/python/bastd/ui/party.py b/assets/src/ba_data/python/bastd/ui/party.py
index 307b5704..f0b65d38 100644
--- a/assets/src/ba_data/python/bastd/ui/party.py
+++ b/assets/src/ba_data/python/bastd/ui/party.py
@@ -5,7 +5,6 @@
from __future__ import annotations
import math
-import weakref
from typing import TYPE_CHECKING, cast
import _ba
@@ -413,53 +412,3 @@ class PartyWindow(ba.Window):
"""Close the window and make a lovely sound."""
ba.playsound(ba.getsound('swish'))
self.close()
-
-
-def handle_party_invite(name: str, invite_id: str) -> None:
- """Handle an incoming party invitation."""
- from bastd import mainmenu
- from bastd.ui import confirm
- ba.playsound(ba.getsound('fanfare'))
-
- # if we're not in the main menu, just print the invite
- # (don't want to screw up an in-progress game)
- in_game = not isinstance(_ba.get_foreground_host_session(),
- mainmenu.MainMenuSession)
- if in_game:
- ba.screenmessage(ba.Lstr(
- value='${A}\n${B}',
- subs=[('${A}',
- ba.Lstr(resource='gatherWindow.partyInviteText',
- subs=[('${NAME}', name)])),
- ('${B}',
- ba.Lstr(
- resource='gatherWindow.partyInviteGooglePlayExtraText'))
- ]),
- color=(0.5, 1, 0))
- else:
-
- def do_accept(inv_id: str) -> None:
- _ba.accept_party_invitation(inv_id)
-
- conf = confirm.ConfirmWindow(
- ba.Lstr(resource='gatherWindow.partyInviteText',
- subs=[('${NAME}', name)]),
- ba.Call(do_accept, invite_id),
- width=500,
- height=150,
- color=(0.75, 1.0, 0.0),
- ok_text=ba.Lstr(resource='gatherWindow.partyInviteAcceptText'),
- cancel_text=ba.Lstr(resource='gatherWindow.partyInviteIgnoreText'))
-
- # FIXME: Ugly.
- # Let's store the invite-id away on the confirm window so we know if
- # we need to kill it later.
- conf.party_invite_id = invite_id # type: ignore
-
- # store a weak-ref so we can get at this later
- ba.app.invite_confirm_windows.append(weakref.ref(conf))
-
- # go ahead and prune our weak refs while we're here.
- ba.app.invite_confirm_windows = [
- w for w in ba.app.invite_confirm_windows if w() is not None
- ]
diff --git a/assets/src/ba_data/python/bastd/ui/settings/advanced.py b/assets/src/ba_data/python/bastd/ui/settings/advanced.py
index d281b4f3..73a1338a 100644
--- a/assets/src/ba_data/python/bastd/ui/settings/advanced.py
+++ b/assets/src/ba_data/python/bastd/ui/settings/advanced.py
@@ -322,8 +322,8 @@ class AdvancedSettingsWindow(ba.Window):
subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))
]),
autoselect=True,
- on_activate_call=ba.Call(ba.open_url,
- 'http://bombsquadgame.com/translate'))
+ on_activate_call=ba.Call(
+ ba.open_url, 'https://legacy.ballistica.net/translate'))
self._lang_status_text = ba.textwidget(parent=self._subcontainer,
position=(self._sub_width * 0.5,
diff --git a/assets/src/ba_data/python/bastd/ui/settings/nettesting.py b/assets/src/ba_data/python/bastd/ui/settings/nettesting.py
index 8a6d8088..31dd809e 100644
--- a/assets/src/ba_data/python/bastd/ui/settings/nettesting.py
+++ b/assets/src/ba_data/python/bastd/ui/settings/nettesting.py
@@ -167,29 +167,28 @@ def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None:
_print_test_results(_dummy_fail)
# V1 ping
- baseaddr = _ba.get_master_server_address(internal=True,
- source=0,
- version=1)
- _print(f'\nContacting V1 master-server ({baseaddr})...')
+ baseaddr = _ba.get_master_server_address(source=0, version=1)
+ _print(f'\nContacting V1 master-server src0 ({baseaddr})...')
_print_test_results(lambda: _test_fetch(baseaddr))
# V1 alternate ping
- baseaddr = _ba.get_master_server_address(internal=True,
- source=1,
- version=1)
- _print(f'\nContacting V1 alt master-server ({baseaddr})...')
+ baseaddr = _ba.get_master_server_address(source=1, version=1)
+ _print(f'\nContacting V1 master-server src1 ({baseaddr})...')
_print_test_results(lambda: _test_fetch(baseaddr))
_print(f'\nV1-test-log: {ba.app.net.v1_test_log}')
- curv1addr = _ba.get_master_server_address(internal=True, version=1)
- _print(f'\nCurrent V1 address: {curv1addr}')
+ for srcid, result in sorted(ba.app.net.v1_ctest_results.items()):
+ _print(f'\nV1 src{srcid} result: {result}')
+
+ curv1addr = _ba.get_master_server_address(version=1)
+ _print(f'\nUsing V1 address: {curv1addr}')
_print('\nRunning V1 transaction...')
_print_test_results(_test_v1_transaction)
# V2 ping
- baseaddr = _ba.get_master_server_address(internal=True, version=2)
+ baseaddr = _ba.get_master_server_address(version=2)
_print(f'\nContacting V2 master-server ({baseaddr})...')
_print_test_results(lambda: _test_fetch(baseaddr))
diff --git a/ballisticacore-cmake/.idea/dictionaries/ericf.xml b/ballisticacore-cmake/.idea/dictionaries/ericf.xml
index 3c7dd4c3..652abd5f 100644
--- a/ballisticacore-cmake/.idea/dictionaries/ericf.xml
+++ b/ballisticacore-cmake/.idea/dictionaries/ericf.xml
@@ -560,6 +560,7 @@
ioprep
ioprepped
ioprepping
+ ioreg
iserverget
iserverput
isinst
@@ -587,6 +588,7 @@
jnames
json's
keepalives
+ kerploople
keyanntype
keycode
keyfilt
@@ -1060,6 +1062,7 @@
spivak
srcattr
srcfolder
+ srcid
srcname
srcpath
srcsz
@@ -1092,6 +1095,7 @@
stepsize
storagenames
storecmd
+ stot
strcasecmp
strchr
strcpy
diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj
index 0d0dc72c..d208d02b 100644
--- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj
+++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj
@@ -217,6 +217,9 @@
+
+
+
@@ -300,6 +303,7 @@
+
@@ -491,6 +495,8 @@
+
+
diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters
index 25c8e135..4644102e 100644
--- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters
+++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters
@@ -85,6 +85,15 @@
ballistica\config
+
+ ballistica\config
+
+
+ ballistica\config
+
+
+ ballistica\config
+
ballistica\core
@@ -334,6 +343,9 @@
ballistica\game
+
+ ballistica\game
+
ballistica\game
@@ -907,6 +919,12 @@
ballistica\platform\sdl
+
+ ballistica\platform\windows
+
+
+ ballistica\platform\windows
+
ballistica\python\class
@@ -1604,6 +1622,7 @@
+
diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj
index d3df0a33..d79838a4 100644
--- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj
+++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj
@@ -212,6 +212,9 @@
+
+
+
@@ -295,6 +298,7 @@
+
@@ -486,6 +490,8 @@
+
+
diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters
index 25c8e135..4644102e 100644
--- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters
+++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters
@@ -85,6 +85,15 @@
ballistica\config
+
+ ballistica\config
+
+
+ ballistica\config
+
+
+ ballistica\config
+
ballistica\core
@@ -334,6 +343,9 @@
ballistica\game
+
+ ballistica\game
+
ballistica\game
@@ -907,6 +919,12 @@
ballistica\platform\sdl
+
+ ballistica\platform\windows
+
+
+ ballistica\platform\windows
+
ballistica\python\class
@@ -1604,6 +1622,7 @@
+
diff --git a/docs/ba_module.md b/docs/ba_module.md
index 472e23ab..2d4152bf 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-
last updated for Ballistica version 1.6.9 build 20480
+last updated for Ballistica version 1.6.10 build 20501
This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!
@@ -3428,7 +3428,7 @@ needs a chooser.
currently-active language.
To see available resource keys, look at any of the bs_language_*.py files
- in the game or the translations pages at bombsquadgame.com/translate.
+ in the game or the translations pages at legacy.ballistica.net/translate.
# EXAMPLE 1: specify a string from a resource path
mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
diff --git a/src/ballistica/app/app.cc b/src/ballistica/app/app.cc
index f891ae0f..c0996f34 100644
--- a/src/ballistica/app/app.cc
+++ b/src/ballistica/app/app.cc
@@ -39,7 +39,7 @@ void App::PostInit() {
App::~App() = default;
-auto App::UsesEventLoop() const -> bool {
+auto App::ManagesEventLoop() const -> bool {
// We have 2 redundant values for essentially the same thing;
// should get rid of IsEventPushMode() once we've created
// App subclasses for our various platforms.
@@ -48,8 +48,8 @@ auto App::UsesEventLoop() const -> bool {
void App::RunRenderUpkeepCycle() {
// This should only be used in cases where the OS is handling the event loop.
- assert(!UsesEventLoop());
- if (UsesEventLoop()) {
+ assert(!ManagesEventLoop());
+ if (ManagesEventLoop()) {
return;
}
@@ -111,13 +111,10 @@ void App::ShutdownComplete() {
assert(InMainThread());
assert(g_platform);
- // Need to call our cleanup stuff that would otherwise get called in main.
- g_platform->FinalCleanup();
-
done_ = true;
// Kill our own event loop (or tell the OS to kill its).
- if (UsesEventLoop()) {
+ if (ManagesEventLoop()) {
thread()->Quit();
} else {
g_platform->QuitApp();
@@ -249,7 +246,7 @@ void App::ResumeApp() {
void App::DidFinishRenderingFrame(FrameDef* frame) {}
void App::PrimeEventPump() {
- assert(!UsesEventLoop());
+ assert(!ManagesEventLoop());
// Pump events manually until a screen gets created.
// At that point we use frame-draws to drive our event loop.
diff --git a/src/ballistica/app/app.h b/src/ballistica/app/app.h
index 30576afc..3bfa0878 100644
--- a/src/ballistica/app/app.h
+++ b/src/ballistica/app/app.h
@@ -30,7 +30,7 @@ class App : public Module {
/// If false, BallisticaMain returns immediately and it is assumed
/// that the OS handles the app lifecycle and pushes events to the app
/// via callbacks/etc.
- auto UsesEventLoop() const -> bool;
+ auto ManagesEventLoop() const -> bool;
/// Called for non-event-loop apps to give them an opportunity to
/// ensure they are self-sustaining. For instance, an app relying on
diff --git a/src/ballistica/app/app_globals.h b/src/ballistica/app/app_globals.h
index 87689a1f..89c9c2c1 100644
--- a/src/ballistica/app/app_globals.h
+++ b/src/ballistica/app/app_globals.h
@@ -77,8 +77,6 @@ class AppGlobals {
int delay_bucket_samples{60};
bool vr_mode{g_buildconfig.vr_build()};
- // Temp dirty way to do some shutdown stuff (FIXME: move to an App method).
- void (*temp_cleanup_callback)() = nullptr;
millisecs_t real_time{};
millisecs_t last_real_time_ticks{};
std::mutex real_time_mutex;
diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc
index fa30ef1d..a4d28b0a 100644
--- a/src/ballistica/ballistica.cc
+++ b/src/ballistica/ballistica.cc
@@ -21,8 +21,8 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kAppBuildNumber = 20480;
-const char* kAppVersion = "1.6.9";
+const int kAppBuildNumber = 20503;
+const char* kAppVersion = "1.6.10";
// Our standalone globals.
// These are separated out for easy access.
@@ -138,9 +138,9 @@ auto BallisticaMain(int argc, char** argv) -> int {
// Phase 3/4: Create a screen and/or kick off game (in other threads).
// -------------------------------------------------------------------------
- if (g_app->UsesEventLoop()) {
- // On our event-loop using platforms we now simply sit in our event loop
- // until the app is quit.
+ if (g_app->ManagesEventLoop()) {
+ // On our event-loop-managing platforms we now simply sit in our event
+ // loop until the app is quit.
g_main_thread->RunEventLoop(false);
} else {
// In this case we'll now simply return and let the OS feed us events
@@ -156,8 +156,8 @@ auto BallisticaMain(int argc, char** argv) -> int {
std::string("Unhandled exception in BallisticaMain(): ") + exc.what();
// Exiting the app via an exception tends to trigger crash reports
- // on various platforms. If it doesn't appear that we're an official live
- // build then we'd rather just exit cleanly with an error code and avoid
+ // on various platforms. If it seems we're not on an official live
+ // build then we'd rather just exit cleanly with an error code and avoid
// polluting crash report logs from dev builds.
FatalError::ReportFatalError(error_msg, true);
bool exit_cleanly = !IsUnmodifiedBlessedBuild();
@@ -210,7 +210,7 @@ auto FatalError(const std::string& message) -> void {
assert(handled);
}
-auto GetUniqueSessionIdentifier() -> const std::string& {
+auto GetAppInstanceUUID() -> const std::string& {
static std::string session_id;
static bool have_session_id = false;
@@ -225,7 +225,7 @@ auto GetUniqueSessionIdentifier() -> const std::string& {
}
if (!have_session_id) {
// As an emergency fallback simply use a single random number.
- Log("WARNING: GetUniqueSessionIdentifier() using rand fallback.");
+ Log("WARNING: GetSessionUUID() using rand fallback.");
srand(static_cast(
Platform::GetCurrentMilliseconds())); // NOLINT
session_id = std::to_string(static_cast(rand())); // NOLINT
@@ -256,11 +256,7 @@ auto InAudioThread() -> bool {
}
auto InBGDynamicsThread() -> bool {
-#if !BA_HEADLESS_BUILD
return (g_bg_dynamics_server && g_bg_dynamics_server->thread()->IsCurrent());
-#else
- return false;
-#endif
}
auto InMediaThread() -> bool {
diff --git a/src/ballistica/ballistica.h b/src/ballistica/ballistica.h
index 47b37dfd..699ce0c9 100644
--- a/src/ballistica/ballistica.h
+++ b/src/ballistica/ballistica.h
@@ -147,9 +147,9 @@ extern Utils* g_utils;
/// Main ballistica entry point.
auto BallisticaMain(int argc, char** argv) -> int;
-/// Return a string that should be universally unique to this device and
+/// Return a string that should be universally unique to this particular
/// running instance of the app.
-auto GetUniqueSessionIdentifier() -> const std::string&;
+auto GetAppInstanceUUID() -> const std::string&;
/// Have our main threads/modules all been inited yet?
auto IsBootstrapped() -> bool;
@@ -242,7 +242,7 @@ inline auto HeadlessMode() -> bool {
/// by significant amounts (even if the app has been sleeping or whatnot).
auto GetRealTime() -> millisecs_t;
-/// Return a random float value. Not guaranteed to be deterministic or
+/// Return a random float value. Not guaranteed to be deterministic or
/// consistent across platforms.
inline auto RandomFloat() -> float {
// FIXME: should convert this to something thread-safe.
diff --git a/src/ballistica/game/connection/connection_set.h b/src/ballistica/game/connection/connection_set.h
index 50335629..29db4fc2 100644
--- a/src/ballistica/game/connection/connection_set.h
+++ b/src/ballistica/game/connection/connection_set.h
@@ -94,19 +94,6 @@ class ConnectionSet {
const std::vector& clients)
-> void;
-#if BA_GOOGLE_BUILD
- auto PushClientDisconnectedGooglePlayCall(int id) -> void;
- int GetGooglePlayClientCount() const;
- auto PushHostConnectedGooglePlayCall() -> void;
- auto PushClientConnectedGooglePlayCall(int id) -> void;
- auto PushCompressedGamePacketFromHostGooglePlayCall(
- const std::vector& data) -> void;
- auto PushCompressedGamePacketFromClientGooglePlayCall(
- int google_client_id, const std::vector& data) -> void;
- auto ClientIDFromGooglePlayClientID(int google_id) -> int;
- auto GooglePlayClientIDFromClientID(int client_id) -> int;
-#endif
-
auto UDPConnectionPacket(const std::vector& data,
const SockAddr& addr) -> void;
auto PushClientDisconnectedCall(int id) -> void;
@@ -125,11 +112,6 @@ class ConnectionSet {
// Prevents us from printing multiple 'you got disconnected' messages.
bool printed_host_disconnect_{};
-
-#if BA_GOOGLE_BUILD
- std::unordered_map google_play_id_to_client_id_map_;
- std::unordered_map client_id_to_google_play_id_map_;
-#endif
};
} // namespace ballistica
diff --git a/src/ballistica/game/connection/connection_to_client.h b/src/ballistica/game/connection/connection_to_client.h
index 2a509344..bb58023d 100644
--- a/src/ballistica/game/connection/connection_to_client.h
+++ b/src/ballistica/game/connection/connection_to_client.h
@@ -41,34 +41,45 @@ class ConnectionToClient : public Connection {
/// account id has been verified by the master server.
auto IsAdmin() const -> bool;
- private:
- virtual auto ShouldPrintIncompatibleClientErrors() const -> bool;
+ auto kick_voted() const { return kick_voted_; }
+ auto set_kick_voted(bool val) { kick_voted_ = val; }
+ auto kick_vote_choice() const { return kick_vote_choice_; }
+ auto set_kick_vote_choice(bool val) { kick_vote_choice_ = val; }
+ auto set_next_kick_vote_allow_time(millisecs_t val) {
+ next_kick_vote_allow_time_ = val;
+ }
+ auto next_kick_vote_allow_time() const { return next_kick_vote_allow_time_; }
+
+ auto public_device_id() const { return public_device_id_; }
// Returns a spec for this client that incorporates their player names
// or their peer name if they have no players.
auto GetCombinedSpec() -> PlayerSpec;
+
+ private:
+ virtual auto ShouldPrintIncompatibleClientErrors() const -> bool;
auto GetClientInputDevice(int remote_id) -> ClientInputDevice*;
void Error(const std::string& error_msg) override;
std::string our_handshake_player_spec_str_;
std::string our_handshake_salt_;
std::string peer_public_account_id_;
- ClientControllerInterface* controller_ = nullptr;
+ std::string public_device_id_;
+ ClientControllerInterface* controller_{};
std::unordered_map client_input_devices_;
- millisecs_t last_hand_shake_send_time_ = 0;
- int id_ = -1;
- int build_number_ = 0;
- bool got_client_info_ = false;
- bool kick_voted_ = false;
- bool kick_vote_choice_ = false;
+ millisecs_t last_hand_shake_send_time_{};
+ int id_{-1};
+ int build_number_{};
+ bool got_client_info_{};
+ bool kick_voted_{};
+ bool kick_vote_choice_{};
std::string token_;
std::string peer_hash_;
PythonRef player_profiles_;
- bool got_info_from_master_server_ = false;
+ bool got_info_from_master_server_{};
std::vector last_chat_times_;
- millisecs_t next_kick_vote_allow_time_ = 0;
- millisecs_t chat_block_time_ = 0;
- millisecs_t last_remove_player_time_ = -99999;
- int next_chat_block_seconds_ = 10;
- friend class Game;
+ millisecs_t next_kick_vote_allow_time_{};
+ millisecs_t chat_block_time_{};
+ millisecs_t last_remove_player_time_{-99999};
+ int next_chat_block_seconds_{10};
};
} // namespace ballistica
diff --git a/src/ballistica/game/connection/connection_to_client_udp.h b/src/ballistica/game/connection/connection_to_client_udp.h
index 357017ed..58c3d045 100644
--- a/src/ballistica/game/connection/connection_to_client_udp.h
+++ b/src/ballistica/game/connection/connection_to_client_udp.h
@@ -20,19 +20,20 @@ class ConnectionToClientUDP : public ConnectionToClient {
~ConnectionToClientUDP() override;
void Update() override;
void HandleGamePacket(const std::vector& buffer) override;
- auto client_name() const -> const std::string& { return client_name_; }
+ auto client_instance_uuid() const { return client_instance_uuid_; }
auto GetAsUDP() -> ConnectionToClientUDP* override;
void RequestDisconnect() override;
-
- protected:
- uint8_t request_id_;
- std::unique_ptr addr_;
- std::string client_name_;
- bool did_die_;
void Die();
void SendDisconnectRequest();
+ auto SendGamePacketCompressed(const std::vector& data)
+ -> void override;
+
+ private:
+ uint8_t request_id_;
+ std::unique_ptr addr_;
+ std::string client_instance_uuid_;
+ bool did_die_;
millisecs_t last_client_response_time_;
- void SendGamePacketCompressed(const std::vector& data) override;
};
} // namespace ballistica
diff --git a/src/ballistica/game/connection/connection_to_host_udp.h b/src/ballistica/game/connection/connection_to_host_udp.h
index d318a8e0..d3fe9cbe 100644
--- a/src/ballistica/game/connection/connection_to_host_udp.h
+++ b/src/ballistica/game/connection/connection_to_host_udp.h
@@ -28,21 +28,20 @@ class ConnectionToHostUDP : public ConnectionToHost {
auto SwitchProtocol() -> bool;
void RequestDisconnect() override;
- protected:
+ void SendGamePacketCompressed(const std::vector& data) override;
+ void Error(const std::string& error_msg) override;
+ void Die();
+ void SendDisconnectRequest();
+
+ private:
+ void GetRequestID();
uint8_t request_id_{};
std::unique_ptr addr_;
bool did_die_{};
- void Die();
- void SendDisconnectRequest();
millisecs_t last_client_id_request_time_{};
millisecs_t last_disconnect_request_time_{};
int client_id_{};
millisecs_t last_host_response_time_{};
- void SendGamePacketCompressed(const std::vector& data) override;
- void Error(const std::string& error_msg) override;
-
- private:
- void GetRequestID();
};
} // namespace ballistica
diff --git a/src/ballistica/game/game.cc b/src/ballistica/game/game.cc
index 6f5d111b..34e618fa 100644
--- a/src/ballistica/game/game.cc
+++ b/src/ballistica/game/game.cc
@@ -396,7 +396,7 @@ void Game::UpdateKickVote() {
kick_vote_in_progress_ = false;
return;
}
- millisecs_t current_time = GetRealTime();
+ millisecs_t current_time{GetRealTime()};
int total_client_count = 0;
int yes_votes = 0;
int no_votes = 0;
@@ -405,8 +405,8 @@ void Game::UpdateKickVote() {
// the update and possibly perform the kick.
for (ConnectionToClient* client : connections()->GetConnectionsToClients()) {
++total_client_count;
- if (client->kick_voted_) {
- if (client->kick_vote_choice_) {
+ if (client->kick_voted()) {
+ if (client->kick_vote_choice()) {
++yes_votes;
} else {
++no_votes;
@@ -437,8 +437,8 @@ void Game::UpdateKickVote() {
if (client == kick_vote_starter) {
delay += kKickVoteFailRetryDelayInitiatorExtra;
}
- client->next_kick_vote_allow_time_ =
- std::max(client->next_kick_vote_allow_time_, current_time + delay);
+ client->set_next_kick_vote_allow_time(
+ std::max(client->next_kick_vote_allow_time(), current_time + delay));
}
} else {
int votes_required;
@@ -1826,26 +1826,6 @@ void Game::CleanUpBeforeConnectingToHost() {
SetPublicPartyEnabled(false);
}
-void Game::PushV1PartyInviteCall(const std::string& name,
- const std::string& invite_id) {
- PushCall([this, name, invite_id] { V1PartyInvite(name, invite_id); });
-}
-
-void Game::V1PartyInvite(const std::string& name,
- const std::string& invite_id) {
- assert(InGameThread());
- g_python->V1PartyInvite(name, invite_id);
-}
-
-void Game::PushV1PartyInviteRevokeCall(const std::string& invite_id) {
- PushCall([this, invite_id] { V1PartyInviteRevoke(invite_id); });
-}
-
-void Game::V1PartyInviteRevoke(const std::string& invite_id) {
- assert(InGameThread());
- g_python->V1PartyInviteRevoke(invite_id);
-}
-
auto Game::GetPartySize() const -> int {
assert(InGameThread());
assert(game_roster_ != nullptr);
@@ -1897,7 +1877,6 @@ auto Game::GetGameRosterMessage() -> std::vector {
// This message is simply a flattened json string of our roster (including
// terminating char).
char* s = cJSON_PrintUnformatted(game_roster_);
- // printf("ROSTER MESSAGE %s\n", s);
auto s_len = strlen(s);
std::vector msg(1 + s_len + 1);
msg[0] = BA_MESSAGE_PARTY_ROSTER;
@@ -1965,13 +1944,13 @@ void Game::StartKickVote(ConnectionToClient* starter,
starter->SendScreenMessage(R"({"r":"kickVoteFailedNotEnoughVotersText",)"
R"("f":"kickVoteFailedText"})",
1, 0, 0);
- } else if (current_time < starter->next_kick_vote_allow_time_) {
+ } else if (current_time < starter->next_kick_vote_allow_time()) {
// Not yet allowed error.
starter->SendScreenMessage(
R"({"r":"voteDelayText","s":[["${NUMBER}",")"
+ std::to_string(std::max(
millisecs_t{1},
- (starter->next_kick_vote_allow_time_ - current_time) / 1000))
+ (starter->next_kick_vote_allow_time() - current_time) / 1000))
+ "\"]]}",
1, 0, 0);
} else {
@@ -2014,10 +1993,10 @@ void Game::StartKickVote(ConnectionToClient* starter,
for (ConnectionToClient* client :
connections()->GetConnectionsToClients()) {
if (client == starter) {
- client->kick_voted_ = true;
- client->kick_vote_choice_ = true;
+ client->set_kick_voted(true);
+ client->set_kick_vote_choice(true);
} else {
- client->kick_voted_ = false;
+ client->set_kick_voted(false);
}
}
}
diff --git a/src/ballistica/game/game.h b/src/ballistica/game/game.h
index a298166a..7e922ded 100644
--- a/src/ballistica/game/game.h
+++ b/src/ballistica/game/game.h
@@ -36,9 +36,6 @@ class Game : public Module {
V1LoginState account_state,
const std::string& account_name,
const std::string& account_id) -> void;
- auto PushV1PartyInviteCall(const std::string& name,
- const std::string& invite_id) -> void;
- auto PushV1PartyInviteRevokeCall(const std::string& invite_id) -> void;
auto PushInitialScreenCreatedCall() -> void;
auto PushApplyConfigCall() -> void;
auto PushRemoveGraphicsServerRenderHoldCall() -> void;
@@ -252,9 +249,6 @@ class Game : public Module {
auto HandleQuitOnIdle() -> void;
auto InitSpecialChars() -> void;
auto Draw() -> void;
- auto V1PartyInvite(const std::string& name, const std::string& invite_id)
- -> void;
- auto V1PartyInviteRevoke(const std::string& invite_id) -> void;
auto InitialScreenCreated() -> void;
auto MainMenuPress(InputDevice* device) -> void;
auto ScreenResize(float virtual_width, float virtual_height,
diff --git a/src/ballistica/generic/utils.cc b/src/ballistica/generic/utils.cc
index 34a736dc..2e9be5b7 100644
--- a/src/ballistica/generic/utils.cc
+++ b/src/ballistica/generic/utils.cc
@@ -408,6 +408,7 @@ auto Utils::PtrToString(const void* val) -> std::string {
return buffer;
}
+// FIXME: This should not live here.
static const char* g_default_random_names[] = {
"Flopsy", "Skippy", "Boomer", "Jolly", "Zeus", "Garth",
"Dizzy", "Mullet", "Ogre", "Ginger", "Nippy", "Murphy",
diff --git a/src/ballistica/platform/apple/platform_apple.h b/src/ballistica/platform/apple/platform_apple.h
index e5f02c0d..5cd9ff0b 100644
--- a/src/ballistica/platform/apple/platform_apple.h
+++ b/src/ballistica/platform/apple/platform_apple.h
@@ -16,8 +16,8 @@ namespace ballistica {
class PlatformApple : public Platform {
public:
PlatformApple();
- auto GetDeviceUUIDPrefix() -> std::string override;
- auto GetRealDeviceUUID(std::string* uuid) -> bool override;
+ auto GetDeviceAccountUUIDPrefix() -> std::string override;
+ auto GetRealLegacyDeviceUUID(std::string* uuid) -> bool override;
auto GenerateUUID() -> std::string override;
auto GetDefaultConfigDir() -> std::string override;
auto GetLocale() -> std::string override;
@@ -68,8 +68,6 @@ class PlatformApple : public Platform {
auto MacMusicAppStop() -> void override;
auto MacMusicAppPlayPlaylist(const std::string& playlist) -> bool override;
auto MacMusicAppGetPlaylists() -> std::list override;
- auto StartListeningForWiiRemotes() -> void override;
- auto StopListeningForWiiRemotes() -> void override;
auto IsEventPushMode() -> bool override;
auto ContainsPythonDist() -> bool override;
auto GetPlatformName() -> std::string override;
@@ -79,14 +77,9 @@ class PlatformApple : public Platform {
auto DoClipboardHasText() -> bool override;
auto DoClipboardSetText(const std::string& text) -> void override;
auto DoClipboardGetText() -> std::string override;
-
- /// Return current text from the clipboard. Raises an Exception if
- /// clipboard is unsupported or if there's no text on the clipboard.
- auto ClipboardGetText() -> std::string;
+ auto GetPublicDeviceUUIDInputs() -> std::list override;
private:
- // std::mutex log_mutex_;
- // std::string log_line_;
};
} // namespace ballistica
diff --git a/src/ballistica/platform/linux/platform_linux.cc b/src/ballistica/platform/linux/platform_linux.cc
index a126fc8b..18bfb5a8 100644
--- a/src/ballistica/platform/linux/platform_linux.cc
+++ b/src/ballistica/platform/linux/platform_linux.cc
@@ -29,6 +29,26 @@ std::string PlatformLinux::GenerateUUID() {
return val;
}
+auto PlatformLinux::GetPublicDeviceUUIDInputs() -> std::list {
+ std::list out;
+
+ // For now let's just go with machine-id.
+ // Perhaps can add kernel version or something later.
+ char buffer[100];
+ if (FILE* infile = fopen("/etc/machine-id", "r")) {
+ int size = fread(buffer, 1, 99, infile);
+ fclose(infile);
+ if (size < 10) {
+ throw Exception("unexpected machine-id value");
+ }
+ buffer[size] = 0;
+ out.push_back(buffer);
+ } else {
+ throw Exception("/etc/machine-id not accessible");
+ }
+ return out;
+};
+
bool PlatformLinux::DoHasTouchScreen() { return false; }
void PlatformLinux::DoOpenURL(const std::string& url) {
diff --git a/src/ballistica/platform/linux/platform_linux.h b/src/ballistica/platform/linux/platform_linux.h
index bebeefc7..07f09b5b 100644
--- a/src/ballistica/platform/linux/platform_linux.h
+++ b/src/ballistica/platform/linux/platform_linux.h
@@ -13,14 +13,15 @@ namespace ballistica {
class PlatformLinux : public Platform {
public:
PlatformLinux();
- std::string GetDeviceUUIDPrefix() override { return "l"; }
- std::string GenerateUUID() override;
- bool DoHasTouchScreen() override;
- void DoOpenURL(const std::string& url) override;
- void OpenFileExternally(const std::string& path) override;
- void OpenDirExternally(const std::string& path) override;
- std::string GetPlatformName() override;
- std::string GetSubplatformName() override;
+ auto GetDeviceAccountUUIDPrefix() -> std::string override { return "l"; }
+ auto GenerateUUID() -> std::string override;
+ auto DoHasTouchScreen() -> bool override;
+ auto DoOpenURL(const std::string& url) -> void override;
+ auto OpenFileExternally(const std::string& path) -> void override;
+ auto OpenDirExternally(const std::string& path) -> void override;
+ auto GetPlatformName() -> std::string override;
+ auto GetSubplatformName() -> std::string override;
+ auto GetPublicDeviceUUIDInputs() -> std::list override;
};
} // namespace ballistica
diff --git a/src/ballistica/platform/platform.cc b/src/ballistica/platform/platform.cc
index 6fba02cd..5032a513 100644
--- a/src/ballistica/platform/platform.cc
+++ b/src/ballistica/platform/platform.cc
@@ -110,33 +110,27 @@ auto Platform::Create() -> Platform* {
return platform;
}
-void Platform::FinalCleanup() {
- if (g_app_globals->temp_cleanup_callback) {
- g_app_globals->temp_cleanup_callback();
- }
-}
-
Platform::Platform() : starttime_(GetCurrentMilliseconds()) {}
auto Platform::PostInit() -> void {}
Platform::~Platform() = default;
-auto Platform::GetUniqueDeviceIdentifier() -> const std::string& {
+auto Platform::GetLegacyDeviceUUID() -> const std::string& {
if (!have_device_uuid_) {
- device_uuid_ = GetDeviceUUIDPrefix();
+ legacy_device_uuid_ = GetDeviceAccountUUIDPrefix();
std::string real_unique_uuid;
- bool have_real_unique_uuid = GetRealDeviceUUID(&real_unique_uuid);
+ bool have_real_unique_uuid = GetRealLegacyDeviceUUID(&real_unique_uuid);
if (have_real_unique_uuid) {
- device_uuid_ += real_unique_uuid;
+ legacy_device_uuid_ += real_unique_uuid;
}
// Keep demo/arcade uuids unique.
if (g_buildconfig.demo_build()) {
- device_uuid_ += "_d";
+ legacy_device_uuid_ += "_d";
} else if (g_buildconfig.arcade_build()) {
- device_uuid_ += "_a";
+ legacy_device_uuid_ += "_a";
}
// Ok, as a fallback on platforms where we don't yet have a way to get a
@@ -153,13 +147,13 @@ auto Platform::GetUniqueDeviceIdentifier() -> const std::string& {
if (size >= 0) {
assert(size < 100);
buffer[size] = 0;
- device_uuid_ += buffer;
+ legacy_device_uuid_ += buffer;
}
fclose(f);
} else {
// No existing one; generate it.
std::string val = GenerateUUID();
- device_uuid_ += val;
+ legacy_device_uuid_ += val;
if (FILE* f2 = FOpen(path.c_str(), "wb")) {
size_t result = fwrite(val.c_str(), val.size(), 1, f2);
if (result != 1) Log("unable to write bsuuid file.");
@@ -171,20 +165,50 @@ auto Platform::GetUniqueDeviceIdentifier() -> const std::string& {
}
have_device_uuid_ = true;
}
- return device_uuid_;
+ return legacy_device_uuid_;
}
-auto Platform::GetDeviceUUIDPrefix() -> std::string {
- Log("GetDeviceUUIDPrefix() unimplemented");
+auto Platform::GetDeviceAccountUUIDPrefix() -> std::string {
+ Log("GetDeviceAccountUUIDPrefix() unimplemented");
return "u";
}
-auto Platform::GetRealDeviceUUID(std::string* uuid) -> bool { return false; }
+auto Platform::GetRealLegacyDeviceUUID(std::string* uuid) -> bool {
+ return false;
+}
auto Platform::GenerateUUID() -> std::string {
throw Exception("GenerateUUID() unimplemented");
}
+auto Platform::GetPublicDeviceUUID() -> std::string {
+ assert(g_python);
+ if (public_device_uuid_.empty()) {
+ std::list inputs{GetPublicDeviceUUIDInputs()};
+
+ // This UUID is supposed to change periodically, so let's plug in
+ // some stuff to enforce that.
+ inputs.emplace_back(GetOSVersionString());
+ inputs.emplace_back(kAppVersion);
+ inputs.emplace_back("kerploople");
+ // int i{};
+ // for (auto&& input : inputs) {
+ // printf("INPUT %d IS %s\n", i + 1, input.c_str());
+ // }
+ auto gil{Python::ScopedInterpreterLock()};
+ auto pylist{g_python->StringList(inputs)};
+ auto args{g_python->SingleMemberTuple(pylist)};
+ auto result = g_python->obj(Python::ObjID::kHashStringsCall).Call(args);
+ assert(result.UnicodeCheck());
+ public_device_uuid_ = result.Str();
+ }
+ return public_device_uuid_;
+}
+
+auto Platform::GetPublicDeviceUUIDInputs() -> std::list {
+ throw Exception("GetPublicDeviceUUIDInputs unimplemented");
+}
+
auto Platform::GetDefaultConfigDir() -> std::string {
std::string config_dir;
// As a default, look for a HOME env var and use that if present
@@ -301,7 +325,7 @@ auto Platform::GetReplaysDir() -> std::string {
// rename() supporting UTF8 strings.
auto Platform::Rename(const char* oldname, const char* newname) -> int {
- // this covers non-windows platforms
+ // This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -310,7 +334,7 @@ auto Platform::Rename(const char* oldname, const char* newname) -> int {
}
auto Platform::Remove(const char* path) -> int {
- // this covers non-windows platforms
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -320,7 +344,7 @@ auto Platform::Remove(const char* path) -> int {
// stat() supporting UTF8 strings.
auto Platform::Stat(const char* path, struct BA_STAT* buffer) -> int {
- // this covers non-windows platforms
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -330,7 +354,7 @@ auto Platform::Stat(const char* path, struct BA_STAT* buffer) -> int {
// fopen() supporting UTF8 strings.
auto Platform::FOpen(const char* path, const char* mode) -> FILE* {
- // this covers non-windows platforms
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -349,12 +373,12 @@ auto Platform::GetSocketErrorString() -> std::string {
}
auto Platform::GetSocketError() -> int {
- // by default this is simply errno
+ // By default this is simply errno.
return errno;
}
auto Platform::GetErrnoString() -> std::string {
- // this covers non-windows platforms
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#elif BA_OSTYPE_LINUX
@@ -419,7 +443,7 @@ auto Platform::DoGetUserPythonDirectory() -> std::string {
}
void Platform::DoMakeDir(const std::string& dir, bool quiet) {
- // Default case here covers all non-windows platforms.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -740,7 +764,7 @@ auto Platform::BlockingFatalErrorDialog(const std::string& message) -> void {
}
void Platform::SetupDataDirectory() {
-// This covers non-windows cases.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -763,7 +787,7 @@ void Platform::SetupDataDirectory() {
}
void Platform::SetEnv(const std::string& name, const std::string& value) {
-// This covers non-windows cases.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -784,7 +808,7 @@ auto Platform::IsStdinATerminal() -> bool {
#endif
}
-auto Platform::GetOSVersionString() -> std::string { return ""; }
+auto Platform::GetOSVersionString() -> std::string { return "?"; }
auto Platform::GetUserAgentString() -> std::string {
std::string device = GetDeviceName();
@@ -843,7 +867,7 @@ auto Platform::GetUserAgentString() -> std::string {
}
auto Platform::GetCWD() -> std::string {
-// Covers non-windows cases.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -931,15 +955,15 @@ void Platform::AndroidQuitActivity() {
auto Platform::GetDeviceAccountID() -> std::string {
if (HeadlessMode()) {
- return "S-" + GetUniqueDeviceIdentifier();
+ return "S-" + GetLegacyDeviceUUID();
}
// Everything else is just considered a 'local' account, though we may
// give unique ids for unique builds..
if (g_buildconfig.iircade_build()) {
- return "L-iRc" + GetUniqueDeviceIdentifier();
+ return "L-iRc" + GetLegacyDeviceUUID();
}
- return "L-" + GetUniqueDeviceIdentifier();
+ return "L-" + GetLegacyDeviceUUID();
}
auto Platform::DemangleCXXSymbol(const std::string& s) -> std::string {
@@ -970,17 +994,6 @@ auto Platform::NewAutoReleasePool() -> void* { throw Exception(); }
void Platform::DrainAutoReleasePool(void* pool) { throw Exception(); }
-auto Platform::AndroidGPGSNewConnectionToClient(int id) -> ConnectionToClient* {
- throw Exception();
-}
-auto Platform::AndroidGPGSNewConnectionToHost() -> ConnectionToHost* {
- throw Exception();
-}
-
-auto Platform::AndroidIsGPGSConnectionToClient(ConnectionToClient* c) -> bool {
- throw Exception();
-}
-
void Platform::OpenURL(const std::string& url) {
// Can't open URLs in VR - just tell the game thread to show the url.
if (IsVRMode()) {
@@ -1064,18 +1077,6 @@ auto Platform::GetHasVideoAds() -> bool {
return GetHasAds();
}
-void Platform::AndroidGPGSPartyInvitePlayers() {
- Log("AndroidGPGSPartyInvitePlayers() unimplemented");
-}
-
-void Platform::AndroidGPGSPartyShowInvites() {
- Log("AndroidGPGSPartyShowInvites() unimplemented");
-}
-
-void Platform::AndroidGPGSPartyInviteAccept(const std::string& invite_id) {
- Log("AndroidGPGSPartyInviteAccept() unimplemented");
-}
-
void Platform::SignIn(const std::string& account_type) {
Log("SignIn() unimplemented");
}
@@ -1140,14 +1141,6 @@ auto Platform::MacMusicAppGetPlaylists() -> std::list {
return {};
}
-void Platform::StartListeningForWiiRemotes() {
- Log("StartListeningForWiiRemotes() unimplemented");
-}
-
-void Platform::StopListeningForWiiRemotes() {
- Log("StopListeningForWiiRemotes() unimplemented");
-}
-
void Platform::SetCurrentThreadName(const std::string& name) {
// Currently we leave the main thread alone, otherwise we show up as
// "BallisticaMainThread" under "top" on linux (should check other platforms).
@@ -1162,7 +1155,7 @@ void Platform::SetCurrentThreadName(const std::string& name) {
}
void Platform::Unlink(const char* path) {
- // This covers all but windows.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -1199,7 +1192,7 @@ auto Platform::IsEventPushMode() -> bool { return false; }
auto Platform::GetDisplayResolution(int* x, int* y) -> bool { return false; }
void Platform::CloseSocket(int socket) {
-// This covers all but windows.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -1207,18 +1200,8 @@ void Platform::CloseSocket(int socket) {
#endif
}
-auto Platform::SocketPair(int domain, int type, int protocol, int socks[2])
- -> int {
- // This covers all but windows.
-#if BA_OSTYPE_WINDOWS
- throw Exception();
-#else
- return socketpair(domain, type, protocol, socks);
-#endif
-}
-
auto Platform::GetBroadcastAddrs() -> std::vector {
-// This covers all but windows.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -1252,7 +1235,7 @@ auto Platform::GetBroadcastAddrs() -> std::vector {
}
auto Platform::SetSocketNonBlocking(int sd) -> bool {
-// This covers all but windows.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
@@ -1363,7 +1346,7 @@ static void HandleSIGINT(int s) {
#endif
void Platform::SetupInterruptHandling() {
- // For non-windows platforms, set up posix-y SIGINT handling.
+// This default implementation covers non-windows platforms.
#if BA_OSTYPE_WINDOWS
throw Exception();
#else
diff --git a/src/ballistica/platform/platform.h b/src/ballistica/platform/platform.h
index 7f68738f..e8a130fe 100644
--- a/src/ballistica/platform/platform.h
+++ b/src/ballistica/platform/platform.h
@@ -15,12 +15,12 @@ namespace ballistica {
/// For capturing and printing stack-traces and related errors.
/// Platforms should subclass this and return instances in GetStackTrace().
+/// Stack trace classes should capture the stack state immediately upon
+/// construction but should do the bare minimum amount of work to store it.
+/// Any expensive operations such as symbolification should be deferred until
+/// GetDescription().
class PlatformStackTrace {
public:
- // The stack trace should capture the stack state immediately upon
- // construction but should do the bare minimum amount of work to store it. Any
- // expensive operations such as symbolification should be deferred until
- // GetDescription().
virtual ~PlatformStackTrace() = default;
// Return a human readable version of the trace (with symbolification if
@@ -32,9 +32,9 @@ class PlatformStackTrace {
virtual auto copy() const noexcept -> PlatformStackTrace* = 0;
};
-// This class attempts to abstract away most platform-specific functionality.
-// Ideally we should need to pull in no platform-specific system headers outside
-// of the platform*.cc files and can just go through this.
+/// This class attempts to abstract away most platform-specific functionality.
+/// Ideally we should need to pull in no platform-specific system headers
+/// outside of the platform*.cc files and can just go through this.
class Platform {
public:
static auto Create() -> Platform*;
@@ -49,60 +49,58 @@ class Platform {
virtual auto PostInit() -> void;
/// Create the proper App module and add it to the main_thread.
- void CreateApp();
+ auto CreateApp() -> void;
/// Create the appropriate Graphics subclass for the app.
- Graphics* CreateGraphics();
+ auto CreateGraphics() -> Graphics*;
- virtual void CreateAuxiliaryModules();
- virtual void WillExitMain(bool errored);
+ virtual auto CreateAuxiliaryModules() -> void;
+ virtual auto WillExitMain(bool errored) -> void;
- // Inform the platform that all subsystems are up and running and it can
- // start talking to them.
- virtual void OnBootstrapComplete();
+ /// Inform the platform that all subsystems are up and running and it can
+ /// start talking to them.
+ virtual auto OnBootstrapComplete() -> void;
// Get/set values before standard game settings are available
// (for values needed before SDL init/etc).
// FIXME: We should have some sort of 'bootconfig.json' file for these.
- // (or simply read the regular config in via c++ immediately)
+ // (or simply read the regular config in via c++ immediately)
auto GetLowLevelConfigValue(const char* key, int default_value) -> int;
- void SetLowLevelConfigValue(const char* key, int value);
+ auto SetLowLevelConfigValue(const char* key, int value) -> void;
- // Called when the app config is being read/applied.
- virtual void ApplyConfig();
+ /// Called when the app config is being read/applied.
+ virtual auto ApplyConfig() -> void;
- // Called when the app should set itself up to intercept ctrl-c presses.
- virtual void SetupInterruptHandling();
-
- void FinalCleanup();
+ /// Called when the app should set itself up to intercept ctrl-c presses.
+ virtual auto SetupInterruptHandling() -> void;
#pragma mark FILES -------------------------------------------------------------
- // remove() support UTF8 strings.
+ /// remove() support UTF8 strings.
virtual auto Remove(const char* path) -> int;
- // stat() supporting UTF8 strings.
+ /// stat() supporting UTF8 strings.
virtual auto Stat(const char* path, struct BA_STAT* buffer) -> int;
- // fopen() supporting UTF8 strings.
+ /// fopen() supporting UTF8 strings.
virtual auto FOpen(const char* path, const char* mode) -> FILE*;
- // rename() supporting UTF8 strings.
- // For cross-platform consistency, this should also remove any file that
- // exists at the target location first.
+ /// rename() supporting UTF8 strings.
+ /// For cross-platform consistency, this should also remove any file that
+ /// exists at the target location first.
virtual auto Rename(const char* oldname, const char* newname) -> int;
- // Simple cross-platform check for existence of a file.
+ /// Simple cross-platform check for existence of a file.
auto FilePathExists(const std::string& name) -> bool;
/// Attempt to make a directory; raise an Exception if unable,
/// unless quiet is true.
- void MakeDir(const std::string& dir, bool quiet = false);
+ auto MakeDir(const std::string& dir, bool quiet = false) -> void;
- // Return the current working directory.
+ /// Return the current working directory.
virtual auto GetCWD() -> std::string;
- // Unlink a file.
+ /// Unlink a file.
virtual auto Unlink(const char* path) -> void;
/// Return the absolute path for the provided path. Note that this requires
@@ -132,7 +130,7 @@ class Platform {
// Send a message to the default platform handler.
// IMPORTANT: No Object::Refs should be created or destroyed within this call,
// or deadlock can occur.
- virtual void HandleLog(const std::string& msg);
+ virtual auto HandleLog(const std::string& msg) -> void;
#pragma mark ENVIRONMENT -------------------------------------------------------
@@ -150,15 +148,6 @@ class Platform {
// Return the interface type based on the environment (phone, tablet, etc).
virtual auto GetUIScale() -> UIScale;
- // Return a string *reasonably* likely to be unique and consistent for this
- // device. Do not assume this is globally unique and *do not* assume that it
- // will never ever change (hardware upgrades may affect it, etc).
- // IMPORTANT: This value should NEVER be sent over the wire to peers.
- virtual auto GetUniqueDeviceIdentifier() -> const std::string&;
-
- // Returns the ID to use for the device account
- auto GetDeviceAccountID() -> std::string;
-
auto GetConfigDirectory() -> std::string;
auto GetConfigFilePath() -> std::string;
auto GetUserPythonDirectory() -> std::string;
@@ -176,36 +165,62 @@ class Platform {
/// Raises an exception on errors.
virtual void SetEnv(const std::string& name, const std::string& value);
- // Are we being run from a terminal? (should we show prompts, etc?).
+ /// Are we being run from a terminal? (should we show prompts, etc?).
virtual auto IsStdinATerminal() -> bool;
- // Return hostname or other id suitable for network searches, etc.
+ /// Return hostname or other id suitable for displaying in network search
+ /// results, etc.
auto GetDeviceName() -> std::string;
- // Are we running on a tv?
+ /// Get a UUID for use with things like device-accounts. This function
+ /// should not be used for other purposes, should not be modified, and
+ /// eventually should go away after device accounts are phased out.
+ /// Also, this value should never be shared beyond the local device.
+ auto GetLegacyDeviceUUID() -> const std::string&;
+
+ /// Get a UUID for the current device that is meant to be publicly shared.
+ /// This value will change occasionally due to OS updates, app updates, or
+ /// other factors, so it can not be used as a permanent identifier, but it
+ /// should remain constant over short periods and should not be easily
+ /// changeable by the user, making it useful for purposes such as temporary
+ /// server bans or spam prevention.
+ auto GetPublicDeviceUUID() -> std::string;
+
+ /// Return values which will be hashed to create a public device uuid.
+ /// These values may include things that may change periodically such
+ /// as minor os version numbers, but they should not include things
+ /// that change constantly or that can be changed easily by the user.
+ /// Only hashed versions of these values should ever be shared beyond
+ /// the local device.
+ virtual auto GetPublicDeviceUUIDInputs() -> std::list;
+
+ /// Return whether there is an actual legacy-device-uuid value for
+ /// this platform, and also return it if so.
+ virtual auto GetRealLegacyDeviceUUID(std::string* uuid) -> bool;
+
+ /// Are we running on a tv?
virtual auto IsRunningOnTV() -> bool;
- // Are we on a daydream enabled android device?
+ /// Are we on a daydream-enabled Android device?
virtual auto IsRunningOnDaydream() -> bool;
- // Do we have touchscreen hardware?
+ /// Do we have touchscreen hardware?
auto HasTouchScreen() -> bool;
- // Are we running on a desktop setup in general?
+ /// Are we running on a desktop setup in general?
virtual auto IsRunningOnDesktop() -> bool;
- // Are we running on fireTV hardware?
+ /// Are we running on fireTV hardware?
virtual auto IsRunningOnFireTV() -> bool;
- // Return the external storage path (currently only relevant on android).
+ /// Return the external storage path (currently only relevant on Android).
virtual auto GetExternalStoragePath() -> std::string;
// For enabling some special hardware optimizations for nvidia.
auto is_tegra_k1() const -> bool { return is_tegra_k1_; }
- void set_is_tegra_k1(bool val) { is_tegra_k1_ = val; }
+ auto set_is_tegra_k1(bool val) -> void { is_tegra_k1_ = val; }
- // Return true if this platform includes its own python distribution
- // (defaults to false).
+ /// Return whether this platform includes its own Python distribution
virtual auto ContainsPythonDist() -> bool;
#pragma mark INPUT DEVICES -----------------------------------------------------
@@ -215,32 +230,26 @@ class Platform {
#pragma mark IN APP PURCHASES --------------------------------------------------
- virtual void Purchase(const std::string& item);
+ virtual auto Purchase(const std::string& item) -> void;
- // Restore purchases (currently only relevant on apple platforms).
- virtual void RestorePurchases();
+ // Restore purchases (currently only relevant on Apple platforms).
+ virtual auto RestorePurchases() -> void;
- // purchase ack'ed by the master-server (so can consume)
- virtual void PurchaseAck(const std::string& purchase,
- const std::string& order_id);
+ // Purchase was ack'ed by the master-server (so can consume).
+ virtual auto PurchaseAck(const std::string& purchase,
+ const std::string& order_id) -> void;
#pragma mark ANDROID -----------------------------------------------------------
virtual auto GetAndroidExecArg() -> std::string;
- virtual void AndroidSetResString(const std::string& res);
- virtual auto AndroidIsGPGSConnectionToClient(ConnectionToClient* c) -> bool;
- virtual auto AndroidGPGSNewConnectionToClient(int id) -> ConnectionToClient*;
- virtual auto AndroidGPGSNewConnectionToHost() -> ConnectionToHost*;
- virtual void AndroidSynthesizeBackPress();
- virtual void AndroidQuitActivity();
- virtual void AndroidShowAppInvite(const std::string& title,
+ virtual auto AndroidSetResString(const std::string& res) -> void;
+ virtual auto AndroidSynthesizeBackPress() -> void;
+ virtual auto AndroidQuitActivity() -> void;
+ virtual auto AndroidShowAppInvite(const std::string& title,
const std::string& message,
- const std::string& code);
- virtual void AndroidRefreshFile(const std::string& file);
- virtual void AndroidGPGSPartyInvitePlayers();
- virtual void AndroidGPGSPartyShowInvites();
- virtual void AndroidGPGSPartyInviteAccept(const std::string& invite_id);
- virtual void AndroidShowWifiSettings();
+ const std::string& code) -> void;
+ virtual auto AndroidRefreshFile(const std::string& file) -> void;
+ virtual auto AndroidShowWifiSettings() -> void;
#pragma mark PERMISSIONS -------------------------------------------------------
@@ -249,7 +258,7 @@ class Platform {
/// then this may also present a message or pop-up instructing the user how
/// to manually grant the permission (up to individual platforms to
/// implement).
- virtual void RequestPermission(Permission p);
+ virtual auto RequestPermission(Permission p) -> void;
/// Returns true if this permission has been granted (or if asking is not
/// required for it).
@@ -257,24 +266,26 @@ class Platform {
#pragma mark ANALYTICS ---------------------------------------------------------
- virtual void SetAnalyticsScreen(const std::string& screen);
- virtual void IncrementAnalyticsCount(const std::string& name, int increment);
- virtual void IncrementAnalyticsCountRaw(const std::string& name,
- int increment);
- virtual void IncrementAnalyticsCountRaw2(const std::string& name,
- int uses_increment, int increment);
- virtual void SubmitAnalyticsCounts();
+ virtual auto SetAnalyticsScreen(const std::string& screen) -> void;
+ virtual auto IncrementAnalyticsCount(const std::string& name, int increment)
+ -> void;
+ virtual auto IncrementAnalyticsCountRaw(const std::string& name,
+ int increment) -> void;
+ virtual auto IncrementAnalyticsCountRaw2(const std::string& name,
+ int uses_increment, int increment)
+ -> void;
+ virtual auto SubmitAnalyticsCounts() -> void;
#pragma mark APPLE -------------------------------------------------------------
virtual auto NewAutoReleasePool() -> void*;
- virtual void DrainAutoReleasePool(void* pool);
+ virtual auto DrainAutoReleasePool(void* pool) -> void;
// FIXME: Can we consolidate these with the general music playback calls?
- virtual void MacMusicAppInit();
+ virtual auto MacMusicAppInit() -> void;
virtual auto MacMusicAppGetVolume() -> int;
- virtual void MacMusicAppSetVolume(int volume);
- virtual void MacMusicAppGetLibrarySource();
- virtual void MacMusicAppStop();
+ virtual auto MacMusicAppSetVolume(int volume) -> void;
+ virtual auto MacMusicAppGetLibrarySource() -> void;
+ virtual auto MacMusicAppStop() -> void;
virtual auto MacMusicAppPlayPlaylist(const std::string& playlist) -> bool;
virtual auto MacMusicAppGetPlaylists() -> std::list;
@@ -282,9 +293,9 @@ class Platform {
// Set bounds/width info for a bit of text.
// (will only be called in BA_ENABLE_OS_FONT_RENDERING is set)
- virtual void GetTextBoundsAndWidth(const std::string& text, Rect* r,
- float* width);
- virtual void FreeTextTexture(void* tex);
+ virtual auto GetTextBoundsAndWidth(const std::string& text, Rect* r,
+ float* width) -> void;
+ virtual auto FreeTextTexture(void* tex) -> void;
virtual auto CreateTextTexture(int width, int height,
const std::vector& strings,
const std::vector& positions,
@@ -296,21 +307,28 @@ class Platform {
virtual auto SignIn(const std::string& account_type) -> void;
virtual auto SignOut() -> void;
+
virtual auto GameCenterLogin() -> void;
virtual auto LoginDidChange() -> void;
+ /// Returns the ID to use for the device account.
+ auto GetDeviceAccountID() -> std::string;
+
+ /// Return the prefix to use for device-account ids on this platform.
+ virtual auto GetDeviceAccountUUIDPrefix() -> std::string;
+
#pragma mark MUSIC PLAYBACK ----------------------------------------------------
- // FIXME: currently these are wired up on android; need to generalize
+ // FIXME: currently these are wired up on Android; need to generalize
// to support mac/itunes or other music player types.
- virtual void MusicPlayerPlay(PyObject* target);
- virtual void MusicPlayerStop();
- virtual void MusicPlayerShutdown();
- virtual void MusicPlayerSetVolume(float volume);
+ virtual auto MusicPlayerPlay(PyObject* target) -> void;
+ virtual auto MusicPlayerStop() -> void;
+ virtual auto MusicPlayerShutdown() -> void;
+ virtual auto MusicPlayerSetVolume(float volume) -> void;
#pragma mark ADS ---------------------------------------------------------------
- virtual void ShowAd(const std::string& purpose);
+ virtual auto ShowAd(const std::string& purpose) -> void;
// Return whether we have the ability to show *any* ads.
virtual auto GetHasAds() -> bool;
@@ -327,62 +345,60 @@ class Platform {
virtual auto ConvertIncomingLeaderboardScore(
const std::string& leaderboard_id, int score) -> int;
- virtual void GetFriendScores(const std::string& game,
+ virtual auto GetFriendScores(const std::string& game,
const std::string& game_version,
- void* py_callback);
- virtual void SubmitScore(const std::string& game, const std::string& version,
- int64_t score);
- virtual void ReportAchievement(const std::string& achievement);
+ void* py_callback) -> void;
+ virtual auto SubmitScore(const std::string& game, const std::string& version,
+ int64_t score) -> void;
+ virtual auto ReportAchievement(const std::string& achievement) -> void;
virtual auto HaveLeaderboard(const std::string& game,
const std::string& config) -> bool;
- virtual void ShowOnlineScoreUI(const std::string& show,
+ virtual auto ShowOnlineScoreUI(const std::string& show,
const std::string& game,
- const std::string& game_version);
- virtual void ResetAchievements();
+ const std::string& game_version) -> void;
+ virtual auto ResetAchievements() -> void;
#pragma mark NETWORKING --------------------------------------------------------
- virtual void CloseSocket(int socket);
- virtual auto SocketPair(int domain, int type, int protocol, int socks[2])
- -> int;
+ virtual auto CloseSocket(int socket) -> void;
virtual auto GetBroadcastAddrs() -> std::vector;
virtual auto SetSocketNonBlocking(int sd) -> bool;
#pragma mark ERRORS & DEBUGGING ------------------------------------------------
- // Should return a subclass of PlatformStackTrace allocated via new.
- // Platforms with no meaningful stack trace functionality can return nullptr.
+ /// Should return a subclass of PlatformStackTrace allocated via new.
+ /// Platforms with no meaningful stack trace functionality can return nullptr.
virtual auto GetStackTrace() -> PlatformStackTrace*;
// Called during stress testing.
virtual auto GetMemUsageInfo() -> std::string;
- // Optionally override fatal error reporting. If true is returned, default
- // fatal error reporting will not run.
+ /// Optionally override fatal error reporting. If true is returned, default
+ /// fatal error reporting will not run.
virtual auto ReportFatalError(const std::string& message,
bool in_top_level_exception_handler) -> bool;
- // Optionally override fatal error handling. If true is returned, default
- // fatal error handling will not run.
+ /// Optionally override fatal error handling. If true is returned, default
+ /// fatal error handling will not run.
virtual auto HandleFatalError(bool exit_cleanly,
bool in_top_level_exception_handler) -> bool;
- // If this platform has the ability to show a blocking dialog on the main
- // thread for fatal errors, return true here.
+ /// If this platform has the ability to show a blocking dialog on the main
+ /// thread for fatal errors, return true here.
virtual auto CanShowBlockingFatalErrorDialog() -> bool;
- // Called on the main thread when a fatal error occurs.
- // Will only be called if CanShowBlockingFatalErrorDialog() is true.
+ /// Called on the main thread when a fatal error occurs.
+ /// Will only be called if CanShowBlockingFatalErrorDialog() is true.
virtual auto BlockingFatalErrorDialog(const std::string& message) -> void;
- // Use this instead of looking at errno (translates winsock errors to errno).
+ /// Use this instead of looking at errno (translates winsock errors to errno).
virtual auto GetSocketError() -> int;
- // Return a string for the current value of errno.
+ /// Return a string for the current value of errno.
virtual auto GetErrnoString() -> std::string;
- // Return a description of errno (unix) or WSAGetLastError() (windows).
+ /// Return a description of errno (unix) or WSAGetLastError() (windows).
virtual auto GetSocketErrorString() -> std::string;
/// Set a key to be included in crash logs or other debug cases.
@@ -426,60 +442,58 @@ class Platform {
static auto GetCurrentMilliseconds() -> millisecs_t;
static auto GetCurrentSeconds() -> int64_t;
- static void SleepMS(millisecs_t ms);
+ static auto SleepMS(millisecs_t ms) -> void;
- // Pop up a text edit dialog.
- virtual void EditText(const std::string& title, const std::string& value,
- int max_chars);
+ /// Pop up a text edit dialog.
+ virtual auto EditText(const std::string& title, const std::string& value,
+ int max_chars) -> void;
- // Open the provided URL in a browser or whatnot.
- void OpenURL(const std::string& url);
+ /// Open the provided URL in a browser or whatnot.
+ auto OpenURL(const std::string& url) -> void;
+
+ /// Given a C++ symbol, attempt to return a pretty one.
virtual auto DemangleCXXSymbol(const std::string& s) -> std::string;
- // Called each time through the main event loop; for custom pumping/handling.
- virtual void RunEvents();
+ /// Called each time through the main event loop;
+ /// for custom pumping/handling.
+ virtual auto RunEvents() -> void;
- // Called when the app module is pausing.
- // Note: only app-thread (main thread) stuff should happen here.
- // (don't push calls to other threads/etc).
- virtual void OnAppPause();
+ /// Called when the app module is pausing.
+ /// Note: only app-thread (main thread) stuff should happen here.
+ /// (don't push calls to other threads/etc).
+ virtual auto OnAppPause() -> void;
- // Called when the app module is resuming.
- virtual void OnAppResume();
+ /// Called when the app module is resuming.
+ virtual auto OnAppResume() -> void;
- // Is the OS currently playing music? (so we can avoid doing so).
+ /// Is the OS currently playing music? (so we can avoid doing so).
virtual auto IsOSPlayingMusic() -> bool;
- // Pass platform-specific misc-read-vals along to the OS (as a json string).
- virtual void SetPlatformMiscReadVals(const std::string& vals);
+ /// Pass platform-specific misc-read-vals along to the OS (as a json string).
+ virtual auto SetPlatformMiscReadVals(const std::string& vals) -> void;
- // Show/hide the hardware cursor.
- virtual void SetHardwareCursorVisible(bool visible);
+ /// Show/hide the hardware cursor.
+ virtual auto SetHardwareCursorVisible(bool visible) -> void;
- // Get the most up-to-date cursor position.
- virtual void GetCursorPosition(float* x, float* y);
+ /// Get the most up-to-date cursor position.
+ virtual auto GetCursorPosition(float* x, float* y) -> void;
- // Quit the app (can be immediate or via posting some high level event).
- virtual void QuitApp();
+ /// Quit the app (can be immediate or via posting some high level event).
+ virtual auto QuitApp() -> void;
- // Do we want to deprecate this?...
- virtual void GetScoresToBeat(const std::string& level,
- const std::string& config, void* py_callback);
+ // Note to self: do we want to deprecate this?...
+ virtual auto GetScoresToBeat(const std::string& level,
+ const std::string& config, void* py_callback)
+ -> void;
- // Open a file using the system default method (in another app, etc.)
- virtual void OpenFileExternally(const std::string& path);
+ /// Open a file using the system default method (in another app, etc.)
+ virtual auto OpenFileExternally(const std::string& path) -> void;
- // Open a directory using the system default method (Finder, etc.)
- virtual void OpenDirExternally(const std::string& path);
+ /// Open a directory using the system default method (Finder, etc.)
+ virtual auto OpenDirExternally(const std::string& path) -> void;
- // Currently mac-only (could be generalized though).
- virtual void StartListeningForWiiRemotes();
-
- // Currently mac-only (could be generalized though).
- virtual void StopListeningForWiiRemotes();
-
- // Set the name of the current thread (for debugging).
- virtual void SetCurrentThreadName(const std::string& name);
+ /// Set the name of the current thread (for debugging).
+ virtual auto SetCurrentThreadName(const std::string& name) -> void;
// If display-resolution can be directly set on this platform,
// return true and set the native full res here. Otherwise return false;
@@ -490,36 +504,32 @@ class Platform {
}
protected:
- // Open the provided URL in a browser or whatnot.
- virtual void DoOpenURL(const std::string& url);
+ /// Open the provided URL in a browser or whatnot.
+ virtual auto DoOpenURL(const std::string& url) -> void;
- // Called once per platform to determine touchscreen presence.
+ /// Called once per platform to determine touchscreen presence.
virtual auto DoHasTouchScreen() -> bool;
+
+ /// Platforms should override this to provide device name.
virtual auto DoGetDeviceName() -> std::string;
- // Attempt to actually create a directory.
- // Should not except if it already exists or if quiet is true.
- virtual void DoMakeDir(const std::string& dir, bool quiet);
+ /// Attempt to actually create a directory.
+ /// Should not raise Exceptions if it already exists or
+ /// if quiet is true.
+ virtual auto DoMakeDir(const std::string& dir, bool quiet) -> void;
- // Attempt to actually get an abs path. This will only be called if
- // the path is valid and exists.
+ /// Attempt to actually get an abs path. This will only be called if
+ /// the path is valid and exists.
virtual auto DoAbsPath(const std::string& path, std::string* outpath) -> bool;
- // Calc the user scripts dir path for this platform.
- // This will be called once and the path cached.
+ /// Calc the user scripts dir path for this platform.
+ /// This will be called once and the path cached.
virtual auto DoGetUserPythonDirectory() -> std::string;
- // Return the default config directory for this platform.
+ /// Return the default config directory for this platform.
virtual auto GetDefaultConfigDir() -> std::string;
- // Return the prefix to use for device UUIDs on this platform.
- virtual auto GetDeviceUUIDPrefix() -> std::string;
-
- // Return whether there is an actual unique UUID available for this platform,
- // and also return it if so.
- virtual auto GetRealDeviceUUID(std::string* uuid) -> bool;
-
- // Generate a random UUID string.
+ /// Generate a random UUID string.
virtual auto GenerateUUID() -> std::string;
virtual auto DoClipboardIsSupported() -> bool;
@@ -537,13 +547,14 @@ class Platform {
bool have_clipboard_is_supported_{};
bool clipboard_is_supported_{};
millisecs_t starttime_{};
- std::string device_uuid_;
+ std::string legacy_device_uuid_;
bool have_device_uuid_{};
std::string config_dir_;
std::string user_scripts_dir_;
std::string app_python_dir_;
std::string site_python_dir_;
std::string replays_dir_;
+ std::string public_device_uuid_;
};
} // namespace ballistica
diff --git a/src/ballistica/platform/sdl/sdl_app.cc b/src/ballistica/platform/sdl/sdl_app.cc
index 69dea3b3..42bdb72a 100644
--- a/src/ballistica/platform/sdl/sdl_app.cc
+++ b/src/ballistica/platform/sdl/sdl_app.cc
@@ -325,7 +325,7 @@ SDLApp::SDLApp(Thread* thread) : App(thread) {
// SDL events the moment they're generated and we process them immediately.
// This way we don't have to poll for events and can be purely callback-based,
// which fits in nicely with most modern event models.
- if (!UsesEventLoop()) {
+ if (!ManagesEventLoop()) {
#if BA_SDL2_BUILD
SDL_SetEventFilter(FilterSDL2Event, nullptr);
#else
diff --git a/src/ballistica/platform/windows/platform_windows.cc b/src/ballistica/platform/windows/platform_windows.cc
index 2948c519..26710c19 100644
--- a/src/ballistica/platform/windows/platform_windows.cc
+++ b/src/ballistica/platform/windows/platform_windows.cc
@@ -127,6 +127,28 @@ void PlatformWindows::SetupInterruptHandling() {
}
}
+auto PlatformWindows::GetPublicDeviceUUIDInputs() -> std::list {
+ std::list out;
+
+ std::string ret;
+ char value[64];
+ DWORD size = _countof(value);
+ DWORD type = REG_SZ;
+ HKEY key;
+ LONG retKey =
+ ::RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography",
+ 0, KEY_READ | KEY_WOW64_64KEY, &key);
+ LONG retVal = ::RegQueryValueExA(key, "MachineGuid", nullptr, &type,
+ (LPBYTE)value, &size);
+ if (retKey == ERROR_SUCCESS && retVal == ERROR_SUCCESS) {
+ ret = value;
+ }
+ ::RegCloseKey(key);
+
+ out.push_back(ret);
+ return out;
+}
+
std::string PlatformWindows::GenerateUUID() {
std::string val;
UUID uuid;
@@ -828,87 +850,6 @@ void PlatformWindows::Unlink(const char* path) { _unlink(path); }
void PlatformWindows::CloseSocket(int socket) { closesocket(socket); }
-int PlatformWindows::SocketPair(int domain, int type, int protocol,
- int socks_out[2]) {
- assert(type == SOCK_STREAM);
-
- int make_overlapped = false;
- union {
- struct sockaddr_in inaddr;
- struct sockaddr addr;
- } a;
- SOCKET listener;
- int e;
- socklen_t addrlen = sizeof(a.inaddr);
- DWORD flags = 0;
- int reuse = 1;
- int nodelay = 1;
-
- listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (listener < 0) {
- return SOCKET_ERROR;
- }
-
- memset(&a, 0, sizeof(a));
- a.inaddr.sin_family = AF_INET;
- a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- a.inaddr.sin_port = 0;
-
- SOCKET socks[2];
- socks[0] = socks[1] = INVALID_SOCKET;
- do {
- if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
- reinterpret_cast(&reuse), (socklen_t)sizeof(reuse))
- == -1) {
- break;
- }
- if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) {
- break;
- }
- if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) {
- break;
- }
- if (listen(listener, 1) == SOCKET_ERROR) {
- break;
- }
- socks[0] = socket(AF_INET, SOCK_STREAM, 0);
- if (socks[0] < 0) {
- break;
- }
- if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) {
- break;
- }
- socks[1] = accept(listener, nullptr, nullptr);
-
- // not sure if this helps but what the hey...
- if (setsockopt(socks[0], IPPROTO_TCP, TCP_NODELAY,
- reinterpret_cast(&nodelay),
- (socklen_t)sizeof(nodelay))
- == -1)
- break;
- if (setsockopt(socks[1], IPPROTO_TCP, TCP_NODELAY,
- reinterpret_cast(&nodelay),
- (socklen_t)sizeof(nodelay))
- == -1) {
- break;
- }
-
- if (socks[1] < 0) break;
-
- closesocket(listener);
- socks_out[0] = static_cast_check_fit(socks[0]);
- socks_out[1] = static_cast_check_fit(socks[1]);
- return 0;
- } while (0);
-
- e = WSAGetLastError();
- closesocket(listener);
- closesocket(socks[0]);
- closesocket(socks[1]);
- WSASetLastError(e);
- return SOCKET_ERROR;
-}
-
std::vector PlatformWindows::GetBroadcastAddrs() {
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
diff --git a/src/ballistica/platform/windows/platform_windows.h b/src/ballistica/platform/windows/platform_windows.h
index 67883ed8..844f8801 100644
--- a/src/ballistica/platform/windows/platform_windows.h
+++ b/src/ballistica/platform/windows/platform_windows.h
@@ -15,7 +15,8 @@ class PlatformWindows : public Platform {
public:
PlatformWindows();
void SetupInterruptHandling() override;
- auto GetDeviceUUIDPrefix() -> std::string override { return "w"; }
+ auto GetDeviceAccountUUIDPrefix() -> std::string override { return "w"; }
+ auto GetPublicDeviceUUIDInputs() -> std::list override;
auto GenerateUUID() -> std::string override;
auto GetDefaultConfigDir() -> std::string override;
auto Remove(const char* path) -> int;
@@ -42,8 +43,6 @@ class PlatformWindows : public Platform {
void OpenDirExternally(const std::string& path) override;
void Unlink(const char* path) override;
void CloseSocket(int socket) override;
- auto SocketPair(int domain, int type, int protocol, int socks[2])
- -> int override;
auto GetBroadcastAddrs() -> std::vector override;
auto SetSocketNonBlocking(int sd) -> bool override;
auto GetPlatformName() -> std::string override;
diff --git a/src/ballistica/python/methods/python_methods_input.cc b/src/ballistica/python/methods/python_methods_input.cc
index f523bd40..a38b9620 100644
--- a/src/ballistica/python/methods/python_methods_input.cc
+++ b/src/ballistica/python/methods/python_methods_input.cc
@@ -43,23 +43,6 @@ auto PyHaveTouchScreenInput(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_CATCH;
}
-auto PyStartListeningForWiiRemotes(PyObject* self, PyObject* args)
- -> PyObject* {
- BA_PYTHON_TRY;
- Platform::SetLastPyCall("start_listening_for_wii_remotes");
- g_platform->StartListeningForWiiRemotes();
- Py_RETURN_NONE;
- BA_PYTHON_CATCH;
-}
-
-auto PyStopListeningForWiiRemotes(PyObject* self, PyObject* args) -> PyObject* {
- BA_PYTHON_TRY;
- Platform::SetLastPyCall("stop_listening_for_wii_remotes");
- g_platform->StopListeningForWiiRemotes();
- Py_RETURN_NONE;
- BA_PYTHON_CATCH;
-}
-
auto PySetTouchscreenEditing(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY;
Platform::SetLastPyCall("set_touchscreen_editing");
@@ -314,22 +297,6 @@ auto PythonMethodsInput::GetMethods() -> std::vector {
"\n"
"(internal)"},
- {"stop_listening_for_wii_remotes", PyStopListeningForWiiRemotes,
- METH_VARARGS,
- "stop_listening_for_wii_remotes() -> None\n"
- "\n"
- "(internal)\n"
- "\n"
- "Stop listening for connections from wii remotes."},
-
- {"start_listening_for_wii_remotes", PyStartListeningForWiiRemotes,
- METH_VARARGS,
- "start_listening_for_wii_remotes() -> None\n"
- "\n"
- "(internal)\n"
- "\n"
- "Start listening for connections from wii remotes."},
-
{"have_touchscreen_input", PyHaveTouchScreenInput, METH_VARARGS,
"have_touchscreen_input() -> bool\n"
"\n"
diff --git a/src/ballistica/python/methods/python_methods_ui.cc b/src/ballistica/python/methods/python_methods_ui.cc
index f2725dbd..020c235a 100644
--- a/src/ballistica/python/methods/python_methods_ui.cc
+++ b/src/ballistica/python/methods/python_methods_ui.cc
@@ -22,7 +22,7 @@
#include "ballistica/ui/widget/scroll_widget.h"
#if !BA_HEADLESS_BUILD
-extern "C" void SDL_ericf_focus();
+extern "C" void SDL_ericf_focus(void);
#endif
namespace ballistica {
@@ -2010,20 +2010,6 @@ auto PyShowProgressBar(PyObject* self, PyObject* args, PyObject* keywds)
BA_PYTHON_CATCH;
}
-auto PyShowInvitesUI(PyObject* self, PyObject* args, PyObject* keywds)
- -> PyObject* {
- BA_PYTHON_TRY;
- Platform::SetLastPyCall("show_invites_ui");
- static const char* kwlist[] = {nullptr};
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "",
- const_cast(kwlist))) {
- return nullptr;
- }
- g_platform->AndroidGPGSPartyShowInvites();
- Py_RETURN_NONE;
- BA_PYTHON_CATCH;
-}
-
auto PySetPartyIconAlwaysVisible(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY;
@@ -2389,14 +2375,6 @@ auto PythonMethodsUI::GetMethods() -> std::vector {
"\n"
"(internal)"},
- {"show_invites_ui", (PyCFunction)PyShowInvitesUI,
- METH_VARARGS | METH_KEYWORDS,
- "show_invites_ui() -> None\n"
- "\n"
- "(internal)\n"
- "\n"
- "Category: **General Utility Functions**"},
-
{"show_progress_bar", (PyCFunction)PyShowProgressBar,
METH_VARARGS | METH_KEYWORDS,
"show_progress_bar() -> None\n"
diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc
index 668496f9..8d86e081 100644
--- a/src/ballistica/python/python.cc
+++ b/src/ballistica/python/python.cc
@@ -1182,6 +1182,24 @@ void Python::ShowURL(const std::string& url) {
}
}
+auto Python::StringList(const std::list& values) -> PythonRef {
+ assert(HaveGIL());
+ PythonRef pylist{PyList_New(values.size()), PythonRef::kSteal};
+ int i{};
+ for (auto&& value : values) {
+ PyObject* item{PyUnicode_FromString(value.c_str())};
+ assert(item);
+ PyList_SET_ITEM(pylist.get(), i, item);
+ ++i;
+ }
+ return pylist;
+}
+
+auto Python::SingleMemberTuple(const PythonRef& member) -> PythonRef {
+ assert(HaveGIL());
+ return PythonRef(Py_BuildValue("(O)", member.NewRef()), PythonRef::kSteal);
+}
+
auto Python::FilterChatMessage(std::string* message, int client_id) -> bool {
assert(message);
ScopedSetContext cp(g_game->GetUIContext());
@@ -2322,30 +2340,6 @@ auto Python::ObjToString(PyObject* obj) -> std::string {
}
}
-void Python::V1PartyInvite(const std::string& player,
- const std::string& invite_id) {
- ScopedSetContext cp(g_game->GetUIContext());
- PythonRef args(
- Py_BuildValue(
- "(OO)",
- PythonRef(PyUnicode_FromString(player.c_str()), PythonRef::kSteal)
- .get(),
- PythonRef(PyUnicode_FromString(invite_id.c_str()), PythonRef::kSteal)
- .get()),
- PythonRef::kSteal);
- obj(ObjID::kHandlePartyInviteCall).Call(args);
-}
-
-void Python::V1PartyInviteRevoke(const std::string& invite_id) {
- ScopedSetContext cp(g_game->GetUIContext());
- PythonRef args(
- Py_BuildValue("(O)", PythonRef(PyUnicode_FromString(invite_id.c_str()),
- PythonRef::kSteal)
- .get()),
- PythonRef::kSteal);
- obj(ObjID::kHandlePartyInviteRevokeCall).Call(args);
-}
-
void Python::StoreObj(ObjID id, PyObject* pyobj, bool incref) {
assert(id < ObjID::kLast);
assert(pyobj);
diff --git a/src/ballistica/python/python.h b/src/ballistica/python/python.h
index 343934b0..1fbfbbd5 100644
--- a/src/ballistica/python/python.h
+++ b/src/ballistica/python/python.h
@@ -142,8 +142,6 @@ class Python {
/// is useful as an object identifier/etc.
static auto GetPythonFileLocation(bool pretty = true) -> std::string;
- void V1PartyInvite(const std::string& player, const std::string& invite_id);
- void V1PartyInviteRevoke(const std::string& invite_id);
void set_env_obj(PyObject* obj) { env_ = obj; }
auto env_obj() const -> PyObject* {
assert(env_);
@@ -270,7 +268,6 @@ class Python {
kOnScreenKeyboardClass,
kFilterChatMessageCall,
kHandleLocalChatMessageCall,
- kHandlePartyInviteCall,
kHandlePartyInviteRevokeCall,
kDoPlayMusicCall,
kDeepLinkCall,
@@ -352,6 +349,7 @@ class Python {
kGetPlayerIconCall,
kLstrFromJsonCall,
kUUIDStrCall,
+ kHashStringsCall,
kLast // Sentinel; must be at end.
};
@@ -373,6 +371,12 @@ class Python {
return objs_[static_cast(id)].exists();
}
+ /// Create a Python list of strings.
+ auto StringList(const std::list& values) -> PythonRef;
+
+ /// Create a Python single-member tuple.
+ auto SingleMemberTuple(const PythonRef& member) -> PythonRef;
+
/// Push a call to a preset obj to the game thread
/// (will be run in the UI context).
void PushObjCall(ObjID obj);
diff --git a/src/ballistica/python/python_ref.cc b/src/ballistica/python/python_ref.cc
index e5be1ae5..a91b6ada 100644
--- a/src/ballistica/python/python_ref.cc
+++ b/src/ballistica/python/python_ref.cc
@@ -140,6 +140,12 @@ auto PythonRef::CallableCheck() const -> bool {
return static_cast(PyCallable_Check(obj_));
}
+auto PythonRef::UnicodeCheck() const -> bool {
+ BA_PRECONDITION(obj_);
+ assert(Python::HaveGIL());
+ return static_cast(PyUnicode_Check(obj_));
+}
+
auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const
-> PythonRef {
assert(obj_);
diff --git a/src/ballistica/python/python_ref.h b/src/ballistica/python/python_ref.h
index 6eecd5b2..80eebbec 100644
--- a/src/ballistica/python/python_ref.h
+++ b/src/ballistica/python/python_ref.h
@@ -107,6 +107,9 @@ class PythonRef {
/// Returns whether the underlying PyObject is callable.
auto CallableCheck() const -> bool;
+ /// Return whether the underlying PyObject is unicode.
+ auto UnicodeCheck() const -> bool;
+
/// Call the PyObject. On error, (optionally) prints errors and returns empty
/// ref.
auto Call(PyObject* args, PyObject* keywds = nullptr,
diff --git a/src/meta/bameta/python_embedded/binding.py b/src/meta/bameta/python_embedded/binding.py
index bcb7bf65..90c1830e 100644
--- a/src/meta/bameta/python_embedded/binding.py
+++ b/src/meta/bameta/python_embedded/binding.py
@@ -24,7 +24,6 @@ import _ba
# FIXME: There should be no bastd in here;
# should pull in bases from ba which get overridden by bastd (or other).
from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow
-from bastd.ui import party
if TYPE_CHECKING:
from typing import Any
@@ -107,7 +106,6 @@ def get_binding_values() -> tuple[Any, ...]:
json.dumps, # kJsonDumpsCall
json.loads, # kJsonLoadsCall
OnScreenKeyboardWindow, # kOnScreenKeyboardClass
- party.handle_party_invite, # kHandlePartyInviteCall
_music.do_play_music, # kDoPlayMusicCall
ba.app.handle_deep_link, # kDeepLinkCall
ba.app.lang.get_resource, # kGetResourceCall
@@ -135,4 +133,5 @@ def get_binding_values() -> tuple[Any, ...]:
_hooks.get_player_icon, # kGetPlayerIconCall
_language.Lstr.from_json, # kLstrFromJsonCall
_hooks.uuid_str, # kUUIDStrCall
+ _hooks.hash_strings, # kHashStringsCall
) # yapf: disable
diff --git a/tools/bacommon/servermanager.py b/tools/bacommon/servermanager.py
index b767006a..5516dbb2 100644
--- a/tools/bacommon/servermanager.py
+++ b/tools/bacommon/servermanager.py
@@ -104,7 +104,7 @@ class ServerConfig:
# if ${ACCOUNT} is present in the string, it will be replaced by the
# currently-signed-in account's id. To fetch info about an account,
# your back-end server can use the following url:
- # http://bombsquadgame.com/accountquery?id=ACCOUNT_ID_HERE
+ # https://legacy.ballistica.net/accountquery?id=ACCOUNT_ID_HERE
stats_url: Optional[str] = None
# If present, the server subprocess will attempt to gracefully exit after
diff --git a/tools/batools/build.py b/tools/batools/build.py
index 2f455a1a..123ae7d5 100644
--- a/tools/batools/build.py
+++ b/tools/batools/build.py
@@ -201,7 +201,7 @@ def lazybuild(target: str, category: SourceCategory, command: str) -> None:
# Everything possibly affecting Windows binary builds.
elif category is SourceCategory.WIN:
- paths = ['Makefile', 'src', 'resources/src']
+ paths = ['Makefile', 'src', 'resources/src', 'ballisticacore-windows']
# Everything possibly affecting resource builds.
elif category is SourceCategory.RESOURCES:
diff --git a/tools/batools/project.py b/tools/batools/project.py
index 5c8967e6..3e598afc 100755
--- a/tools/batools/project.py
+++ b/tools/batools/project.py
@@ -448,23 +448,24 @@ class Updater:
def _update_visual_studio_project(self, basename: str) -> None:
- fname = f'ballisticacore-windows/{basename}/{basename}.vcxproj'
+ fname = (f'ballisticacore-windows/{basename}/'
+ f'BallisticaCore{basename}.vcxproj')
# Currently just silently skipping if not found (for public repo).
if not os.path.exists(fname):
- return
+ raise CleanError(f'Visual Studio project not found: {fname}')
with open(fname, encoding='utf-8') as infile:
lines = infile.read().splitlines()
src_root = '..\\..\\src'
- public = 'Internal' not in basename
+ public_project = 'Internal' not in basename
all_files = sorted([
f for f in (self._source_files + self._header_files)
- if not f.endswith('.m') and not f.endswith('.mm') and
- not f.endswith('.c') and self._is_public_source_file(f) == public
+ if not f.endswith('.m') and not f.endswith('.mm') and not f.
+ endswith('.c') and self._is_public_source_file(f) == public_project
])
# Find the ItemGroup containing stdafx.cpp. This is where we'll dump
@@ -491,6 +492,7 @@ class Updater:
+ src_root + '\\ballistica' + src.replace('/', '\\') + '" />'
for src in all_files
] + group_lines
+
filtered = lines[:begin_index + 1] + group_lines + lines[end_index:]
self._file_changes[fname] = '\r\n'.join(filtered) + '\r\n'
@@ -537,12 +539,13 @@ class Updater:
'.filters'] = '\r\n'.join(filterlines) + '\r\n'
def _update_visual_studio_projects(self) -> None:
- self._update_visual_studio_project('BallisticaCoreGeneric')
- self._update_visual_studio_project('BallisticaCoreGenericInternal')
- self._update_visual_studio_project('BallisticaCoreHeadless')
- self._update_visual_studio_project('BallisticaCoreHeadlessInternal')
- self._update_visual_studio_project('BallisticaCoreOculus')
- self._update_visual_studio_project('BallisticaCoreOculusInternal')
+ self._update_visual_studio_project('Generic')
+ self._update_visual_studio_project('Headless')
+ if not self._public:
+ self._update_visual_studio_project('GenericInternal')
+ self._update_visual_studio_project('HeadlessInternal')
+ self._update_visual_studio_project('Oculus')
+ self._update_visual_studio_project('OculusInternal')
def _is_public_source_file(self, filename: str) -> bool:
assert filename.startswith('/')